24. 两两交换链表中的节点
题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/
答案链接:代码随想录
思路:
思路很简单,遍历链表,更换两两元素的位置。难点在于每次换位置的实现。
解答:
感悟:
(1)firstnode不可以移动。在第一遍做的时候,想到了用虚拟节点指向head,但是在遍历的时候,用虚拟节点firstnode进行移动,也就是变动了firstnode。这相当于链表的头一直在变。正确的做法是另声明一个变量cur,让cur = firstnode,之后移动cur
(2)步骤23错位。之前我的想法是先连cur-2,之后连1-3,最后连2-1。但是这个想法很不规范,可以先画出图来。
代码:
ListNode* swapPairs(ListNode* head) {
ListNode * firstnode = new ListNode();
firstnode->next = head;
ListNode * tempnode = new ListNode();
ListNode * lastnode = new ListNode();
ListNode * cur = firstnode;
while(cur->next!=nullptr && cur->next->next!=nullptr){
lastnode = cur->next->next->next;
tempnode = cur->next;
cur->next = cur->next->next;
cur->next->next = tempnode;
tempnode->next = lastnode;
cur = tempnode;
}
return firstnode->next;
}
19. 删除链表倒数第N个节点
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
题目解答:代码随想录
思路:
因为链表无法索引,所以我利用两次遍历的方式解决这个问题。第一次得到链表长度,第二次删除指定节点。
解答:
双指针。如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
感悟:
(1)如果创建虚拟节点dummyhead,返回head其实为return dummyhead->next。因为如果head发生变动,return head就没有意义了。而dummyhead是不会变的。
(2)可以利用双指针的方式,实现差额补位。(链表中最后一个节点不知道,但是双指针中快慢指针的差异可以弥补)
代码:
(1)二次搜索
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *cur = head;
int len = 0;
while(cur!=nullptr){
cur=cur->next;
len+=1;
}
ListNode *dummyfirst = new ListNode();
dummyfirst->next = head;
int need_move = len - n;
cur = dummyfirst;
for(int i = 0; i<need_move; i++){
cur = cur->next;
}
//ListNode *del = cur->next;
cur->next = cur->next->next;
//delete del;
return dummyfirst->next;
}
(2)双指针
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *dummyhead = new ListNode();
dummyhead->next = head;
ListNode *fast = dummyhead;
ListNode *slow = dummyhead;
for(int i=0; i<n; i++){
fast = fast->next;
}
while(fast->next!=nullptr){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyhead->next;
}
面试题02.07.链表相交
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
答案链接:代码随想录
思路:
这道题的难点还是在于链表长度不已知。因此,同样可以利用双指针法,每个链表一对双指针,先通过快指针让链表对齐,之后通过慢指针找到合并的位置。
这道题代码随想录中给的是二次遍历的方法,时间复杂度比我的高,空间复杂度比我的低。
代码:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *fast1 = headA;
ListNode *slow1 = headA;
ListNode *fast2 = headB;
ListNode *slow2 = headB;
while(slow1 != nullptr && slow2 != nullptr){
if (slow1 == slow2){
return slow1;
}
if (fast1 == nullptr && fast2 != nullptr){
fast2 = fast2->next;
slow2 = slow2->next;
}else if (fast2 == nullptr && fast1 != nullptr){
fast1 = fast1->next;
slow1 = slow1->next;
}else if (fast1 == nullptr && fast2 == nullptr){
slow1 = slow1->next;
slow2 = slow2->next;
}else{
fast1 = fast1->next;
fast2 = fast2->next;
}
}
return NULL;
}
142. 环形链表2
思路:
这道题的难点在于不知道如何判断是否进入循环,比较困难。
方法:
设计快慢指针。快指针追上慢指针,说明存在循环。之后通过数学计算得到循环口位置:
当快指针追上慢指针的时候:
(快指针走过:x+y+n*(y+z)) =2 * (慢指针走过:(x+y))
n*(y+z) = x + y
x = n*y - y + n*z
x = (n-1)*(y+z) + z
也就是说,从head发出一个指针1,从相遇点发出一个指针2,指针2会转n-1圈,之后走z与指针1相遇,这个时候,相遇点正好是环入口。
感悟:
(1)无限循环问题,可以用速度差进行识别
代码:
ListNode *detectCycle(ListNode *head) {
ListNode *slowindex = head;
ListNode *fastindex = head;
ListNode *newindex = head;
while(fastindex != nullptr && fastindex->next != nullptr){
slowindex = slowindex->next;
fastindex = fastindex->next->next;
if(slowindex == fastindex){
while(newindex != slowindex){
newindex = newindex->next;
slowindex = slowindex->next;
}
return slowindex;
}
}
return NULL;
}