# 暑期LeetCode打卡——Week1，链表

Leetcode 专栏收录该内容
13 篇文章 0 订阅

147Insertion Sort Listhttps://leetcode.com/problems/insertion-sort-list/
138Copy List with Random Pointerhttps://leetcode.com/problems/copy-list-with-random-pointer/
86Partition Listhttps://leetcode.com/problems/partition-list/
30Substring with Concatenation of All Wordshttps://leetcode.com/problems/substring-with-concatenation-of-all-words/
24Swap Nodes in Pairshttps://leetcode.com/problems/swap-nodes-in-pairs/
19Remove Nth Node From End of Listhttps://leetcode.com/problems/remove-nth-node-from-end-of-list/
83Remove Duplicates from Sorted Listhttps://leetcode.com/problems/remove-duplicates-from-sorted-list/
206Reverse Linked Listhttps://leetcode.com/problems/reverse-linked-list/
92Reverse Linked List IIhttps://leetcode.com/problems/reverse-linked-list-ii/
61Rotate Listhttps://leetcode.com/problems/rotate-list/
143Reorder Listhttps://leetcode.com/problems/reorder-list/
141Linked List Cyclehttps://leetcode.com/problems/linked-list-cycle/
160Intersection of Two Linked Listshttps://leetcode.com/problems/intersection-of-two-linked-lists/
142Linked List Cycle IIhttps://leetcode.com/problems/linked-list-cycle-ii/
109Convert Sorted List to Binary Search Treehttps://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/
82Remove Duplicates from Sorted List IIhttps://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/
148Sort Listhttps://leetcode.com/problems/sort-list/
234Palindrome Linked Listhttps://leetcode.com/problems/palindrome-linked-list/
237Delete Node in a Linked Listhttps://leetcode.com/problems/delete-node-in-a-linked-list/
328Odd Even Linked Listhttps://leetcode.com/problems/odd-even-linked-list/

## 解题心得

No.147 对链表插入排序 （AC）（划重点）

• 本题的思路是：从前往后，找到第一个比当前结点大的结点，然后将当前结点插入到它的前面。
• 本比很容易写出Bug. 第一个需要注意的点是，按照自己的惯常思路和想法，容易出现尾指针没有指向空的问题。 按照yxc的思路写时， 需要特别注意，新建立的虚拟头结点不指向head指针，否者也是Bug,这个可以举一个例子画画图理解。

No.138 复杂链表的赋值 （AC）

• step1: 在链表每个节点后插入一个它的新建赋值结点；
• step2: 给新插入的结点赋值random指针的值
• step3: 从修改链表中把赋值的链表拎出来。并将原链表结构还原。还原原链表非常关键，否则就不是deep copy了，之前程序不能ac就是这个原因。
• 还有一点是，要看结点的构造函数，在本题中，初始化时，next指针和random指针也需要给定值，否则会报指针错误。

No.86 将链表按照指定规则调整顺序 （AC）

• 插入位置可能是头结点之前，所以需要建立一个虚拟头结点；
• 先找到第一个值比target小的结点p，作为插入结点的头，然后新建一个q结点从p之后遍历，并记录他的前驱结点和后继结点。当q结点的值小于target时，将q结点插入到p之后，为了保持位置关系，p结点后移。同时q结点这边的连接关系也需要更新。

No.30 Substring with Concatenation of All Words （AC）

• 枚举的方法，时间复杂度为O(n* len)，循环n次，每次只要判断word_num * word_len的长度即可。

No.24. 成对交换前后两个结点对 （AC）

• 需要注意的是，pre->1->2->3中，把1指向3， pre指向2之后，好需要将2指向1，不然中间就断了。

No.19 移除链表中的倒数第n和结点 (AC)

• 链表问题通常存在边界问题，如头结点会被删除，解决这个问题通常是定义一个虚拟头结点，指向真正的头结点。
• 在本题中，用双指针找到倒数第n个结点，然后删除。整个过程只要扫描一遍。
• 还有一个边界是删除倒数第0个结点，即不删除，这种情况直接返回即可。

No.83 删除排序链表中的重复元素 (AC)

• 从前往后遍历，若前后两个元素相等，则删除后者，否者继续往后扫描。

No.206 翻转链表 (AC)

• 需要记录一个前驱结点和一个后继结点。
• 最终前驱结点即为翻转链表的新结点。

No.92 翻转链表中指定段 (AC)

• 头结点也有可能被翻转，因此需要定义一个虚拟头结点。
• 先定位要翻转的段，翻转完成之后，再链接到原链表中。
• 最好不要用pre = pre->next; 去更新pre结点，因为链接顺序改变之后，直接这样做很可能会指向其他结点。

No.61 循环数组 (AC)

• 这道题有两种做法。
• 第一种方法是单个往前移，需要用一个deque队列存放链表中的元素。
• 由于k可能比链表长度大，所以被弹出的尾结点在处理完之后再添加到deque队列的对手。
• 有几个边界特判条件：当链表为空，或链表长度为1，或k为0时都可以直接返回。
• 第二种方法是整段往前移。这里一定要处理的是k大于链表长度的情况。k对链表长度取模，若为0，则直接返回，否则，移到链表前面。
• 这两种方法中头结点都可能会被翻转，所以在表头增加一个虚拟结点。

