diff --git a/README.md b/README.md index 7e32a341..140304e8 100644 --- a/README.md +++ b/README.md @@ -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) |链表 |**模拟**| diff --git a/pics/203_链表删除元素1.png b/pics/203_链表删除元素1.png new file mode 100644 index 00000000..b32aa50e Binary files /dev/null and b/pics/203_链表删除元素1.png differ diff --git a/pics/203_链表删除元素2.png b/pics/203_链表删除元素2.png new file mode 100644 index 00000000..5519a69d Binary files /dev/null and b/pics/203_链表删除元素2.png differ diff --git a/pics/203_链表删除元素3.png b/pics/203_链表删除元素3.png new file mode 100644 index 00000000..cd50ce13 Binary files /dev/null and b/pics/203_链表删除元素3.png differ diff --git a/pics/203_链表删除元素4.png b/pics/203_链表删除元素4.png new file mode 100644 index 00000000..02aaf115 Binary files /dev/null and b/pics/203_链表删除元素4.png differ diff --git a/pics/203_链表删除元素5.png b/pics/203_链表删除元素5.png new file mode 100644 index 00000000..e24ad3d3 Binary files /dev/null and b/pics/203_链表删除元素5.png differ diff --git a/pics/203_链表删除元素6.png b/pics/203_链表删除元素6.png new file mode 100644 index 00000000..13f9b6d6 Binary files /dev/null and b/pics/203_链表删除元素6.png differ diff --git a/pics/206_反转链表.png b/pics/206_反转链表.png new file mode 100644 index 00000000..f26ad08f Binary files /dev/null and b/pics/206_反转链表.png differ diff --git a/problems/0035.搜索插入位置.md b/problems/0035.搜索插入位置.md index 5b8f971b..5cea4c32 100644 --- a/problems/0035.搜索插入位置.md +++ b/problems/0035.搜索插入位置.md @@ -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: 效率如下: -### 二分法第二种写法 +## 二分法第二种写法 如果说我们定义 target 是在一个在左闭右开的区间里,也就是[left, right) diff --git a/problems/0083.删除排序链表中的重复元素.md b/problems/0083.删除排序链表中的重复元素.md new file mode 100644 index 00000000..39c50b94 --- /dev/null +++ b/problems/0083.删除排序链表中的重复元素.md @@ -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; + } +}; +``` + diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md new file mode 100644 index 00000000..69c41eec --- /dev/null +++ b/problems/0142.环形链表II.md @@ -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; + } +}; +``` diff --git a/problems/0203.移除链表元素.md b/problems/0203.移除链表元素.md new file mode 100644 index 00000000..3aef4d2e --- /dev/null +++ b/problems/0203.移除链表元素.md @@ -0,0 +1,130 @@ +## 题目地址 + +https://leetcode-cn.com/problems/remove-linked-list-elements/ + +## 思路 + +我们这里以链表 1 4 2 4 来举例,移除元素4 + + + +如果使用C,C++编程语言的话,不要忘了还要从内存中删除这两个移除的节点, 清理节点内存之后如图: + + + +**当然如果使用java ,python的话就不用手动管理内存了。** + +还要说明一下,就算我们使用C++来做leetcode,如果移除一个节点之后,没有手动在内存中删除这个节点,leetcode依然也是可以通过的,只不过,内存使用的空间大一些而已,但建议同学们依然要养生手动清理内存的习惯,因为在面试中 这些细节都是面试官考察的点 + +这种情况下的移除操作,就是让节点next指针直接指向下下一个节点就可以了, + +那么因为单链表的特殊性,只能指向下一个节点,我们刚刚删除的是 链表的中第二个,和第四个节点,那么如果我们删除的是头结点 又该怎么办呢 + +这里就涉及到 链表操作的两种方式,**一种是 直接使用原来的链表来进行删除操作,一种是设置一个虚拟头结点在进行删除操作。** + + + +我们来看第一种操作 直接使用原来的链表来进行移除 + + + +移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点 + +所以头结点如何移除呢,其实我们只要 将头结点向后移动一位就可以,这样我们就从链表中移除了一个头结点 + + + + +依然别忘将原头结点从内存中删掉 + + + +这样我们移除了一个头结点,是不是发现,在单链表中移除头结点 和 移除其他节点的操作方式是不一样,其实在我们写代码的时候也会发现,需要单独写一段逻辑来处理 移除头结点的情况。 + +那么可不可以 以一种统一的逻辑来移除 链表的节点呢。 + +其实我们可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。 + +我们来看看如何设置一个虚拟头。我们依然还是在这个链表中,移除元素1。 + + + + +这里我们来给链表添加一个虚拟头结点为新的头结点,此时我们要移除这个旧头结点 元素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; + } +}; +``` diff --git a/problems/0206.翻转链表.md b/problems/0206.翻转链表.md new file mode 100644 index 00000000..5746ae3b --- /dev/null +++ b/problems/0206.翻转链表.md @@ -0,0 +1,56 @@ +## 题目地址 + +https://leetcode-cn.com/problems/reverse-linked-list/ + +## 思路 + +其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表,如图所示 + + + +## 代码 + +### 模拟算法 +``` +/** + * 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); + } + +}; +``` diff --git a/problems/0707.设计链表.md b/problems/0707.设计链表.md new file mode 100644 index 00000000..4e23bb18 --- /dev/null +++ b/problems/0707.设计链表.md @@ -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; + +}; +```