LeetCode刷题(一)

🗨️字数统计=1.8k字 ⏳阅读时长≈8分钟

1. 两数之和(简单)

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例

1
2
3
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回[0, 1]

通过

1
2
执行用时:12 ms, 在所有 C++ 提交中击败了87.82 %的用户
内存消耗:8.3 MB, 在所有 C++ 提交中击败了100.00 %的用户

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <math.h>
#include <iomanip>
#include <unordered_map>
#include <string>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> m_hash;
vector<int> result;
for (int i = 0; i < nums.size(); i++) {
if (m_hash.count(nums[i]) != 0) {
result = { i,m_hash[nums[i]] };
return result;
}
else
{
m_hash[target - nums[i]] = i;
}
}
}

int main()
{
vector<int> a = { 11,2,15,7 };
vector<int> b = twoSum(a, 9);
cout << b[0] << b[1] << endl;

return 0;
}

思路解析

我们可以通过遍历一次哈希表完成。在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。

复杂度分析:

– 时间复杂度:O(n),我们只遍历了包含有 n 个元素的列表一次。在表中进行的每次查找只花费 O(1) 的时间。

– 空间复杂度:O(n),所需的额外空间取决于哈希表中存储的元素数量,该表最多需要存储 n 个元素。

2. 两数相加(中等)

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

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

示例

1
2
3
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

代码如下(解法一)

通过

1
2
执行用时:36 ms, 在所有 C++ 提交中击败了55.53 %的用户
内存消耗:9.3 MB, 在所有 C++ 提交中击败了100.00 %的用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <math.h>
#include <iomanip>
#include <unordered_map>
#include <string>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

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

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
int carry = 0;//进位
ListNode* result = new ListNode(0);
result->next = new ListNode(0);
ListNode* cur = result->next;
while (l1 != NULL || l2 != NULL || carry)
{
if (l1 == NULL)l1 = new ListNode(0);
if (l2 == NULL)l2 = new ListNode(0);
cur->val = (l1->val + l2->val+ carry) % 10;
carry = (l1->val + l2->val + carry) / 10;
l1 = l1->next;
l2 = l2->next;
if (l1 != NULL || l2 != NULL || carry) {
cur->next = new ListNode(0);
cur = cur->next;
}
}
return result->next;
}

int main()
{
ListNode* a1 = new ListNode(2);
a1->next = new ListNode(4);
a1->next->next = new ListNode(3);
ListNode* a2 = new ListNode(5);
a2->next = new ListNode(6);
a2->next->next = new ListNode(4);
ListNode* b = addTwoNumbers(a1,a2);

return 0;
}

代码如下(解法二)

通过

1
2
执行用时:24 ms, 在所有 C++ 提交中击败了97.43 %的用户
内存消耗:8.6 MB, 在所有 C++ 提交中击败了100.00 %的用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <math.h>
#include <iomanip>
#include <unordered_map>
#include <string>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

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

ListNode* addTwoNumbers2(ListNode* l1, ListNode* l2) {
int c = 0, temp = 0;
ListNode* t1 = l1, * t2 = l2;
while (t1->next != NULL && t2->next != NULL) {
temp = (t1->val + t2->val + c) / 10;
t1->val = (t1->val + t2->val + c) % 10;
c = temp;
t1 = t1->next;
t2 = t2->next;
}
if (t1->next == NULL && t2->next == NULL) {
temp = t1->val + t2->val + c;
if (temp > 9)
t1->next = new ListNode(temp / 10);
t1->val = temp % 10;
}
else {
if (t1->next == NULL)
t1->next = t2->next;
temp = (t1->val + t2->val + c) / 10;
t1->val = (t1->val + t2->val + c) % 10;
c = temp;
t1 = t1->next;
while (c != 0 && t1->next != NULL) {
temp = t1->val + c;
t1->val = temp % 10;
c = temp / 10;
t1 = t1->next;
}
if (c != 0) {
if (t1->val + c > 9)
t1->next = new ListNode(1);
t1->val = (t1->val + c) % 10;
}
}
return l1;
}

int main()
{
ListNode* a1 = new ListNode(2);
a1->next = new ListNode(4);
a1->next->next = new ListNode(3);
ListNode* a2 = new ListNode(5);
a2->next = new ListNode(6);
a2->next->next = new ListNode(4);
ListNode* b = addTwoNumbers(a1,a2);

return 0;
}

思路解析

就像在纸上计算两个数字的和那样,我们首先从最低有效位也就是列表 l1 和 l2 的表头开始相加。由于每位数字都应当处于 0…9 的范围内,我们计算两个数字的和时可能会出现 “溢出”。例如,5 + 7 = 12。在这种情况下,我们会将当前位的数值设置为 2,并将进位 carry = 1 带入下一次迭代。进位 carry 必定是 0 或 1,这是因为两个数字相加(考虑到进位)可能出现的最大和为 9 + 9 + 1 = 19。

伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
- 将当前结点初始化为返回列表的哑结点。
- 将进位 carry 初始化为 0。
- 将 p 和 q 分别初始化为列表 l1 和 l2 的头部。
- 遍历列表 l1 和 l2 直至到达它们的尾端。
--- 将 xx 设为结点 p 的值。如果 p 已经到达 l1 的末尾,则将其值设置为 0。
--- 将 yy 设为结点 q 的值。如果 q 已经到达 l2 的末尾,则将其值设置为 0。
--- 设定 sum = x + y + carry。
--- 更新进位的值,carry = sum / 10。
--- 创建一个数值为 (sum mod 10) 的新结点,并将其设置为当前结点的下一个结点,然后将当前结点前进到下一个结点。
--- 同时,将 pp 和 qq 前进到下一个结点。
- 检查 carry = 1 是否成立,如果成立,则向返回列表追加一个含有数字 1 的新结点。
- 返回哑结点的下一个结点。

请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来初始化表头的值。

注意陷阱:若只考虑三位数相加,那么会出现val = (l1->val +l2->val) + carry / 10,但这样是错的,还要考虑5 + 5 = 10以及99 + 1 = 100等情况,则val = (l1->val +l2->val + carry) / 10才是对的。

复杂度分析:

– 时间复杂度:O(max(m, n)),假设 m 和 n 分别表示 l1 和 l2 的长度,上面的算法最多重复 max(m,n) 次。

– 空间复杂度:O(max(m, n)), 新列表的长度最多为 max(m,n)+1。

分享到