代码随想录Day4

781 阅读5分钟

19.删除链表的倒数第N个节点

力扣题目链接

文章讲解

链表遍历学清楚! | LeetCode: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. 两两交换链表中的节点

力扣题目链接

文章讲解

帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点

思路:这题一定要画图来模拟,否则很难理清。

image.png 原文这张图浅显易懂,在自己做这题时候,也要动手画出模拟图,再根据模拟图写代码。

// 画图模拟,一定要画图,否则很难想清楚
    // 时间复杂度: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

力扣题目链接

文章讲解

把环形链表讲清楚!| LeetCode:142.环形链表II

思路:这题体现了数学的美(哈哈),纯结合carl大神的图解和tips可以完美理解本题,这里引用两张图。

141.环形链表.gif

image.png 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;
    }

考到数学就真的只能看积累了,数学困难户难顶哦。