第二章 链表part02
昨日由于写的太晚没有上传,对于递归仍然不能独立写出,需要多加总结练习
LeetCode 24.两两交换链表中的节点
题目链接:24. 两两交换链表中的节点
思路:
-
直接三指针迭代交换法,时间复杂度最优:交换节点位置,很直观看出每一轮的操作就是(以1,2为例子,假设前面有dummy_Hand)
p->next=q->next; t->next=q; q->next=p; //t后移两位,p后移一位,q后移三位,继续操作,直到q为nullptr //不为都后移两位的原因是,p,q位置是经过交换的,交换后q在p前,下一轮则q后移三位,p后移一位
-
递归法,栈的思路,一轮操作如下图:
完整C++代码如下:
//1
//时间复杂度:O(n)
//空间复杂度:O(1)
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==nullptr||head->next==nullptr)
return head;
ListNode *t = new ListNode();
t->next = head;
ListNode *p = head;
ListNode *q = head->next;
head = head->next;
while(q){
p->next = q->next;
t->next = q;
q->next = p;
//p->next不过,p->next->next==nullptr判断直接跳过
if (p->next==nullptr||p->next->next==nullptr)
return head;
p = p->next;
q = q->next->next->next;
t = t->next->next;
}
return head;
}
};
//2
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==nullptr||head->next==nullptr)
return head;
ListNode *newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}
};
LeetCode 19.删除链表的倒数第N个节点
题目链接:19. 删除链表的倒数第 N 个结点
思路:
本题其实并不难做,但怎么快速的一遍定位到倒数第N个节点,这需要天才思维之:快慢指针,通过一点数学原理,一次就能定位到目标位置
- 由于删除可能为任意一个节点(包括第一个节点),所以设置虚拟头结点dummy_Head
- 设置s,q快慢指针指向dummy_Head位置,先让快指针q先走n+1步
- 再让s,q同时走,结束条件是q指到nullptr
- 此时s所指位置下一个位置就是要删除的结点
解释:
设有t个节点,倒数第n个节点就是正数第t-n+1个节点,因为是要删除节点,所以要走到t-n个节点即从dummy_Head走t-n步就是目标。
q先走n+1步,到了第n+1个节点位置上,当走到nullptr需要再走t-n步,而前面说了s走t-n步恰好就是位于要删除节点的上一个位置(长度互补)。
完整C++代码如下:
//时间复杂度: O(n)
//空间复杂度: O(1)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy_Head=new ListNode();
dummy_Head->next=head;
ListNode*q=dummy_Head,*s=dummy_Head;//连续定义指针注意*s
n++;//走n+1步,先提前让n+1
while(n--){
q=q->next;
}
while(q){
q=q->next;
s=s->next;
}
ListNode* temp=s->next;
s->next=temp->next;
delete temp;
return dummy_Head->next;
}
};
注:ListNode*
是一个指针类型说明符,ListNode *
q=dummy_Head, *
s=dummy_Head,其中s前面有 *
, *
表示 q
和 s
都是指针类型。
LeetCode 面试题 02.07. 链表相交
题目链接:面试题 02.07. 链表相交
思路:
-
哈希表,把一条路径上的所有节点地址存储了,之后另一条路径上逐个对照直到找到相交节点
-
双指针遍历法,运用数学思维,某种程度上来说本题解和上一道题的题解有异曲同工之处,仍然是(总长相等,内部两个部分互补的本质)
借用Krahets老哥的图来描述:
如图,我们可以在headA和headB处分别建立指针A,B
- 头节点
headA
到node
前,共有a−c
个节点; - 头节点
headB
到node
前,共有b−c
个节点; - 这里可以发现A从头走到尾(null)走了a,再将A从尾移到headB开始走到首个公共节点走了b-c,一共走了a+b-c
- A从头走到尾(null)走了b,再将A从尾移到headB开始走到首个公共节点走了a-c,一共走了a+b-c
- 原因在于长度互补,类似于我画的图这段橘色的线段长度是恒定的
- 头节点
完整C++代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode *>set;
while(headA){
set.insert(headA);
headA=headA->next;
}
while(headB){
if(set.find(headB)!=set.end()){
return headB;
}
headB=headB->next;
}
return nullptr;
}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(!headA||!headB)return NULL;
ListNode *A=headA;
ListNode *B=headB;
while(A!=B){
if(!A){
A=headB;//也算作一次机会,等于是说明A已经经过headA节点
}else{
A=A->next;
}
if(!B){
B=headA;
}else{
B=B->next;
}
}
return B;
}
};
LeetCode 142.环形链表 II
题目链接:142. 环形链表 II
思路:数学思维
-
数学思维双指针法,建议特别收录至数学思维题库
- 设置快指针fast ,慢指针slow在头节点,快指针每次走两步,慢指针每次走一步,如果是无环单链表,slow永远追不上quick,但当有环的时候,我们发现,只要slow也进了圈,quick就相当于以速度1(相对)追slow,最后某一时刻一定会追上。
- 此处需要用到数学思考:
假设从头结点到环形入口节点 的位移数为x。 环形入口节点到 fast指针与slow指针相遇节点 位移数为y。 从相遇节点 再到环形入口节点位移数为 z。
相遇时: slow指针走过的节点数为:
x + y
, fast指针走过的节点数:x + y + n (y + z)
又已经知道: fast指针走过的节点数 = slow指针走过的节点数 * 2
即
(x + y) * 2 = x + y + n (y + z)
->x + y = n (y + z)
又由于我们要求x,可以将式子转化为:
x = (n - 1) (y + z) + z
至此,真相大白,y+z是整个环形圈,并不重要,我们可以知道的是当两个指针分别从头节点和相遇节点开始位移,终有时刻会相遇,而相遇时的位置即为入环的第一个节点。
-
哈希,和前面的题目哈希思路基本一致,较为简单,不再写
完整C++代码如下:
//1
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) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};