No.143 数字重排序 (AC) 划重点！

• 本题可以用一个栈辅助操作，需要额外O(n)的空间
• 更好的方法是，找到中点，将后半部分翻转，然后与前半部分合并。
• 几个实现细节：1. 将前后两小段的尾结点指向空，方便后边合并。2.合并时容易写错。
• 假设两个待合并链表为pNode和pPre,

若按照先1后2的方式合并（！错误！），代码如下：
for (int i = num + 1 >> 1; i < num; i++){
ListNode* pNext = pNode->next;
pNode->next = pPre;
pPre = pPre->next;
pPre->next = pNext; // ERROR! 此时pPre指向的位置已经别改变了
pNode = pNext;
}


for (int i = num + 1 >> 1; i < num; i++){
ListNode* t = pPre->next;
pPre->next = pNode->next;
pNode->next = pPre;
pNode = pNode->next->next;
pPre = t;
}


No.141 判断链表是否有环 （AC）（易错题）

• 快慢指针来做
• 本题很容易写出bug,有两个地方值得注意一下：
bool hasCycle(ListNode *head) {
if (!head) return false;
ListNode* fast = head->next;
ListNode* slow = head;
while(fast != slow){
/*
// bug写法
// 若只有一个元素，那么指针始终无法后移，造成TLE
// 因此可以将快慢指针分开来后移，这样可以保证慢指针能往后移
if (fast && fast->next && slow) { // 不加fast,若只有一个元素，会溢出
fast = fast->next->next;
slow = slow->next;
}
*/
if (fast && fast->next ) { // 若只有一个元素，会溢出
fast = fast->next->next;
}
if (slow) slow = slow->next;
if(!fast || !slow) return false;
}
return !fast->next ? false : true; // 需要判断是否下一个指针为结尾
// 反例： 1,2  -1 这种情况下快慢指针仍可以相等，但并没有形成环，因此需要判断相遇位置是否是尾结点的前一个位置
}


NO.160 找两个链表的公共结点 (AC) (易错题)

• 基本思路：双指针把来哥哥链表走两遍，相遇的位置即为交点。
• 特判边界：1. 两个链表没有交点。这种情况下什么时候跳出循环以及返回什么值需要考虑一下，容易写出bug,详见代码。 2. 两个链表本身就是同一个链表，即从第一个元素起就相同，这种情况需要特判，否则会返回nullptr。

NO.142 寻找循环链表的环入口 （AC）

• Step1: 快慢指针找到换种的相遇点
• Step2: 计算环的大小k
• Step3: 双指针前后相隔k步出发，相遇点即为环的入口。
• 需要特判的情况： fast->next-next是否存在；环是否存在。若环不存在，则fast || fast->next || slow中一定有个指针会到达nullptr结点，此时直接返回nullptr即可。

No.109 将排序链表转换成平衡搜索二叉树 （AC）

• 思路类似于根据中序和先序重构二叉树，用递归的方法来做。
• 在写的时候需要注意满足搜索二叉树的需求，这里很巧，正常写就可以满足。

No.82 将链表中重复的数字全部删除 （AC）

• 需要记录一个前驱结点和一个cur结点，关键两行代码如下：
while(q){
while(q && p->next->val == q->val)
q = q->next; // 是判断结点的值相等，不是结点相等，因为结点还有next指针
if (p->next->next == q) p = p->next; // 此时是需要指向相同的结点
else p->next = q;
}


No. 148 链表排序（归并）（AC）（划重点）

• 解题思路是： 先对最小的区间进行排序，然后将两个排序子区间合并；然后再向上递归处理。
• 需要注意的是，每个排好序的小区间需要断开，即末尾要指向nullptr,方便下一步的链表合并。
• 在将一个小区间划分成两个部分时，可以用快慢指针快速实现。
• 在链表合并时，最后剩余的而一条链的判断语句是if,不是while，这里写错几次了。

No.234 判断一个链表是否是回文链表 （AC）

• BUG思路：将整个链表翻转，然后比较翻转前后的链表是否相同。这种做法存在的问题是， 完成翻转之后，原链表的头指针已经不在是原链表的头指针，即只能维持一条链表的完整，不能同时保证翻转前后的链表完整。
• 改进思路： 将链表后半部分翻转，翻转后比较前后时候是完全相同的。在比较的时候只要处理n/2长度的，这样不管是奇偶都可以解决。

No.237 删除链表中的某个结点 （AC）

• 当前删除结点的下一个结点为尾结点，则直接将当前结点赋值为nullptr;
• 将要删除的结点的下一个结点的值赋给当前结点，然后将当前结点的下一个结点删除。

No.328 将原链表中奇数位置的结点放在一起，后面接偶数位置的结点 （AC）

• 新建两个表头，分别是奇数位置结点和偶数位置结点的表头，然后将原链表中相应结点临界点两个头结点后边，再把偶数头结点链到奇数链表的结尾。特别需要注意的是，偶数链表的为节点需要指向nullptr。否则无法打印出结果，显示TLE。
• 0
点赞
• 0
评论
• 0
收藏
• 打赏
• 扫一扫，分享海报

01-15
01-15

01-15
01-15
01-15
01-15
01-15
©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客

MaggieYue

¥2 ¥4 ¥6 ¥10 ¥20

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。