【代码随想录算法训练营11期】Day 4 | C++

24. 两两交换链表中的节点

解题思路:

定义虚拟头结点辅助节点交换,如下图所示:

/**
 * 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 == NULL || (head && head -> next == NULL)) return head; // 特判
        ListNode *dummyHead = new ListNode(0); // 定义虚拟头结点
        dummyHead -> next = head; 
        ListNode *cur = dummyHead; // 定义“当前”节点
        while (cur -> next && cur -> next -> next) {
            ListNode *tmp = cur -> next;
            ListNode *tmp1 = cur -> next -> next -> next;
            cur -> next = cur -> next -> next;  // 步骤1
            cur -> next -> next = tmp;          // 步骤2
            tmp -> next = tmp1;                 // 步骤3
            cur = cur -> next -> next;          // 移动两轮,为下次交换做准备
        }
        return dummyHead -> next;               // 返回头结点
    }
};

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

解题思路:

1. while 循环遍历所有节点确定链表节点数量;

2. 找到待删除节点的上一位节点,删除节点。

/**
 * 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 *dummyHead = new ListNode(0); // 定义虚拟头节点
        dummyHead -> next = head;
        ListNode * cur = dummyHead; // 定义当前节点
        int size = 0;
        // 遍历所有节点明确节点数量
        while (cur -> next) { 
            cur = cur -> next;
            size ++;
        }
        cur = dummyHead; // 重置当前节点
        int cnt = size - n; // 找到待删除节点的上一位节点
        while (cnt --) {
            cur = cur -> next;
        }
        // 删除节点
        ListNode *tmp = cur -> next;
        cur -> next = tmp -> next;
        delete tmp;
        return dummyHead -> next;
    }
};

 面试题 02.07. 链表相交

解题思路:

1.分别遍历两个链表 A 和 B ,找到他两的长度之差;

2.哪边更长就让哪边从这个差值点之后开始遍历,确保二者遍历时相对长度一致。

/**
 * 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) {
        ListNode *dummyHeadA = new ListNode(0); // 定义虚拟头节点
        dummyHeadA -> next = headA;
        ListNode *cur1 = dummyHeadA;            // 定义指向当前节点的指针
        int sizeA = 0;

        ListNode *dummyHeadB = new ListNode(0); // 定义虚拟头节点
        dummyHeadB -> next = headB;
        ListNode *cur2 = dummyHeadB;            // 定义指向当前节点的指针
        int sizeB = 0;
        // 求链表A长度
        while (cur1 -> next) {
            cur1 = cur1 -> next;
            sizeA ++;
        }
        // 求链表B长度
        while (cur2 -> next) {
            cur2 = cur2 -> next;
            sizeB ++;
        }   
        // 重置链表A和B起始点位
        cur1 = dummyHeadA;
        cur2 = dummyHeadB;
        if (sizeA >= sizeB) {
            int cnt = sizeA - sizeB;
            // A的长度大于B,就令A从这个差值点之后开始遍历
            while (cnt --) cur1 = cur1 -> next;
            while (cur1 -> next != cur2 -> next) {
                cur1 = cur1 -> next;
                cur2 = cur2 -> next;
            }
        }else {
            int cnt = sizeB - sizeA; // B的长度大于A,就令B从这个差值之后开始遍历
            while (cnt --) cur2 = cur2 -> next; 
            while (cur1 -> next != cur2 -> next) {
                cur1 = cur1 -> next;
                cur2 = cur2 -> next;
            }
        }
        if (cur1 -> next == cur2 -> next) 
            return cur1 -> next;
        else return NULL;     
    }
};

142. 环形链表 II

解题思路:双指针(快慢指针)

本题主要考察两个知识点:

  • 链表是否有环
  • 如何确定环的入口

如何确定链表有环?

        首先定义快慢指针,设定快指针步长为2,慢指针步长为1。当环存在时,快指针必定可在环中与慢指针相遇。

        因为当环存在时,快指针必定先进入环,慢指针后进入环,而当慢指针进入环后,快指针相对慢指针就以1个格子的速度逼近慢指针,最终二者相遇。具体的证明过程可能需要画图才更清晰一些......

如何确定环的入口?

        假定环存在且快慢指针在 Meet 点相遇,此时慢指针走过的距离是:x + y,而快指针走过的距离是:x + y + n * (y + z),其中 n 代表快指针循环的圈数。因为快指针的速度是慢指针的两倍,所以当二者相遇时,存在等式:2*(x + y) = x + y + n * (y + z),化简得:x = n * (y + z) - y,此时等式出现了负数,我们很难看出如何用该公式解题,因为可将其优化为:x = (n - 1)(y + z) + z,其中 n 是等于或大于1的正整数。

        由图中可看出,环的长度可表示为:y + z,那么就是快指针在环中至少经过 n - 1 圈,慢指针才会进入环中,这时又经过不到一圈二者就相遇。假设entrance是入口,那么我们可以用 Meet 点到 entrance 点的距离来等价地表示出 head 到entrance 的距离。

 用数学归纳法证明公式:x = (n - 1)(y + z) + z,其中 n 是等于或大于1的正整数

  1. 当 n = 1 时,x = z,公式成立;
  2. 当 n = 2 时,x = (y +z)+ z,fast 在环中转一圈后与 slow 相遇找到 Meet 点,此时在 Meet 点初始化meet指针, 在head点初始化entrance指针,当二者同步向入口点进军时,必在entrance 相遇,公式成立;
  3. 当 n = k 时,x = (k - 1) * (y + z) + z,fast 在环中转 k - 1 圈后与 slow 相遇找到 Meet 点,此时在 Meet 点初始化meet指针, 在head点初始化entrance指针,当二者同步向入口点进军时,必在 entrance 点相遇,公式成立,证毕。
/**
 * 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 *slow = head;
        ListNode *fast = head;
        // 设定 fast 的步长为2,若链表节点数严格小于2且无环时,可直接return NULL
        while (fast && fast -> next) {
            // fast 的步长与 slow 相差1,他们在环中必定相遇 
            fast = fast -> next -> next; 
            slow = slow -> next;            // 设定 slow 的步长为1
            // 当二者相遇即可推算入口点
            if (fast == slow) {
                ListNode *meet = slow;      // 定义快慢指针在环中的相遇点
                ListNode *entrance = head;  // 定义入口点
                while (entrance != meet) {
                    entrance = entrance -> next;
                    meet = meet -> next;
                }
                return entrance;            // 返回入口点
            }
        }
        return NULL;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值