力扣-2.两数相加

1.题目介绍

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]

示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:
每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零

2.题解

一.C代码

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
    int temp, count = 0;
    struct ListNode* p1 = l1, *p2 =l2,*prev = NULL;
    while(p1 && p2){
        temp = p1->val + p2->val + count;
        if (temp >=10) {count = 1; temp = temp -10;}
        else count = 0;
        p1->val = temp;
        prev = p1; // 保存前一个节点
        p1 = p1->next;
        p2 = p2->next;
    }

    if (!p1){
        prev->next = p2;
    }
 
    while(prev->next){
        prev = prev->next;
        temp = prev->val + count;
        if (temp >=10) {count = 1; temp = temp -10;}
        else count = 0;
        prev->val = temp;
    }

    if (count) {
        struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
        prev->next = node;
        node->next = NULL;
        node->val = 1;
    }

    return l1;
}

题解:

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {
    struct ListNode *head = NULL, *tail = NULL;
    int carry = 0;
    while (l1 || l2) {
        int n1 = l1 ? l1->val : 0;
        int n2 = l2 ? l2->val : 0;
        int sum = n1 + n2 + carry;
        if (!head) {
            head = tail = malloc(sizeof(struct ListNode));
            tail->val = sum % 10;
            tail->next = NULL;
        } else {
            tail->next = malloc(sizeof(struct ListNode));
            tail->next->val = sum % 10;
            tail = tail->next;
            tail->next = NULL;
        }
        carry = sum / 10;
        if (l1) {
            l1 = l1->next;
        }
        if (l2) {
            l2 = l2->next;
        }
    }
    if (carry > 0) {
        tail->next = malloc(sizeof(struct ListNode));
        tail->next->val = carry;
        tail->next->next = NULL;
    }
    return head;
}

总结:

1.选取一个变量记录进位

2.我这里直接利用了之前便有的链表l1,在他的基础上改值,第一次循环到一个链表结尾。然后进行一个判断,如果l1结束,将l2的剩余部分接上去,然后再改值;如果l1没有结束,就在l1的基础上继续改值,操作同前。(这里我出现了一个小错误,如果l1结束,这里p1指向的不是最后一个节点,而是NULL,就不能直接用p1来接上p2剩余部分,要设计一个prev存取链表l1的最后一个节点)

3.这一题最容易错的是没有考虑到最后一个节点还能进位的情况,这样就会多出一个节点,需要单独进行判断。

4.题解这里设置比较巧妙,他的循环结束是直到两个链表都到结尾处,使用一个三元运算符来判断加值,并且单独设立了一个链表来存储结果,并分别设置头指针和尾指针,将数据不断往上加,但也要注意到最后一个节点进位的情况。

二.C++代码

核心代码(最省空间)

  • 我们可以使用 l1||l2 作为终止条件, 新链表存在l1中, 这样必然运行至遍历完两条链表为止;
    如果l1 > l2(长度), 那我们直接向后运行即可(l2 == nullptr后就不再next,默认其值为0)
    如果l1 < l2(长度), 那我们在l1==nullptr的时候,就要开始不断创建新节点(这就要求记录prev), 保证与l2同步了
    最后记得处理可能最高位的进位即可
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = l1;
        ListNode* pre = nullptr;
        int carry = 0; // 进位标志

        while (l1 || l2) {
            int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;
            carry = sum / 10; // 计算进位
            if (l1 == nullptr) {
                pre->next = new ListNode(sum % 10);
                pre = pre->next;
            } else {
                l1->val = sum % 10;
                pre = l1;
                l1 = l1->next;
            }

            if (l2)
                l2 = l2->next;
        }
        if (carry)
            pre->next = new ListNode(carry);
        return head; // 返回结果链表的头结点
    }
};
  • 我们这里采用l1&&l2作为终止条件,只要有一个链表遍历完就退出循环,由于我们采用l1接收新链表,这里有两种情况
    如果是l2nullptr, 那么我们对于剩余的l1继续操作即可
    如果是l1
    nullptr, 那么我们将剩余的l2接到l1的末尾, 然后对于剩余的l1继续操作即可
    这种方法比起上面减少了创建新节点的资源消耗!
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = l1;
        ListNode* pre = nullptr;
        int carry = 0; // 进位标志

        while (l1 && l2) {
            int sum = (l1 == nullptr ? 0 : l1->val) + (l2 == nullptr ? 0 : l2->val) + carry;
            carry = sum / 10;
            l1->val = sum % 10;
            pre = l1;
            l1 = l1->next;
            l2 = l2->next;
        }

        if (l2) {
            pre->next = l2;
            l1 = l2; // 这里本来pre->next = l1, 现在=l2, 就跟原来的最后一个l1==nullptr断掉了,我们要更新l1
        }

        while (l1) {
            int sum = l1->val + carry;
            carry = sum / 10;
            l1->val = sum % 10;
            pre = l1;
            l1 = l1->next;
        }

        if (carry)
            pre->next = new ListNode(carry);
        return head; // 返回结果链表的头结点
    }
};

