LeetCode 两数相加 链表操作的思考
题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
遇到的问题:
样例执行似乎没有问题,但是提交就出错:
Line 22: Char 22: runtime error: member access within null pointer of type 'struct ListNode' (solution.c)
经过检查发现,当 p 的 next 域为空时,此时建立新节点,然后再将其添加到链表尾端,比较麻烦,因为需要记住前一个节点,将其指针域指向刚刚创建的新节点。于是乎,经过分析,我们可以这样解决这个问题:
-
为了此操作不会影响到链表的状态,我们决定牺牲一些内存资源,创建一个新的带头节点的链表,注意是带头节点的,这样后续操作会方便一些。
-
将进位 carry 初始化为 0。
-
将 p 和 q 分别初始化为列表 l1 和 l2 的头部(就是第一个节点,本题中的链表是不带头节点的)。
-
遍历列表 l1 和 l2 直至到达它们的尾端。
-
将 x 设为结点 p 的值。如果 p 已经到达 l1 的末尾,则将其值设置为 0 (这里可以理解为将两个长度不一致链表用 0 扩展为相等的,这一点很重要、很关键、很巧妙,反正我当时没想到)。
-
将 y 设为结点 q 的值。如果 q 已经到达 q 的末尾,则将其值设置为 0 (与上一步类似)。
-
设定 sum=x+y+carry。
-
更新进位的值,carry=sum/10
-
创建一个数值为 (sum mod 10) 的新结点,并将其设置为当前结点的下一个结点,然后将当前结点前进到下一个结点。
-
将 p 和 q 前进到下一个结点。
-
检查 carry=1carry = 1carry=1 是否成立,如果成立,则向返回列表追加一个含有数字 111 的新结点。
-
返回头指针的下一个结点,因为我们创建的是一个带有头结点的链表。
-
结束
源码(已经全部写好,可以直接调试运行):
#define _CRT_SECURE_NO_WARNINGS /*vs 下写的, scanf 会出错,忽略*/
#include <stdio.h>
#include <stdlib.h>
struct ListNode
{
int val;
int len;
struct ListNode *next;
};
struct ListNode *creatList()
{
int i = 0 , n = 0;
puts("enter number of list:\n");
scanf("%d", &n);
struct ListNode *head = NULL, *rear = NULL, *behind = NULL;
for (i = 0; i < n; i++)
{
behind = (struct ListNode *) malloc(sizeof(struct ListNode)); /*申请存储空间*/
/*填充一个结点的信息*/
puts("enter data zone:");
scanf("%d", &behind->val);
if (i == 0) /*链表为空则把新结点存入头指针*/
{
rear = behind;
head = behind;
}
else
{
rear->next = behind; /*链表不空则把新结点连到前一结点的后面*/
head->len++;
}
behind->next = NULL; /*使新结点的指针域为空*/
rear = behind; /* rear移动到新结点上*/
}
return head; /*返回链表头指针*/
}
void output(struct ListNode *head)
{
struct ListNode *p = head;
puts("list data:\n");
while (p)
{
printf("%-3d", p->val);
p = p->next;
}
puts("\n");
}
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2)
{
struct ListNode *p = l1, *q = l2;
struct ListNode *newHead = (struct ListNode *)malloc(sizeof(struct ListNode));
struct ListNode *curr = newHead;
int carry = 0;
while (p != NULL || q != NULL)
{
int x = (p != NULL) ? p->val : 0;
int y = (q != NULL) ? q->val : 0;
int sum = x + y + carry;
carry = sum / 10;
struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
node->next = NULL;
node->val = sum % 10;
curr->next = node;
curr = curr->next;
if (p != NULL)
p = p->next;
if (q != NULL)
q = q->next;
}
if (carry > 0)
{
struct ListNode *node = (struct ListNode *)malloc(sizeof(struct ListNode));
node->next = NULL;
node->val = 1;
curr->next = node;
}
return newHead->next;
}
int main(void)
{
struct ListNode *ListHead1 = creatList();
struct ListNode *ListHead2 = creatList();
output(ListHead1);
output(ListHead2);
output(addTwoNumbers(ListHead1, ListHead2));
return 0;
}
总结
对于指针的操作,一定要谨慎再谨慎。开始改的受,只是将 curr 的next 域指向了新节点,但是并没有将 curr 本身后移一位,造成只能输出最后一位,单步调试后才发现了这个问题。所以一定要会调试,顺便说一下,VS 真的很强大。