// O(n)
// 思路: 关键是理清楚三个步骤的先后顺序,cur指针一定要指向待交换的两个节点的前一个接点,因此要设置虚拟头节点更好的操作,其次是要保存后面的节点防止断链
// 遍历终止条件: 当cur->next == NULL时结束说明链表节点个数时数。当cur->next->next == NULL时说明链接节点个数是奇数。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode * _dummyHead = new ListNode(0); // 设置虚拟头节点
ListNode *cur = _dummyHead; // 遍历指针
_dummyHead->next = head;
while (cur->next != NULL && cur->next->next != NULL) {
ListNode *tmp = cur->next; // 保存临时节点
ListNode *tmp1 = tmp->next->next;
cur->next = cur->next->next; // 步骤1,断掉虚拟头节点和head节点之间的链
cur->next->next = tmp; // 步骤2
cur->next->next->next = tmp1; // 步骤3
cur = cur->next->next; // cur向后移动2个位置,进行下一轮交换
}
return _dummyHead->next;
}
};
// 双指针法
// O(n)
// 思路: 先让fast指针先走n+1步,因为只有先走n+1步才能同时移动的时候**slow才能指向删除节点的上一个节点(方便做删除操作)**
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode *_dummyHead = new ListNode(0);
_dummyHead->next = head;
ListNode *fast = _dummyHead;
ListNode *slow = _dummyHead;
while (n-- && fast->next != NULL) { // 先让fast先走n步
fast = fast->next;
}
fast = fast->next; // 再多走1步,共n+1步
while (fast != NULL) { // 保证fast和slow同步移动,找到倒数第N个节点
fast = fast->next;
slow = slow->next;
}
// slow->next = slow->next->next;
ListNode *tmp = slow->next; // 释放内存
slow->next = slow->next->next; // 删除倒数第N个节点
delete tmp;
return _dummyHead->next;
}
};
// O(n+m)
// 思路: 求两个链表交点节点的指针,交点不是数值相等,而是指针相等。
// 求两个链表的长度差,让curA始终位最长的哪个链表,
// 利用长度差先让curA移动gap个长度,再让curA与curB同时移动,
// 保证curA移动到,和curB 末尾对齐的位置,然后比较若相同就返回。
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; // 让curA重新回到headA,没有这两句回爆空指针错误
curB = headB;
if (lenB > lenA) { // // 让curA为最长链表的头,lenA为其长度
swap (lenA, lenB);
swap (curA, curB);
}
int gap = lenA - lenB; // 长度差
while (gap--) { // 让curA和curB在同一起点上(末尾位置对齐)
curA = curA->next;
}
while (curA != NULL) { // 遍历curA 和 curB,遇到相同则直接返回
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
// 双指针法
// O(n)
// 思路: 见解析https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html#%E6%80%9D%E8%B7%AF
// 考查两点
// 一、判断链表是否环: 可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
// fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。
// 二、如果有环,如何找到这个环的入口
// **从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。**
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head;
ListNode *slow = head;
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next; // fast每次走两步
slow = slow->next; // slow每次走一步
if (fast == slow) { // 相遇时
ListNode *index1 = fast; // 相遇时重新定义一个指针指向相遇时的节点
ListNode *index2 = head; // 定义一个指针指向头节点
while (index1 != index2) { // x=z,看图
index1 = index1->next; // 让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
index2 = index2->next;
}
return index1;
}
}
return NULL;
}
};