LEETCODE-2.两数之和
题目-两数之和
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-two-numbers
方法 模拟竖式运算
思路较为清晰,两个链表相加,数位顺序是大尾端,也就是从链表的头开始进行加法运算即可,进位用一个int型变量作为缓存,等到进入下一个循环中加入到求和中即可。
此处对链表的遍历有两个问题:
- 不知道l1与l2哪个较长
- 提前设定好输出链表的大小会带来内存溢出问题
问题1的解决,我们采用 while(p!=NULL||q!=NULL) 的写法,这样可以一直遍历到最长链表的尾部,并且在进行链表向下转移的判断条件是 p->next != NULL 。
问题2的解决,采用遍历链表的同时分配新的内存空间。在每个循环内部,使用 cur->next = (struct ListNode*)malloc(sizeof(struct ListNode)) 的写法。
另外需要注意的是,会出现最后生成的链表长度大于原先任一链表的情况,此时需要考虑最后一个结点的处理逻辑。
Note: The returned array must be malloced, assume caller calls free().
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
int x, y, num, flag=0;
struct ListNode *p = l1, *q = l2;
struct ListNode *cur = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *pre_cur ;
cur->next = NULL;
struct ListNode *head = cur;
while(p!=NULL || q!=NULL)
{
x = (p!=NULL) ? p->val : 0;
y = (q!=NULL) ? q->val : 0;
num = x+y+flag;
flag = num/10;
cur->val = num%10;
cur->next = (struct ListNode*)malloc(sizeof(struct ListNode));
pre_cur = cur;
cur = cur->next;
cur->next = NULL;
if(p!=NULL)
p = p->next;
if(q!=NULL)
q = q->next;
}
if(flag > 0)
{
cur->val = 1;
cur->next = NULL;
}
else
{
cur = pre_cur;
cur->next = NULL;
}
return head;
}
方法二 hash查找
方法一的复杂度在于,要想得知外层循环当前所在的数字是否为满足和要求的数字,需要将其后的所有数字都查询一遍,复杂度为O(n^2)。引入哈希表的思路是可以直接找到需要的数字是否在数组中,可以将复杂度降至O(n)。
哈希表的构建:
- 将hash[MAX_SIZE]的每一项初始化为-1
- 对nums数组进行遍历,假设当前对象为nums[i]
- 查看hash[nums[i]],如果该项的值为-1,就将其替换为i
例:
nums数组:[2 , 4 , 6 , 7]
nums下标: 0 1 2 3
hash表:[ -1,-1,0, -1, 1,-1, 2, 3,…]
hash下标:0 1 2 3 4 5 6 7
由此得到的hash表,可以作为第二个和因子的查询表。这样在外层循环当前指向nums[i]时,只需要查找hash[target - nums[i]]的值,如果为-1说明target - nums[i]在数组中不存在,如果为自然数则为target - nums[i]在数组中的位置。
另外需要注意的是,可能会出现负数,对于负数的操作则补充到hash表的队尾。
int *twoSum(int *nums, int numsSize, int target, int *returnSize)
{
int i, hash[2048], *res = (int *)malloc(sizeof(int) * 2);
memset(hash, -1, sizeof(hash));
for (i = 0; i < numsSize; i++)
{
if (hash[(target - nums[i] + 2048) % 2048] != -1)
{
res[0] = hash[(target - nums[i] + 2048) % 2048];
res[1] = i;
*returnSize = 2;
return res;
}
hash[(nums[i] + 2048) % 2048] = i;
}
free(hash);
*returnSize = 0;
return res;
}
该算法要求hash表的大小需要足够大,不能发生正数区和负数区的碰撞。
通过在hash表中增加一项标记,标明每个项目入表时是正值还是负值可以解决一部分问题。
参考
https://leetcode-cn.com/problems/two-sum/solution/cyu-yan-ji-yu-shu-zu-de-san-lie-15xing-dai-ma-8ms-/