从零开始的LeetCode题解

从零开始的LeetCode题解

始まりと終わりのプロローグ

-Turning Point-


数组

二分

704. 二分查找
35. 搜索插入位置

在数组中搜索元素的题通常都可以用二分。但要注意前提是该数组有序

关于二分的边界条件
在二分的过程中,要保证每次分的区间都满足一开始的定义。
而区间的定义一般有两种:左闭右闭 和 左闭右开。
以左闭右闭为例:

  • while(left <= right) 必须是 <= ,因为 leftright 是有可能相等的。
  • 同理left 更新时是 mid + 1right 更新时是 mid - 1

704. 二分查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = nums.size();
int l = 0, r = n - 1;
int mid = 0;
while (l <= r) {
mid = (l + r) / 2;
if (nums[mid] > target) {
r = mid - 1;
} else if (nums[mid] < target) {
l = mid + 1;
} else return mid;
}
return -1;
}
};

双指针

27. 移除元素
977. 有序数组的平方

这类题往往用暴力需要两层 for 循环。如果在第一层循环时能够记录一些信息提供给第二层循环,则可以考虑使用双指针。

27. 移除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int l = 0;
int n = nums.size();
for (int r = 0; r < n; r ++) {
if (val != nums[r]) {
nums[l++] = nums[r];
}
}
return r;
}
};

但双指针不一定要是都向同一个方向运动,也可以是从数组两端向中间走。

977. 有序数组的平方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
int k = n - 1;
vector<int> ans(n, 0);
int l = 0, r = n - 1;
while (l != r) {
int a = nums[l] * nums[l];
int b = nums[r] * nums[r];
if (a >= b) {
ans[k --] = a;
l ++;
} else {
ans[k --] = b;
r --;
}
}
ans[0] = nums[l] * nums[l];
return ans;
}
};

滑动窗口

209. 长度最小的子数组

本质也是双指针。适用于求解数组中的子数组的情况。

209. 长度最小的子数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int l = 0, r = 0;
int sum = 0, res = 0;
int len = INT_MAX; // 窗口的长度

for (r = 0; r < n; r ++) {
sum += nums[r];
while (sum >= target) {
len = min(len, r - l + 1);
sum -= nums[l ++];
}
}
if (len == INT_MAX) return 0;
return len;
}
};

链表

链表定义

1
2
3
4
5
6
7
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) {}
};

删除元素

203. 移除链表元素

对链表进行删除时一定要注意判断链表是否为空,以及对头结点的特殊操作。
另外这题用到了虚拟头结点,这样就不用针对头结点特判,也是一个实用的技巧。

203. 移除链表元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0, head); // 新建一个虚拟头结点,next指向head
ListNode* cur = dummyHead;
while (cur->next != nullptr) {
ListNode* tmp = cur->next;
if (tmp->val == val) {
cur->next = tmp->next;
delete tmp;
} else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};

反转链表

206. 反转链表

原本想的是定义一个新链表,然后把旧链表元素依次插到新表头。但是显然复杂度比较高。
好的办法依然是双指针。从头开始直接改变 next 指针的朝向。

206. 反转链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
ListNode* tmp;
while (cur) {
tmp = cur->next;
cur->next = pre;
pre = cur; // pre 前进
cur = tmp; // cur 前进
}
return pre;
}
};

交换结点

24. 两两交换链表中的节点

用到了前面提到的虚拟头结点。
没啥难的,主要是要自己画图模拟一下。

24. 两两交换链表中的节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0, head);
ListNode* cur = dummyHead;
while (cur->next != nullptr && cur->next->next != nullptr) {
ListNode* t = cur->next;
ListNode* t1 = cur->next->next->next;

cur->next = cur->next->next;
cur->next->next = t;
cur->next->next->next = t1;

cur = cur->next->next;
}
// 注意不要直接返回head,因为被cur修改过了,head不再指向表头了
return dummyHead->next;
}
};

链表中的双指针

19.删除链表的倒数第 N 个结点

双指针的经典应用。fast 先移动 n 个位置,再让 slow 指针一起移动。
虚拟头结点依然很方便,不用特别处理头结点。

19. 删除链表的倒数第 N 个结点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0, head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
while (n-- && fast->next != nullptr) {
fast = fast->next;
}
while (fast->next != nullptr) {
fast = fast->next;
slow = slow->next;
}
if (n == 1) {
delete fast;
return head;
}
ListNode* tmp = slow->next;
slow->next = tmp->next;
delete tmp;
return dummyHead->next;

}
}

142. 环形链表 II

两种解法:
1、数学题。详解看这里
2、哈希表。遍历每个结点,并加入哈希表。遇到的第一个遍历过的结点就是环的入口。

142. 环形链表 II

解法一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
if (slow == fast) {
ListNode* p = head;
ListNode* t = fast;
while (p != t) {
p = p->next;
t = t->next;
}
return p;
}
}
return NULL;
}
};

解法二

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode*> visited;
while(head != NULL) {
if (visited.count(head)) return head;
visited.insert(head);
head = head->next;
}
return NULL;
}
};

设计链表

707. 设计链表

模拟,注意 index 从 0 开始。

class MyLinkedList {

public:
    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) {}
    };

    MyLinkedList() {
        dummyHead = new ListNode(0);
        size = 0;
    }

    int get(int index) {
        if (index < 0 || index > size - 1) return -1;
        ListNode* cur = dummyHead->next;
        while (index --) cur = cur->next;
        return cur->val;

    }
    
    void addAtHead(int val) {
        ListNode* node = new ListNode(val, dummyHead->next);
        dummyHead->next = node;
        size ++;
    }
    
    void addAtTail(int val) {
        ListNode* cur = dummyHead;
        while (cur->next != nullptr) {
            cur = cur->next;
        }
        ListNode* node = new ListNode(val);
        cur->next = node;
        size ++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > size) return;
        ListNode* cur = dummyHead;
        ListNode* node = new ListNode(val);
        while (index --) cur = cur->next;
        node->next = cur->next;
        cur->next = node;
        size ++;
    }
    
    void deleteAtIndex(int index) {
        if (index < 0 || index > size - 1) return;
        ListNode* cur = dummyHead;
        while (index --) cur = cur->next;
        ListNode* tmp = cur->next;
        cur->next = tmp->next;
        delete tmp;
        size --;
    }
private:
    int size;
    ListNode* dummyHead;
};
作者

Moodles

发布于

2022-04-05

更新于

2022-04-07

许可协议