核心代码(最省时间)

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode dummy(0); // 创建一个虚拟头结点,简化链表操作
        ListNode* current = &dummy; // 用于遍历链表
        int carry = 0; // 进位标志

        while (l1 || l2 || carry) {
            int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;
            carry = sum / 10; // 计算进位
            current->next = new ListNode(sum % 10); // 创建新结点
            current = current->next;

            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }

        return dummy.next; // 返回结果链表的头结点
    }
};

完整代码(包含测试用例)

#include <iostream>
#include <vector>

using namespace std;

struct ListNode {
    int val;
    ListNode* next;

    ListNode() : val(0), next(nullptr) {}

    ListNode(int x) : val(x), next(nullptr) {}

    ListNode(int x, ListNode* next) : val(x), next(next) {}
};

ListNode* createLinkedList(const std::vector<int>& values) {
    if (values.empty()) {
        return nullptr;
    }

    ListNode* head = new ListNode(values[0]);
    ListNode* current = head;

    for (int i = 1; i < values.size(); i++) {
        current->next = new ListNode(values[i]);
        current = current->next;
    }

    return head;
}

void deleteLinkedList(ListNode* head) {
    while (head) {
        ListNode* temp = head;
        head = head->next;
        delete temp;
    }
}

void printLinkedList(ListNode* head) {
    ListNode* current = head;
    while (current != nullptr) {
        std::cout << current->val;
        if (current->next != nullptr) {
            std::cout << " -> ";
        }
        current = current->next;
    }
    std::cout << std::endl;
}

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head = l1;
        ListNode* pre = nullptr;
        int carry = 0; // 进位标志

        while (l1 || l2) {
            int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;
            carry = sum / 10;
            if (l1 == nullptr) {
                pre->next = new ListNode(sum % 10);
                pre = pre->next;
            } else
                l1->val = sum % 10;
            if (l1) {
                pre = l1;
                l1 = l1->next;
            }
            if (l2) l2 = l2->next;
        }
        if (carry) pre->next = new ListNode(carry);
        return head;
    }
};

int main() {
    std::vector<int> values1 = {2, 4, 9};
    std::vector<int> values2 = {5, 6, 4, 9};

    ListNode* l1 = createLinkedList(values1);
    ListNode* l2 = createLinkedList(values2);

    Solution solution;
    ListNode* result = solution.addTwoNumbers(l1, l2);
    printLinkedList(result);

    // 释放内存
    deleteLinkedList(l1);
    deleteLinkedList(l2);

    return 0;
}


总结

这里要总结的主要有以下几个思路

1.这里最简单的思路其实是创建第三个链表进行存储,比较简单,,就不详细叙述了

2.在为了节省空间的同时,想要直接利用现成已有的链表节点,即l1

3.这里又分为几种情况:

l1的长度大于等于l2,这个时候除了最后一次若有进位需要创建新节点,其余均可利用l1现有节点即可
l1的长度小于l2,这里要么在之后均创建新的节点,要么直接接上l2后续的节点(经过测试两种方法空间利用差别不大,但第二种方法过于耗时)
这里注意一下为什么需要pre前置节点指针?因为当l1 = l1 -> next; 使得l1 == nullptr时,你直接使用l1 = new ListNode(...);只会使l1指向别的空间节点,但是实际上整个链表末尾指向的还是nullptr。(整个链表未变,只变了一个指针)
注意最后一个进位可能导致的节点数增加即可!

posted @ 2023-09-10 20:14  DawnTraveler  阅读(6)  评论(0编辑  收藏  举报