首页 > 编程语言 >代码随想录算法训练营第四天 24. 两两交换链表中的节点 | 19.删除链表的倒数第N个节点 | 面试题 02.07. 链表相交 | 142.环形链表II

代码随想录算法训练营第四天 24. 两两交换链表中的节点 | 19.删除链表的倒数第N个节点 | 面试题 02.07. 链表相交 | 142.环形链表II

时间:2023-01-14 23:33:28浏览次数:44  
标签:head ListNode cur 随想录 next 链表 节点 指针

链表操作 lc24 两两交换链表中的节点

这道题在熟悉链表操作后并不困难,在做的时候要想清楚每一步的顺序,可以画图辅助。
感觉步骤的顺序也可以更改,最重要的是不要出现空指针丢失接下来数据位置
使用虚拟头节点可以一直找到头节点的位置

初见 pre+cur

/**
 * 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) {
        ListNode* dummy_head = new ListNode(0,head);
        ListNode* pre = dummy_head;
        ListNode* cur = head;
        while(pre && cur && cur->next){
            pre->next = cur->next;
            cur->next = pre->next->next;
            pre->next->next = cur;
            pre = cur;
            cur = pre->next;
        }
        return dummy_head->next;
    }
};

代码随想录答案24

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            ListNode* tmp = cur->next; // 记录临时节点
            ListNode* tmp1 = cur->next->next->next; // 记录临时节点

            cur->next = cur->next->next;    // 步骤一
            cur->next->next = tmp;          // 步骤二
            cur->next->next->next = tmp1;   // 步骤三

            cur = cur->next->next; // cur移动两位,准备下一轮交换
        }
        return dummyHead->next;
    }
};

链表操作 lc19 删除链表倒数第N个节点

前面已经熟悉了删除链表节点的操作,但是刚看到这道题目还是感觉至少需要历遍两次,即第一次看一下链表长度,然后计算出删除节点的位置计数,再历遍一次找到目标节点并删除。

然而题目要求在一次遍历中完成。可以使用快慢指针(双指针)来解决。
即先让快指针与慢指针相距n个节点,再同时移动两个指针,当前面的快指针到尾后(NULL)的时候,后面的慢指针将正好指向想要删除的目标节点。在实际实现中,我们需要目标节点的前一个节点来进行删除操作,即我们需要落后于前面快指针n+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* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy_head = new ListNode(0,head);
        ListNode* fast = dummy_head;
        ListNode* slow = dummy_head;
        n++;
        while(n--&&fast){
            fast = fast->next;
        }
        while(fast){
            fast = fast->next;
            slow = slow->next;
        }
        ListNode* tmp = slow->next;
        slow->next = slow->next->next;
        delete tmp;
        return dummy_head->next;
    }
};

链表交点 lc面试题02.07.链表相交

这道题目的关键在于想到若两单向链表存在交点,则交点之后的所有节点都相同。所以可以找出较短的链表,并让较长链表从后往前保留较短链表相同长度,进行同时遍历。

/**
 * 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* curA = headA;
        ListNode* curB = headB;
        int lenA = 0;
        int lenB = 0;

        while(curA){
            lenA++;
            curA = curA->next;
        }
        while(curB){
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        if (lenB>lenA){ //A永远长,curA指向长的链表头
            swap(lenA,lenB);
            swap(curA,curB);
        }
        //对齐
        int diff = lenA - lenB;
        while(diff--){
            curA = curA->next;
        }
        while(lenB--){
            if(curA==curB){
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

环形列表 lc142 环形列表II

本题编码并不是主要的难点,难点在于确定数学关系。
思路大致如下:

  • 判断是否存在环
  • 可以设立快慢指针,两个指针同时遍历,快指针每次走两步,慢指针每次走一步,如果存在环,两个指针会在环中相遇
  • 因为两个指针相差为1,所以两个指针一定会相遇
  • 接下来需要确定数学逻辑,考虑下图

lc142

慢指针路程:\(x+y\) (值得一提的是慢指针在相遇前走的路程不会超过一圈,可以这样想,慢指针走一圈,快指针可以走两圈,绝对会相遇)
快指针路程:\(x+n(y+z)+y\) (这里n是快指针所走圈数,快指针在相遇前一定会走一圈以上,即\(n\geq1\) 。因为快指针会比慢指针先进入环,然后再追上慢指针,这个过程至少经过环入口两次)
因为快指针速度是慢指针的2倍,所以慢指针所走路程的二倍是快指针的路程:

\[2(x+y)=x+n(y+z)+y \]

左右两边消掉一个\(x+y\)

\[x+y=n(y+z) \]

把\(y\)放到右边

\[x=n(y+z)-y \]

因为\(n\geq1\)继续推导

\[x=(n-1)(y+z)+y+z-y \]

\[x=(n-1)(y+z)+z \]

从这里我们不难看出,x和z的关系:我们让两个指针同时从head和相遇点开始遍历,head出发的指针走x个距离就可以到环入口,而相遇点出发的指针走z个距离后也会到环入口,根据上面公式,x与z的差除以环长度会整除(n=1时x=z),所以我们从相遇点出发的指针一定会与从head出发的指针相遇在环入口。
以上分析好后,代码并不难写。

/**
 * 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 *fast = head;
        ListNode *slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if (fast==slow){
                ListNode *track1 = fast;
                ListNode *track2 = head;
                while(track1 != track2){
                    track1 = track1->next;
                    track2 = track2->next;
                }
                return track1;
            }
        }
        return NULL;
    }
};

标签:head,ListNode,cur,随想录,next,链表,节点,指针
From: https://www.cnblogs.com/frozenwaxberry/p/17052824.html

相关文章