19.删除链表的倒数第N个节点
思路:凸显一个逆向思维+快慢指针,不能从后往前遍历,那么我快慢指针从前往后遍历,两者差距n位也能找到倒数第N个节点,注意的是需要找到该节点的前一个节点,以便进行删除操作。
// 想要遍历一遍,使用快慢指针法,快指针先走n+1步(为了找到倒数第n个节点的上一个节点,以便进行删除操作)
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 创建虚拟头结点
ListNode* dummyHead = new ListNode();
dummyHead->next = head;
// 定义快慢指针
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
int m = n + 1;
// 快指针先走n+1步
while (m-- && fast != NULL) {
fast = fast->next;
}
// 快慢指针同步前进
while (fast != NULL) {
slow = slow->next;
fast = fast->next;
}
// 慢指针到达待删除的节点的前一个节点,删除并清理内存
ListNode* tmp = slow->next;
slow->next = slow->next->next;
delete tmp;
tmp = nullptr;
return dummyHead->next;
}
};
24. 两两交换链表中的节点
思路:这题一定要画图来模拟,否则很难理清。
原文这张图浅显易懂,在自己做这题时候,也要动手画出模拟图,再根据模拟图写代码。
// 画图模拟,一定要画图,否则很难想清楚
// 时间复杂度:O(n)
// 空间复杂度:O(1)
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode();
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur->next != nullptr && cur->next->next != nullptr) {
// 指针指向记录点,tmp1记录节点1,tmp2记录节点3
ListNode* tmp1 = cur->next;
ListNode* tmp2 = cur->next->next->next;
// 步骤一
cur->next = cur->next->next;
// 步骤二
cur->next->next = tmp1;
// 步骤三
cur->next->next->next = tmp2;
// cur移动两位
cur = cur->next->next;
}
// return head; error head被交换了顺序
return dummyHead->next;
}
面试题 02.07. 链表相交
思路:这题我并没有采用原文的解法,而是用的力扣评论区的ID为宝石的大神的交替双指针法,简约美观,解答思路放到了代码注释中。
//设交集链表长c,链表1除交集的长度为a,链表2除交集的长度为b,有
//a + c + b = b + c + a 在AB链表交替寻找交集,解决长短不一问题
//若无交集,则a + b = b + a 没有交集跑到a+b时两个指针的next都是null
//时间复杂度:<=O(n+m)
//空间复杂度:O(1)
ListNode *getIntersectionNode1(ListNode *headA, ListNode *headB) {
ListNode* a = headA;
ListNode* b = headB;
while (a!=b) {
a = !a ? headB : a->next;
b = !b ? headA : b->next;
}
return a;
}
此处引用力扣评论方便食用
夜瞳L1
来自江苏2021-09-16
@King 从a开始遍历,跑完会跑到b遍历, 这时候就是答主说的a + b,b在跑完又回到a,一直跑到相交点就是a + b + c的过程,从b开始也是如此,就是b + a + c,如果有相交点这样循环下去,肯定会有个相交的点,如果没有的话,就是a + b, b + a的最后一个点的next(null相等),太巧妙了 HaerWangL1
来自江苏2021-04-28
@KC. Tieing 假设相交(相交点8->8):headA(1->2->3->8->8): 3+2;headB(7->6->8->8): 2+2。则 h1走3+2+2+2 = 9步(每一步访问:1->2->3->8->8->7->6->8->8); h2走2+2+2+3 = 9步(每一步访问:7->6->8->8->1->2->3->8->8)。 看最后两步。 不相交最终满足 h1 == h2 == null
carl的解法
ListNode *getIntersectionNode2(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;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
142.环形链表II
思路:这题体现了数学的美(哈哈),纯结合carl大神的图解和tips可以完美理解本题,这里引用两张图。
slow指针走过的节点数为:
x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
最终公式化为x = (n - 1) (y + z) + z
当n=1时,x=z,意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。 n>1时,也能得出同样结论。
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;
}
考到数学就真的只能看积累了,数学困难户难顶哦。