(以下参考来自代码随想录训练营资料)
24. 两两交换链表中的节点
![](https://i-blog.csdnimg.cn/blog_migrate/653ac005ca4b1f70901ceedf587d3855.jpeg)
使用虚拟头节点,可避免头节点head的单独处理,总体思路:
![](https://i-blog.csdnimg.cn/blog_migrate/0ae76d1227bf8903c2615e16516f6a98.png)
python代码:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
res = ListNode(next=head) ###设置虚拟头节点
pre = res
# 必须有pre的下一个和下下个才能交换,否则说明已经交换结束了
while pre.next and pre.next.next:
cur = pre.next ##首先把pre cur post设置好。相当于temp临时变量的作用
post = pre.next.next
# pre,cur,post对应最左,中间的,最右边的节点,python用的是从后往前,即先步骤三再步骤二
cur.next = post.next ##步骤三
post.next = cur##步骤二
pre.next = post##步骤一
pre = pre.next.next #pre移动两位准备下一轮替换
return res.next
C++代码:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
return dummyHead->next;
}
};
19.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:
![](https://i-blog.csdnimg.cn/blog_migrate/7e55a0b15489af8fac946793d047697f.png)
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 示例 2:
输入:head = [1], n = 1 输出:[] 示例 3:
输入:head = [1,2], n = 1 输出:[1]
分析:
方法一:最容易想到的,计算链表长度。
我们首先从头节点开始对链表进行一次遍历,得到链表的长度 L。随后我们再从头节点开始对链表进行一次遍历,当遍历到第 L-n+1L−n+1 个节点时,它就是我们需要删除的节点。但此法耗费内存空间,需要单独开辟一个空间用来存放遍历的结果。
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
def getLength(head: ListNode) -> int:
length = 0
while head:
length += 1
head = head.next
return length
dummy = ListNode(0, head)
length = getLength(head)
cur = dummy
for i in range(1, length - n + 1):
cur = cur.next
cur.next = cur.next.next
return dummy.next
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solution/shan-chu-lian-biao-de-dao-shu-di-nge-jie-dian-b-61/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法二:定义fast和slow双指针
![](https://i-blog.csdnimg.cn/blog_migrate/a04f29454fa8d1dd8d34fa696bfc4a03.png)
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作)注意:做链表的增删操作,最主要是找到前面那个节点,如图:
![](https://i-blog.csdnimg.cn/blog_migrate/0624752a60da881ccf76955c1f706dd0.png)
fast和slow同时移动,直到fast指向末尾,如题:
![](https://i-blog.csdnimg.cn/blog_migrate/c6ea38a4dbe71ae4bb4cac022ee25ee4.png)
删除slow指向的下一个节点,如图:
![](https://i-blog.csdnimg.cn/blog_migrate/4d134a19ce292eba65681d372be7026f.png)
python代码:
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
head_dummy = ListNode()
head_dummy.next = head
slow, fast = head_dummy, head_dummy
while(n!=0): #fast先往前走n步
fast = fast.next
n -= 1
while(fast.next!=None):
slow = slow.next
fast = fast.next
#fast 走到结尾后,slow的下一个节点为倒数第N个节点
slow.next = slow.next.next #删除
return head_dummy.next
C++代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
// ListNode *tmp = slow->next; C++释放内存的逻辑
// slow->next = tmp->next;
// delete nth;
return dummyHead->next;
}
};
面试题 02.07. 链表相交
同:160.链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
![](https://i-blog.csdnimg.cn/blog_migrate/3a10766e7589f50288311f8e2aa1a31a.png)
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
![](https://i-blog.csdnimg.cn/blog_migrate/f1b0dd17b9199183feb29dc4b6cc3e9c.png)
示例 2:
![](https://i-blog.csdnimg.cn/blog_migrate/5f05b7f0c39db550944e387921248048.png)
示例 3:
![](https://i-blog.csdnimg.cn/blog_migrate/788883dfc68de1300770009e13f89f45.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8f8de1e2ff4f66bba5c678c7e21bf794.png)
分析:就是求两个链表交点节点的指针,但注意交点不是数值相等,而是指针相等。
看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
![](https://i-blog.csdnimg.cn/blog_migrate/b67b4dab6f87f4c567c6517dbcff5431.png)
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
![](https://i-blog.csdnimg.cn/blog_migrate/3eef62804dd1196e66e6d514f9d98107.png)
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
否则循环退出返回空指针。
python:
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头,lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上(末尾位置对齐)
curB = curB.next
while curA: # 遍历curA 和 curB,遇到相同则直接返回
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
C++:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
环形链表II
(这题不容易,多想想)
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
![](https://i-blog.csdnimg.cn/blog_migrate/a86f508036b3bb64a00956b8d91be50d.png)
分析:
判断是否有环,用快慢指针(注:这类题一般用快慢指针法,例如寻找距离尾部第 K 个节点、寻找环入口、寻找公共尾部入口等。);从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。因为如果有最小的环,是相差两步的。
![](https://i-blog.csdnimg.cn/blog_migrate/5c333f40345c123f972f864f5d9a3fe9.gif)
2.寻找入环的第一个节点,即找环的入口:
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/f9d5988f405a2dd9a40d81f1ef210115.png)
那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y): x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以要求x ,将x单独放在左面:x = n (y + z) - y ,
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x-z = (n - 1) (y + z) 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针,。
结论:在head定义index1,在相遇点定义index2,俩指针同时出发,step为1,index1和index2相遇点即为入口点
python:
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 如果相遇
if slow == fast:
p = head
q = slow
while p!=q:
p = p.next
q = q.next
#你也可以return q
return p
return None
C++:
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;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};