1 两两交换链表中的节点
本题关键在于交换结点之后,原来的线就不存在,无法通过这根线找到下面的节点,具体如下图。
1.1迭代法
/**
* 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) {
if(head == nullptr) return head;
ListNode *dummyNode = new ListNode();
dummyNode->next = head;
ListNode *cur = dummyNode;
//操作指针cur要指向要反转两个节点的前一个节点
//前者是偶数时的条件,后者是奇数时的条件
//并且注意不能写反,否则如果cur->next为空时,先判断cur->next->next,会出现空指针异常
while(cur->next != nullptr && cur->next->next != nullptr){
//图中有说明为什么提前保存
ListNode *temp = cur->next;
ListNode *temp1 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = temp;
cur->next->next->next = temp1;
cur = cur->next->next;
}
return dummyNode->next;
}
};
1.2 递归
class Solution {
public:
//递归:两两交换
ListNode* swapPairs(ListNode* head) {
//终止条件:节点数偶数,刚好两两交换结束||节点数奇数,剩下一个节点,不需要交换
if(head == nullptr || head->next == nullptr){
return head;
}
//单层逻辑
ListNode *newHead = head->next;//本组交换后头节点自然就变为原来头节点的下一个节点
head->next = swapPairs(head->next->next);//将交换后的本组最后一个节点和交换后的下一组连接起来
newHead->next = head;
return newHead;
}
};
2 删除链表的倒数第N个节点
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。此时slow指向的是需要删除的节点。
/**
* 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:
//双指针:快慢指针,快指针先走n步,然后快慢指针一起走,直到快指针走到nullptr处,此时慢指针的位置就是需要删除的节点
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head == nullptr) return head;
ListNode *dummy = new ListNode();//虚拟头节点的存在是为了方便删除头节点
dummy->next = head;
ListNode *slow = dummy;
ListNode *fast = dummy;
int count = n+1;
while(count--){//走n+1步是为了让后续fast走到nullptr少走一步,也就是slow会走到需要删除节点的前一个
fast = fast->next;
}
while(fast){
fast = fast->next;
slow = slow->next;
}
//清理内存
ListNode *temp = slow->next;
slow->next = temp->next;
delete temp;
head = dummy->next;
delete dummy;
return head;
}
};
3 链表相交
/**
* 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) {
if(headA == nullptr || headB == nullptr) return nullptr;
int lenA = 0, lenB = 0;
ListNode *curA = headA;
ListNode *curB = headB;
//记录A的长度
while(curA){
curA = curA->next;
lenA++;
}
lenA += 1;
//记录B的长度
while(curB){
curB = curB->next;
lenB++;
}
lenB += 1;
//还原curA和curB
curA = headA;
curB = headB;
//判断谁大谁小,默认A组长,所以A小就交换两组内容
if(lenA < lenB){
swap(curA,curB);
swap(lenA,lenB);
}
int diff = lenA-lenB;
//lenA先走diff
while(diff--){
curA = curA->next;
}
//寻找交点
while(curA != nullptr){
if(curA == curB) return curA;
curA = curA->next;
curB = curB->next;
}
return nullptr;
}
};
swap()是C++自带函数,可以交换两个容器的内容。
另外,参考swap,这篇博客,自写swap要注意用指针或引用,因为在调用函数传入参数的时候会生成与原参数列表完全相同的临时参数参与函数的执行。并不会改变本身的值。
4 环形链表Ⅱ
快慢指针:关键在于理解快慢指针在圆中相遇之后,从相遇点和头节点同时出发,刚好会在循环入口处相遇。
快指针每次走两步,慢指针每次走一步:快指针相对于慢指针来说,每次走一步,也就是说必会相遇。
/**
* 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) {
if(head == nullptr) return nullptr;
ListNode *slow = head;
ListNode *fast = head;
while(fast != nullptr && fast->next != nullptr){
slow = slow->next;
fast = fast->next->next;
if(slow == fast) {
ListNode *index1 = head;
ListNode *index2 = fast;
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return nullptr;
}
};