(一)问题描述
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
(二)示例展示
输入:l1 = [2, 4, 3];l2 = [5, 6, 4]
输出:[7, 0, 8]
解释:342 + 465 = 807
(三)算法分析
首先,整体过程是一个加法运算。其次,具体的实现方式是使用链表来完成,从而避免了 int 类型大小有限的缺陷。最后,输出的是一个链表的头。因此,解答的过程主要包括:遍历链表,创建新链表,两数按位相加并判断是否进位。为了方便起见,我们使用尾插法来创建链表,下面的参考代码将给出两种方法解决此问题。
(四)参考代码
说明一下:原题来自 LeetCode ,题目的要求是按照一定的函数结构进行编写,给出的函数参数是两个被加数的头,并且给出了结构体的形式如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
1、4ms 版本,非常简洁,使用了三元表达式非常聪明!
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {
struct ListNode* head = NULL; // 新链表的头节点
struct ListNode* tail = NULL; // 新链表的尾节点
int carry = 0; // 进位
while (l1 != NULL || l2 != NULL) { // 当两个链表只要有一个没有遍历完时,循环就继续
int val1 = l1 != NULL ? l1->val : 0; // 如果 l1 不为空,取其节点值,否则为 0
int val2 = l2 != NULL ? l2->val : 0; // 如果 l2 不为空,取其节点值,否则为 0
int sum = val1 + val2 + carry; // 计算两个节点值和进位的和
carry = sum / 10; // 更新进位
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode)); // 创建新的节点
node->val = sum % 10; // 节点值为和的个位数
node->next = NULL; // 节点指针为 NULL
if (head == NULL) { // 如果新链表为空,将头节点和尾节点都指向新节点
head = node;
tail = node;
} else { // 如果新链表不为空,将尾节点的指针指向新节点,并更新尾节点
tail->next = node;
tail = node;
}
if (l1 != NULL) l1 = l1->next; // 如果 l1 不为空,移动到下一个节点
if (l2 != NULL) l2 = l2->next; // 如果 l2 不为空,移动到下一个节点
}
if (carry > 0) { // 如果最高位有进位,创建一个节点存储进位,并将其添加到新链表的末尾
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->val = carry;
node->next = NULL;
tail->next = node;
tail = node;
}
return head; // 返回新链表的头节点
}
2、16ms 版本,比较容易想到,时间消耗比较长!
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {
struct ListNode* rear, *sum_head = NULL;
int sum_left = 0, left = 0;
/*Implementing the addition process*/
while(l1 != NULL && l2 != NULL){
/*Create a new node by tail-inserting way*/
struct ListNode* p;
p = (struct ListNode*) malloc (sizeof(struct ListNode));
p -> next = NULL;
if(!sum_head) sum_head = p;
else rear -> next = p;
rear = p;
/*the adding process*/
sum_left = (l1->val + l2->val + left) % 10;
left = (l1->val + l2->val + left) / 10;
p -> val = sum_left;
/*Move the nodes of l1 and l2 back one bit*/
l1 = l1 -> next;
l2 = l2 -> next;
}
if(l1){ //if the length of l1 is longer
rear -> next = l1;
while(l1){
sum_left = (l1->val + left) % 10;
left = (l1->val + left) / 10;
l1->val = sum_left;
if(left == 0) break;
else{
rear = l1;
l1 = l1->next;
}
}
if(l1 == NULL && left != 0){
struct ListNode* p;
p = (struct ListNode*) malloc (sizeof(struct ListNode));
p->val = left;
p->next = NULL;
rear->next = p;
}
}
else{ //if the length of l2 is longer
rear -> next = l2;
while(l2){
sum_left = (l2->val + left) % 10;
left = (l2->val + left) / 10;
l2->val = sum_left;
if(left == 0) break;
else{
rear = l2;
l2 = l2->next;
}
}
if(l2 == NULL && left != 0){
struct ListNode* p;
p = (struct ListNode*) malloc (sizeof(struct ListNode));
p->val = left;
p->next = NULL;
rear->next = p;
}
}
return sum_head;
}
(五)做题收获
本题中,我们学会了巧妙使用三元条件表达式,这一步大大减少了程序所消耗的时间。熟悉了大整数加法的按位运算以及链表的创建与连接。虽然难度不大,但涉及面也十分广泛。