LeetCode | 2. Add Two Numbers

题目链接:https://leetcode.com/problems/add-two-numbers/
题目难度:Medium
题目描述:

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example:

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.

相关主题:Linked List, Math

链表定义:

// C++
struct ListNoe {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

思路 1

头结点对应的是个位数,可以从头结点开始,同步遍历两个链表,算出每一步的结果和进位,同时创建新的链表存放结果。关键还是对链表、指针的操作。
时间复杂度:《LeetCode | 2. Add Two Numbers》
空间复杂度:《LeetCode | 2. Add Two Numbers》

// C++
class Solution {
   public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        ListNode *result = new ListNode(0);
        ListNode *pl1 = l1, *pl2 = l2, *pr = result;
        int sum = 0, carry = 0;
        while (pl1 != NULL or pl2 != NULL) {
            sum = 0;
            if (pl1 != NULL) {
                sum += pl1->val;
                pl1 = pl1->next;
            }
            if (pl2 != NULL) {
                sum += pl2->val;
                pl2 = pl2->next;
            }
            sum += carry;
            pr->val = sum % 10;
            carry = sum / 10;
            if (pl1 == NULL && pl2 == NULL) {
                if (carry > 0) {
                    pr->next = new ListNode(carry);
                }
            } else {
                pr->next = new ListNode(0);
                pr = pr->next;
            }
        }
        return result;
    }
};

思路 2

题目没有对返回的链表作额外的要求,看到也有人将两个链表的和加到其中一个链表上,并返回这个链表。这样程序的开销会小一些,但是从最差的情况考虑,时间复杂度和空间复杂度应该还都是 《LeetCode | 2. Add Two Numbers》
时间复杂度:《LeetCode | 2. Add Two Numbers》
空间复杂度:《LeetCode | 2. Add Two Numbers》

// C++
class Solution {
   public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        ListNode *pl1 = l1, *pl2 = l2;
        int sum = 0, carry = 0;
        while (pl2->next != NULL) {
            sum = pl1->val + pl2->val + carry;
            carry = sum / 10;
            pl1->val = sum % 10;
            if (pl1->next == NULL) {
                pl1->next = new ListNode(0);
            }
            pl1 = pl1->next;
            pl2 = pl2->next;
        }

        sum = pl1->val + pl2->val + carry;
        carry = sum / 10;
        pl1->val = sum % 10;
        
        while (carry > 0) {
            if (pl1->next == NULL) {
                pl1->next = new ListNode(carry);
                break;
            } else {
                pl1 = pl1->next;
                sum = pl1->val + carry;
                carry = sum / 10;
                pl1->val = sum % 10;
            }
        }
        return l1;
    }
};

关于本地调试

为了在本地调试方便,添加了从 vector 创建链表、打印链表、销毁链表的代码:

// C++
ListNode *create_list(vector<int> v)
{
    if (v.size() > 0) {
        ListNode *head = new ListNode(v[0]);
        ListNode *p = head;
        for (int i = 1; i < v.size(); i++) {
            p->next = new ListNode(v[i]);
            p = p->next;
        }
        return head;
    } else {
        return NULL;
    }
    
}

void print_list(ListNode *head)
{
    ListNode *p = head;
    while (p != NULL) {
        cout << p->val; 
        if (p->next == NULL) {
            cout << endl;
        } else {
            cout << " -> ";
        }
        p = p->next;
    }
}

void destruct_list(ListNode *head)
{
    ListNode *p = head;
    while (head != NULL) {
        head = head->next;
        delete p;
        p = head;
    }
}

为了方便初始化,这里先把链表中的元素放到了 vector 里。测试代码:

void test(Solution s, vector<int> v1, vector<int> v2)
{
    ListNode *l1 = create_list(v1), *l2 = create_list(v2);
    cout << "Input:" << endl;
    print_list(l1);
    print_list(l2);
    ListNode *result = s.addTwoNumbers(l1, l2);
    cout << "Output:" << endl;
    print_list(result);
    if (l1 == result) {
        destruct_list(l1);
    } else {
        destruct_list(l1);
        destruct_list(result);
    }
    destruct_list(l2);
}

int main(int argc, char *argv[])
{
    vector<int> v1 = {2, 4, 3}, v2 = {5, 6, 4}, v3 = {9, 8}, v4 = {1}, v5 = {9};
    Solution s;
    test(s, v1, v2);
    test(s, v1, v3);
    test(s, v3, v4);
    test(s, v3, v1);
    test(s, v4, v5);
    return 0;
}

因为涉及到对指针的操作,稍有不慎就会造成内存泄漏。所以在本地调试时可以使用 Valgrind 进行内存泄漏检测。例如:

$ g++ demo.cpp -o demo -std=c++11
$ valgrind ./demo
==32266== Memcheck, a memory error detector
==32266== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==32266== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==32266== Command: ./demo
==32266==
Input:
2 -> 4 -> 3
5 -> 6 -> 4
Output:
7 -> 0 -> 8
Input:
2 -> 4 -> 3
9 -> 8
Output:
1 -> 3 -> 4
Input:
9 -> 8
1
Output:
0 -> 9
Input:
9 -> 8
2 -> 4 -> 3
Output:
1 -> 3 -> 4
Input:
1
9
Output:
0 -> 1
==32266==
==32266== HEAP SUMMARY:
==32266==     in use at exit: 72,704 bytes in 1 blocks
==32266==   total heap usage: 49 allocs, 48 frees, 73,280 bytes allocated
==32266==
==32266== LEAK SUMMARY:
==32266==    definitely lost: 0 bytes in 0 blocks
==32266==    indirectly lost: 0 bytes in 0 blocks
==32266==      possibly lost: 0 bytes in 0 blocks
==32266==    still reachable: 72,704 bytes in 1 blocks
==32266==         suppressed: 0 bytes in 0 blocks
==32266== Rerun with --leak-check=full to see details of leaked memory
==32266==
==32266== For counts of detected and suppressed errors, rerun with: -v
==32266== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

上面终端输出的 LEAK SUMMARY 就反映了内存的泄漏情况。

2019年03月29日

    原文作者:听这一刻的晚风
    原文地址: https://www.jianshu.com/p/212dc691d220
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