LC 24 两两交换链表中的节点
题目链接:LC 24 两两交换链表中的节点
思路:为了代码更优雅,加上虚拟头节点,但是返回的时候要返回真实的头节点。两个节点之间交换容易,但是要注意交换后两节点与前后节点之间的联系,这也是引入虚拟头节点的原因之一。
具体按随想录中设两个temp,按结果的顺序来传递。这样容易缕清思路。
例如,交换0->1->2->3->4中的2与3:先1->3,再3->2,最后2->4,这样需要temp1保存2的地址(1->3之后就找不到2了),temp2保存4的地址(3->2之后就找不到4了)
也可以只设一个temp,按下面例子的思路。
例如,交换0->1->2->3->4中的2与3:先1->3,再2->4,最后3->2,这样只需要temp1保存2的地址即可。把交换2,3位置改为了两步,第一步删除2,第二步把2放到3后面
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = dummy;
while(cur->next!=nullptr && cur->next->next!=nullptr){//第一种情况是偶数个,第二种情况是奇数个(不包括虚拟头节点)
ListNode* temp = cur->next;
cur->next = temp->next;
temp->next = cur->next->next;
cur->next->next = temp;
cur = temp;
}
return dummy->next;
}
};
LC 19 删除链表的倒数第N个节点
题目链接:LC 19 删除链表的倒数第N个节点
思路:这个题目解决办法看一遍就忘不了了。一个指针r先走k+1步,另一个指针l重头开始,两个指针一起走,直到r到了链表尾,l指向的是倒数第k+1个节点。然后再删除倒数第k个节点。也要设置虚拟头节点,不然不好删除头节点。
两指针走的时候要注意细节
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* r = dummy;
ListNode* l = dummy;
for(int i=0; i<n; ++i){//让r先走n步
r = r->next;
}
while(r->next!=nullptr){//r,l一起走
r = r->next;
l = l->next;
}
ListNode* temp = l->next;
l->next = temp->next;
delete temp;
return dummy->next;//不能是head,因为删除的节点可能是head
}
};
LC 面试题 02.07. 链表相交
题目链接: LC 面试题 02.07. 链表相交
思路:相交的情况一定是从某个节点开始两个链表的节点地址就一样了,直至链表末尾(因为相交的节点地址相同,所以next也一定都相同)。解题思路就是,将两链表的末尾对齐后遍历。先遍历两个链表,得到它们的长度,然后做差,长的先遍历,然后同时遍历直到节点的地址相同。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA = 0;
int lenB = 0;
ListNode* curA = headA;
ListNode* curB = headB;
while(curA!=nullptr){//有可能是空链表,所以即使是长度做差也要遍历到底,不然空链表curA->next超出范围
++lenA;
curA = curA->next;
}
while(curB!=nullptr){
++lenB;
curB = curB->next;
}
int x = lenA-lenB;
curA = headA;
curB = headB;
if(x>=0){
while(x){
curA = curA->next;
--x;
}
while(curA!=nullptr){
if(curA==curB)return curA;
curA = curA->next;
curB = curB->next;
}
}
else{
x = -x;
while(x){
curB = curB->next;
x--;
}
while(curA!=nullptr){
if(curB==curA)return curA;
curA = curA->next;
curB = curB->next;
}
}
return nullptr;
}
};
LC 142 环形链表II
题目链接:LC 142 环形链表II
思路:有两个基本问题。1.链表是否有环? 2.环的入口在哪里?
解决第一个问题:快慢指针法,快指针每步走两个节点,慢指针每步走一个节点,若慢指针到链表尾,两指针仍未相遇,则没有环。分析:当没有环的时候,两个指针不会相遇。当有环的时候,在慢指针到环的入口的时候,快指针已经进入环,假设快指针在环的任意位置,相当于快指针开始追慢指针,之后两指针的距离每步减一,因此两指针定会相遇。
解决第二个问题:假设两个指针已相遇,令两个指针分别从相遇点和起点开始每步走一个节点,相遇点则为入口。推导思路如下,建议自己推一下,不难。
代码:
/**
* 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* f = head;//fast
// ListNode* s = head;//slow
// int flag = 0;//防止只有一个节点且节点没有环的情况
// while(f!=nullptr&&f->next!=nullptr){
// flag = 1;
// s = s->next;
// f = f->next->next;
// if(s==f)break;
// }
// if(flag==0 || s!=f)return nullptr;
// //s为相遇点
// ListNode* h = head;
// while(h!=s){
// h = h->next;
// s = s->next;
// }
// return s;
// }
ListNode *detectCycle(ListNode *head) {
ListNode* f = head;//fast
ListNode* s = head;//slow
while(f!=nullptr&&f->next!=nullptr){
s = s->next;
f = f->next->next;
if(s==f){
f = head;
while(f!=s){
f = f->next;
s = s->next;
}
return s;
}
}
return nullptr;
}
};