Update
@ -14,8 +14,12 @@ LeetCode 最强题解(持续更新中):
|
||||
|[0035.搜索插入位置](https://github.com/youngyangyang04/leetcode/blob/master/problems/0035.搜索插入位置.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) |数组 |**模拟**|
|
||||
|[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) |数组 | **暴力** **滑动窗口**|
|
||||
|[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) |数组 |**暴力** **字典计数**|
|
||||
|[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/
|
||||
|
||||
## 思路
|
||||
# 思路
|
||||
|
||||
这道题目其实是一道很简单的题,但是为什么通过率相对来说并不高呢,我理解是大家对 边界处理的判断有所失误,导致的。
|
||||
|
||||
@ -22,7 +22,7 @@ https://leetcode-cn.com/problems/search-insert-position/
|
||||
这里我给出了一种简洁的暴力解法,和两种二分查找的解法
|
||||
|
||||
|
||||
## 解法:暴力枚举
|
||||
# 解法:暴力枚举
|
||||
|
||||
```
|
||||
class Solution {
|
||||
@ -49,7 +49,7 @@ public:
|
||||
时间复杂度:O(1)
|
||||
|
||||
|
||||
## 二分法
|
||||
# 二分法
|
||||
|
||||
既然暴力解法的时间复杂度是On,我们就要尝试一下使用二分查找法。
|
||||
|
||||
@ -73,7 +73,7 @@ public:
|
||||
|
||||
我们要在二分查找的过程中,保持不变量,这也就是**循环不变量** (感兴趣的同学可以查一查)
|
||||
|
||||
### 二分法第一种写法
|
||||
## 二分法第一种写法
|
||||
|
||||
以这道题目来举例,以下的代码中我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
|
||||
|
||||
@ -111,7 +111,7 @@ public:
|
||||
效率如下:
|
||||
<img src='../pics/35_搜索插入位置2.png' width=600> </img></div>
|
||||
|
||||
### 二分法第二种写法
|
||||
## 二分法第二种写法
|
||||
|
||||
如果说我们定义 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;
|
||||
|
||||
};
|
||||
```
|