2.两数相加-初等数学运算

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

解题思路

由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加。

我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。具体而言,如果当前两个链表处相应位置的数字为 n1,n2,进位值为 carry,则它们的和为 n1+n2+carry;其中,答案链表处相应位置的数字为 (n1+n2+carry)mod10,而新的进位值为 (n1+n2+carry) / 10

如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 0 ;或者 短的链表 结束后,只留长的链表 与 进位 参与运算。两种不同的写法

此外,如果链表遍历结束后,有 carry>0,还需要在答案链表的后面附加一个节点,节点的值为 carry。

C

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
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 = carry + n1 + n2;
        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 = tail->next;
        tail->next = NULL;
    }
    return head;
}

三元运算符

n1 = l1 ? l1->val : 0

条件 ? 表达式1 : 表达式2

如果链表节点 l1 不为空,则 n1 的值为 l1->val;如果 l1 为空,则 n1 的值为 0

Python3

由C翻译得到

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        head = tail = None
        carry = 0
        
        while l1 or l2:
            n1 = l1.val if l1 else 0
            n2 = l2.val if l2 else 0
            total = carry + n1 + n2
            if not head:
                head = ListNode(total%10)
                tail = head
            else:
                tail.next = ListNode(total%10)
                tail = tail.next

            carry = total // 10
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next

        if carry > 0:
            tail.next = ListNode(carry)
            tail = tail.next

        return head

在Python中,sum是一个内置函数,用于计算可迭代对象(如列表、元组等)中所有元素的总和。然而,Python允许开发者将内置函数的名称用作变量名,但这样做会覆盖内置函数的功能,可能导致代码的可读性和功能性问题。

  • 变量名:将 sum 改为 total 避免与内置函数冲突

Python的三元运算符的语法结构是这样的:

a if condition else b

这里,condition 是一个布尔表达式,a 和 b 是在条件为真或假时返回的值。如果 condition 为真(True),则表达式的结果是 a;如果为假(False),则结果是 b

在Python中,/ 和 // 是两种不同的除法运算符,主要区别如下:

1. / - 普通除法(真除法)

  • 结果类型:总是返回浮点数(float)

  • 功能:执行精确的数学除法

2. // - 整数除法(地板除)

  • 结果类型:返回整数(向下取整)

  • 功能:取商的整数部分

Python中的引用行为

# Python中的引用行为
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# 创建链表: 1 -> 2 -> 3
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node1.next = node2
node2.next = node3

# l1 引用 node1
l1 = node1
print(l1.val)  # 输出: 1

# "移动指针" - 实际上是改变引用指向下一个节点
l1 = l1.next
print(l1.val)  # 输出: 2

区别

特性C指针Python引用
语法*l1l1->vall1.val
移动l1 = l1->nextl1 = l1.next
空值NULLNone
内存操作直接操作内存地址自动内存管理

另解

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        total = l1.val + l2.val
        carry = 1 if total >= 10 else 0
        head = ListNode(total%10)
        tail = head
        while l1.next and l2.next:
            l1 = l1.next
            l2 = l2.next
            total = l1.val + l2.val + carry
            carry = 1 if total >= 10 else 0
            tail.next = ListNode(total%10)
            tail = tail.next
        while l1.next:
            l1 = l1.next
            total = l1.val + carry
            carry = 1 if total >= 10 else 0
            tail.next = ListNode(total%10)
            tail = tail.next
        while l2.next:
            l2 = l2.next
            total = l2.val + carry
            carry = 1 if total >= 10 else 0
            tail.next = ListNode(total%10)
            tail = tail.next
        if carry == 1:
            tail.next = ListNode(1)
            tail = tail.next
        return head

C++

单链表节点的结构体定义

struct ListNode {
    int val;           // 节点存储的数据(整数值)
    ListNode *next;    // 指向下一个节点的指针
    
    // 构造函数重载
};

三个构造函数

1. 默认构造函数

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

  • 创建节点时:new ListNode()

  • 值 val 默认为 0

  • 指针 next 默认为 nullptr(空指针)

2. 带值的构造函数

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

  • 创建节点时:new ListNode(5)

  • 值 val 设置为传入的参数 x

  • 指针 next 默认为 nullptr

3. 完整构造函数

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

  • 创建节点时:new ListNode(5, nextNode)

  • 值 val 设置为 x

  • 指针 next 指向传入的下一个节点

构造函数和析构函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

使用初始化列表来初始化字段

特性初始化列表普通赋值
执行时机在构造函数体执行之前在构造函数体内部
效率更高(直接初始化)较低(先默认构造再赋值)
必须使用的情况const 成员、引用成员、没有默认构造的类成员无限制

区别原因:

C语言struct 关键字是必须的,因为 struct 名称和普通标识符在不同的命名空间中。

C++struct 名称进入普通的标识符命名空间,所以可以直接使用类名。

现代C++推荐写法:

// 方式1:每个变量单独声明(更清晰)
ListNode* head = nullptr;
ListNode* tail = nullptr;

// 方式2:使用一行声明
ListNode *head = nullptr, *tail = nullptr;

解法

/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *head = nullptr, *tail = nullptr;
        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 = new ListNode(sum%10);
            }else{
                tail->next = new ListNode(sum%10);
                tail = tail->next;
            }
            carry = sum / 10;
            if(l1){
                l1 = l1->next;
            }
            if(l2){
                l2 = l2->next;
            }
        }
        if(carry>0){
            tail->next = new ListNode(carry);
            tail = tail->next;
        }
        return head;
    }
};

Java

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = null, tail = null;
        int carry = 0;
        while (l1 != null || l2 != null){
            int n1 = (l1 != null) ? l1.val : 0;
            int n2 = (l2 != null) ? l2.val : 0;
            int sum = n1 + n2 + carry;
            if (head == null){
                head = tail = new ListNode(sum % 10);
            }else{
                tail.next = new ListNode(sum % 10);
                tail = tail.next;
            }
            carry = sum / 10;
            if (l1 != null){
                l1 = l1.next;
            }
            if (l2 != null){
                l2 = l2.next;
            }
        }
        if (carry>0){
            tail.next = new ListNode(carry);
            tail = tail.next;
        }
        return head;
    }
}

必须显式与 null 比较

语言语法原理
C/C++while (l1)指针到bool的隐式转换
Javawhile (l1 != null)严格的类型系统,无隐式转换
Pythonwhile l1所有对象都有布尔值

Java 中引用类型不能直接作为布尔条件,必须显式与 null 比较

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值