This commit is contained in:
youngyangyang04
2020-06-21 17:26:25 +08:00
parent 2ad7ce6cb0
commit 0e7c60bdc7
14 changed files with 391 additions and 7 deletions

View File

@ -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) |链表 |**模拟**|

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
pics/206_反转链表.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -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)

View 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;
}
};
```

View 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;
}
};
```

View 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>
如果使用CC++编程语言的话,不要忘了还要从内存中删除这两个移除的节点, 清理节点内存之后如图:
<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;
}
};
```

View 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);
}
};
```

View 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;
};
```