Update
@ -14,8 +14,12 @@ LeetCode 最强题解(持续更新中):
|
|||||||
|[0035.搜索插入位置](https://github.com/youngyangyang04/leetcode/blob/master/problems/0035.搜索插入位置.md) |数组 | **暴力** **二分**|
|
|[0035.搜索插入位置](https://github.com/youngyangyang04/leetcode/blob/master/problems/0035.搜索插入位置.md) |数组 | **暴力** **二分**|
|
||||||
|[0053.最大子序和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0053.最大子序和.md) |数组 |**暴力** **贪心** 动态规划 分治|
|
|[0053.最大子序和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0053.最大子序和.md) |数组 |**暴力** **贪心** 动态规划 分治|
|
||||||
|[0059.螺旋矩阵II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0059.螺旋矩阵II.md) |数组 |**模拟**|
|
|[0059.螺旋矩阵II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0059.螺旋矩阵II.md) |数组 |**模拟**|
|
||||||
|
|[0083.删除排序链表中的重复元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0083.删除排序链表中的重复元素.md) |链表 |**模拟**|
|
||||||
|
|[0203.移除链表元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0203.移除链表元素.md) |链表 |**模拟** **虚拟头结点**|
|
||||||
|
|[0206.翻转链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0206.翻转链表.md) |链表 | **模拟** **递归**|
|
||||||
|[0209.长度最小的子数组](https://github.com/youngyangyang04/leetcode/blob/master/problems/0209.长度最小的子数组.md) |数组 | **暴力** **滑动窗口**|
|
|[0209.长度最小的子数组](https://github.com/youngyangyang04/leetcode/blob/master/problems/0209.长度最小的子数组.md) |数组 | **暴力** **滑动窗口**|
|
||||||
|[0237.删除链表中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0237.删除链表中的节点.md) |链表 | **暴力**|
|
|[0237.删除链表中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0237.删除链表中的节点.md) |链表 | **原链表移除** **添加虚拟节点** 递归|
|
||||||
|[0383.赎金信](https://github.com/youngyangyang04/leetcode/blob/master/problems/0383.赎金信.md) |数组 |**暴力** **字典计数**|
|
|[0383.赎金信](https://github.com/youngyangyang04/leetcode/blob/master/problems/0383.赎金信.md) |数组 |**暴力** **字典计数**|
|
||||||
|
|[0707.设计链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0707.设计链表.md) |链表 |**模拟**|
|
||||||
|
|
||||||
|
|
||||||
|
BIN
pics/203_链表删除元素1.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
pics/203_链表删除元素2.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
pics/203_链表删除元素3.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
pics/203_链表删除元素4.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
pics/203_链表删除元素5.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
pics/203_链表删除元素6.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
pics/206_反转链表.png
Normal file
After Width: | Height: | Size: 26 KiB |
@ -1,8 +1,8 @@
|
|||||||
## 题目地址
|
# 题目地址
|
||||||
|
|
||||||
https://leetcode-cn.com/problems/search-insert-position/
|
https://leetcode-cn.com/problems/search-insert-position/
|
||||||
|
|
||||||
## 思路
|
# 思路
|
||||||
|
|
||||||
这道题目其实是一道很简单的题,但是为什么通过率相对来说并不高呢,我理解是大家对 边界处理的判断有所失误,导致的。
|
这道题目其实是一道很简单的题,但是为什么通过率相对来说并不高呢,我理解是大家对 边界处理的判断有所失误,导致的。
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ https://leetcode-cn.com/problems/search-insert-position/
|
|||||||
这里我给出了一种简洁的暴力解法,和两种二分查找的解法
|
这里我给出了一种简洁的暴力解法,和两种二分查找的解法
|
||||||
|
|
||||||
|
|
||||||
## 解法:暴力枚举
|
# 解法:暴力枚举
|
||||||
|
|
||||||
```
|
```
|
||||||
class Solution {
|
class Solution {
|
||||||
@ -49,7 +49,7 @@ public:
|
|||||||
时间复杂度:O(1)
|
时间复杂度:O(1)
|
||||||
|
|
||||||
|
|
||||||
## 二分法
|
# 二分法
|
||||||
|
|
||||||
既然暴力解法的时间复杂度是On,我们就要尝试一下使用二分查找法。
|
既然暴力解法的时间复杂度是On,我们就要尝试一下使用二分查找法。
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public:
|
|||||||
|
|
||||||
我们要在二分查找的过程中,保持不变量,这也就是**循环不变量** (感兴趣的同学可以查一查)
|
我们要在二分查找的过程中,保持不变量,这也就是**循环不变量** (感兴趣的同学可以查一查)
|
||||||
|
|
||||||
### 二分法第一种写法
|
## 二分法第一种写法
|
||||||
|
|
||||||
以这道题目来举例,以下的代码中我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
|
以这道题目来举例,以下的代码中我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ public:
|
|||||||
效率如下:
|
效率如下:
|
||||||
<img src='../pics/35_搜索插入位置2.png' width=600> </img></div>
|
<img src='../pics/35_搜索插入位置2.png' width=600> </img></div>
|
||||||
|
|
||||||
### 二分法第二种写法
|
## 二分法第二种写法
|
||||||
|
|
||||||
如果说我们定义 target 是在一个在左闭右开的区间里,也就是[left, right)
|
如果说我们定义 target 是在一个在左闭右开的区间里,也就是[left, right)
|
||||||
|
|
||||||
|
38
problems/0083.删除排序链表中的重复元素.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
## 题目地址
|
||||||
|
|
||||||
|
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
这道题目没有必要设置虚拟节点,因为不会删除头结点
|
||||||
|
|
||||||
|
## 代码
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* Definition for singly-linked list.
|
||||||
|
* struct ListNode {
|
||||||
|
* int val;
|
||||||
|
* ListNode *next;
|
||||||
|
* ListNode(int x) : val(x), next(NULL) {}
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
ListNode* deleteDuplicates(ListNode* head) {
|
||||||
|
ListNode* p = head;
|
||||||
|
while (p != NULL && p->next!= NULL) {
|
||||||
|
if (p->val == p->next->val) {
|
||||||
|
ListNode* tmp = p->next;
|
||||||
|
p->next = p->next->next;
|
||||||
|
delete tmp;
|
||||||
|
} else {
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
46
problems/0142.环形链表II.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
## 题目地址
|
||||||
|
https://leetcode-cn.com/problems/linked-list-cycle-ii/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
快慢指针为什么一定会相遇
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.第一次相遇,slow = nb
|
||||||
|
2.a+nb = 入口点
|
||||||
|
3.slow再走a = 入口 = head走到入口 = a
|
||||||
|
4.由3得出,起始距离入口 = 第一次相遇位置 + a
|
||||||
|
|
||||||
|
## 代码
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* Definition for singly-linked list.
|
||||||
|
* struct ListNode {
|
||||||
|
* int val;
|
||||||
|
* ListNode *next;
|
||||||
|
* ListNode(int x) : val(x), next(NULL) {}
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
ListNode *detectCycle(ListNode *head) {
|
||||||
|
ListNode* fast = head;
|
||||||
|
ListNode* slow = head;
|
||||||
|
while(fast != NULL && fast->next != NULL) {
|
||||||
|
slow = slow->next;
|
||||||
|
fast = fast->next->next;
|
||||||
|
if (slow == fast) { // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
|
||||||
|
slow = head;
|
||||||
|
while (slow != fast) {
|
||||||
|
fast = fast->next;
|
||||||
|
slow = slow->next;
|
||||||
|
}
|
||||||
|
return fast; // 返回环的入口
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
130
problems/0203.移除链表元素.md
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
## 题目地址
|
||||||
|
|
||||||
|
https://leetcode-cn.com/problems/remove-linked-list-elements/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
我们这里以链表 1 4 2 4 来举例,移除元素4
|
||||||
|
|
||||||
|
<img src='../pics/203_链表删除元素1.png' width=600> </img></div>
|
||||||
|
|
||||||
|
如果使用C,C++编程语言的话,不要忘了还要从内存中删除这两个移除的节点, 清理节点内存之后如图:
|
||||||
|
|
||||||
|
<img src='../pics/203_链表删除元素2.png' width=600> </img></div>
|
||||||
|
|
||||||
|
**当然如果使用java ,python的话就不用手动管理内存了。**
|
||||||
|
|
||||||
|
还要说明一下,就算我们使用C++来做leetcode,如果移除一个节点之后,没有手动在内存中删除这个节点,leetcode依然也是可以通过的,只不过,内存使用的空间大一些而已,但建议同学们依然要养生手动清理内存的习惯,因为在面试中 这些细节都是面试官考察的点
|
||||||
|
|
||||||
|
这种情况下的移除操作,就是让节点next指针直接指向下下一个节点就可以了,
|
||||||
|
|
||||||
|
那么因为单链表的特殊性,只能指向下一个节点,我们刚刚删除的是 链表的中第二个,和第四个节点,那么如果我们删除的是头结点 又该怎么办呢
|
||||||
|
|
||||||
|
这里就涉及到 链表操作的两种方式,**一种是 直接使用原来的链表来进行删除操作,一种是设置一个虚拟头结点在进行删除操作。**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
我们来看第一种操作 直接使用原来的链表来进行移除
|
||||||
|
|
||||||
|
<img src='../pics/203_链表删除元素3.png' width=600> </img></div>
|
||||||
|
|
||||||
|
移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点
|
||||||
|
|
||||||
|
所以头结点如何移除呢,其实我们只要 将头结点向后移动一位就可以,这样我们就从链表中移除了一个头结点
|
||||||
|
|
||||||
|
<img src='../pics/203_链表删除元素4.png' width=600> </img></div>
|
||||||
|
|
||||||
|
|
||||||
|
依然别忘将原头结点从内存中删掉
|
||||||
|
<img src='../pics/203_链表删除元素5.png' width=600> </img></div>
|
||||||
|
|
||||||
|
|
||||||
|
这样我们移除了一个头结点,是不是发现,在单链表中移除头结点 和 移除其他节点的操作方式是不一样,其实在我们写代码的时候也会发现,需要单独写一段逻辑来处理 移除头结点的情况。
|
||||||
|
|
||||||
|
那么可不可以 以一种统一的逻辑来移除 链表的节点呢。
|
||||||
|
|
||||||
|
其实我们可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。
|
||||||
|
|
||||||
|
我们来看看如何设置一个虚拟头。我们依然还是在这个链表中,移除元素1。
|
||||||
|
|
||||||
|
|
||||||
|
<img src='../pics/203_链表删除元素6.png' width=600> </img></div>
|
||||||
|
|
||||||
|
这里我们来给链表添加一个虚拟头结点为新的头结点,此时我们要移除这个旧头结点 元素1,
|
||||||
|
|
||||||
|
这样是不是就可以使用 和移除链表其他节点的方式统一了呢
|
||||||
|
|
||||||
|
大家来看一下,如何移除元素1 呢,还是我们熟悉的方式,然后从内存中删除元素1
|
||||||
|
|
||||||
|
最后呢在题目中,return 头结点的时候,别让了 `return dummyNode->next;`, 这才是新的头结点
|
||||||
|
|
||||||
|
|
||||||
|
## 代码
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* Definition for singly-linked list.
|
||||||
|
* struct ListNode {
|
||||||
|
* int val;
|
||||||
|
* ListNode *next;
|
||||||
|
* ListNode(int x) : val(x), next(NULL) {}
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
// 直接使用原来的链表来进行移除节点操作
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
ListNode* removeElements(ListNode* head, int val) {
|
||||||
|
// 删除头结点
|
||||||
|
while (head != NULL && head->val == val) { // 注意这里不是if
|
||||||
|
ListNode* tmp = head;
|
||||||
|
head = head->next;
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除非头结点
|
||||||
|
ListNode* cur = head;
|
||||||
|
while (cur != NULL && cur->next!= NULL) {
|
||||||
|
if (cur->next->val == val) {
|
||||||
|
ListNode* tmp = cur->next;
|
||||||
|
cur->next = cur->next->next;
|
||||||
|
delete tmp;
|
||||||
|
} else {
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* Definition for singly-linked list.
|
||||||
|
* struct ListNode {
|
||||||
|
* int val;
|
||||||
|
* ListNode *next;
|
||||||
|
* ListNode(int x) : val(x), next(NULL) {}
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
// 设置一个虚拟头结点在进行移除节点操作
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
ListNode* removeElements(ListNode* head, int val) {
|
||||||
|
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
|
||||||
|
dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
|
||||||
|
ListNode* cur = dummyHead;
|
||||||
|
|
||||||
|
while (cur->next != NULL) {
|
||||||
|
if(cur->next->val == val) {
|
||||||
|
ListNode* tmp = cur->next;
|
||||||
|
cur->next = cur->next->next;
|
||||||
|
delete tmp;
|
||||||
|
} else {
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dummyHead->next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
56
problems/0206.翻转链表.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
## 题目地址
|
||||||
|
|
||||||
|
https://leetcode-cn.com/problems/reverse-linked-list/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表,如图所示
|
||||||
|
|
||||||
|
<img src='../pics/206_反转链表.png' width=600> </img></div>
|
||||||
|
|
||||||
|
## 代码
|
||||||
|
|
||||||
|
### 模拟算法
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* Definition for singly-linked list.
|
||||||
|
* struct ListNode {
|
||||||
|
* int val;
|
||||||
|
* ListNode *next;
|
||||||
|
* ListNode(int x) : val(x), next(NULL) {}
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
ListNode* reverseList(ListNode* head) {
|
||||||
|
ListNode* temp; // 保存cur的下一个节点
|
||||||
|
ListNode* cur = head;
|
||||||
|
ListNode* pre = NULL;
|
||||||
|
while(cur) {
|
||||||
|
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
|
||||||
|
cur->next = pre; // 翻转操作
|
||||||
|
// 更新pre 和 cur指针
|
||||||
|
pre = cur;
|
||||||
|
cur = temp;
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 递归算法
|
||||||
|
```
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
ListNode* reverse(ListNode* pre,ListNode* cur){
|
||||||
|
if(cur == NULL) return pre;
|
||||||
|
ListNode* temp = cur->next;
|
||||||
|
cur->next = pre;
|
||||||
|
return reverse(cur,temp);
|
||||||
|
}
|
||||||
|
ListNode* reverseList(ListNode* head) {
|
||||||
|
return reverse(NULL, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
```
|
110
problems/0707.设计链表.md
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
## 题目地址
|
||||||
|
|
||||||
|
https://leetcode-cn.com/problems/design-linked-list/
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
|
||||||
|
这道题目设计链表的五个接口
|
||||||
|
* 获取链表第index个节点的数值
|
||||||
|
* 在链表的最前面插入一个节点
|
||||||
|
* 在链表的最后面插入一个节点
|
||||||
|
* 在链表第index个节点前面插入一个节点
|
||||||
|
* 删除链表的第index个节点
|
||||||
|
|
||||||
|
可以说这五个接口,已经覆盖了链表的常见操作,是练习链表操作非常好的一道题目
|
||||||
|
|
||||||
|
## 代码
|
||||||
|
```
|
||||||
|
class MyLinkedList {
|
||||||
|
public:
|
||||||
|
// 定义链表节点结构体
|
||||||
|
struct LinkedNode {
|
||||||
|
int val;
|
||||||
|
LinkedNode* next;
|
||||||
|
LinkedNode(int val):val(val), next(nullptr){}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化链表
|
||||||
|
MyLinkedList() {
|
||||||
|
_dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
|
||||||
|
int get(int index) {
|
||||||
|
if (index > (_size - 1) || index < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LinkedNode* cur = _dummyHead->next;
|
||||||
|
while(index--){ // 如果--index 就会陷入死循环
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return cur->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
|
||||||
|
void addAtHead(int val) {
|
||||||
|
LinkedNode* newNode = new LinkedNode(val);
|
||||||
|
newNode->next = _dummyHead->next;
|
||||||
|
_dummyHead->next = newNode;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在链表最后面添加一个节点
|
||||||
|
void addAtTail(int val) {
|
||||||
|
LinkedNode* newNode = new LinkedNode(val);
|
||||||
|
LinkedNode* cur = _dummyHead;
|
||||||
|
while(cur->next != nullptr){
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
cur->next = newNode;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
|
||||||
|
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
|
||||||
|
// 如果index大于链表的长度,则返回空
|
||||||
|
void addAtIndex(int index, int val) {
|
||||||
|
if (index > _size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LinkedNode* newNode = new LinkedNode(val);
|
||||||
|
LinkedNode* cur = _dummyHead;
|
||||||
|
while(index--) {
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
newNode->next = cur->next;
|
||||||
|
cur->next = newNode;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
|
||||||
|
void deleteAtIndex(int index) {
|
||||||
|
if (index >= _size || index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LinkedNode* cur = _dummyHead;
|
||||||
|
while(index--) {
|
||||||
|
cur = cur ->next;
|
||||||
|
}
|
||||||
|
LinkedNode* tmp = cur->next;
|
||||||
|
cur->next = cur->next->next;
|
||||||
|
delete tmp;
|
||||||
|
_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印链表
|
||||||
|
void printLinkedList() {
|
||||||
|
LinkedNode* cur = _dummyHead;
|
||||||
|
while (cur->next != nullptr) {
|
||||||
|
cout << cur->next->val << " ";
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
int _size;
|
||||||
|
LinkedNode* _dummyHead;
|
||||||
|
|
||||||
|
};
|
||||||
|
```
|