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

目录

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

 算法实现:

具体分析 :

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

前言:

方法一:单指针法

方法二:双指针法

面试题 02.07. 链表相交

思路分析:

算法实现:

142.环形链表II

 前言:

思路分析:

算法实现:

难点:

总结


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) {
        ListNode* vhead = new ListNode(0);
        vhead->next = head;
        ListNode* cur = vhead;
        while (cur->next != NULL && cur->next->next != NULL){
            ListNode* tmp = cur->next->next->next;
            ListNode* tmp1 = cur->next;
            cur->next = tmp1->next; //cur->next = cur->next->next;
            cur->next->next =tmp1;
            tmp1->next = tmp; //cur->next->next->next = tmp
            cur = tmp1; //cur = cur->next->next;
        }
        return vhead->next;
    }
};

具体分析 :

        为了便于实现头结点位置的变更,本题使用虚拟头结点进行处理,设置一个指针从虚拟头结点开始遍历链表,每次遍历循环都要确定下一个和下下一个的节点不为空,否则无法实现下面两个结点的互换,在互换前还要创建临时指针记录位置,具体实现时要注意各个操作的顺序,建议先进性画图分析。

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

题目链接

文章链接

前言:

        本题采用两种算法进行实现:第一种采用单指针,将删除倒数第N个节点转化为删除正数第M个节点,让单指针移动到第M - 1个节点即可实现删除倒数第N-1个节点的目的;第二种采用双指针实现,快慢指针都从虚拟头结点出发,让快指针先出发,待快指针移动了N+1个结点后再移动慢指针,之后让快慢指针始终保持N+1的间距向后移动,等到快指针移动到NULL时,慢指针刚好指向倒数第N+1个结点,可以实现对倒数第N个节点的删除操作。

方法一:单指针法

/**
 * 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* vhead = new ListNode(0);
        vhead->next = head;
        ListNode* cur = vhead;
        int size = 0;
        while (cur->next != NULL){
            cur = cur->next;
            size++;
        }
        int m = size - n; //将结点倒数顺序转为(以0为始)正数
        cur = vhead; //重置cur指针
        while (m--){
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        tmp = nullptr; //防止tmp成为野指针
        return vhead->next;
    }
};

        本题使用单指针法是一种类似于逆向思维的过程,需要注意的是转换后节点的正数位置不是直接链表长度减去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* vhead = new ListNode(0);
        vhead->next = head;
        ListNode* pre = vhead;
        ListNode* cur = vhead;
        n++;
        while (n--){
            pre = pre->next;
        }
        while (pre != NULL){
            pre = pre->next;
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        tmp = nullptr;
        return vhead->next;
    }
};

        使用双指针法时要注意两个指针间距的设置,当设置成n+1时,快指针到达NULL时,慢指针才指到倒数第N个节点的前一个节点。

面试题 02.07. 链表相交

题目链接

文章链接

思路分析:

        定义两个指针分别从两链表的头节点开始运动,当运动到当前链表的尾部时(NULL),移动到另一个链表的头节点继续向后移动,当两指针相遇时即为链表相交的位置,若最终当两指针分别遍历两链表后都为NULL,则退出循环遍历。

算法实现:

/**
 * 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* cur1 = headA;
        ListNode* cur2 = headB;
        while (cur1 != cur2){
            if (cur1 == NULL){
                cur1 = headB;
            }
            else
                cur1 = cur1->next;
            if (cur2 == NULL){
                cur2 = headA;
            }
            else
             cur2 = cur2->next;
        }
        return cur1;
    }
};

142.环形链表II

题目链接

文章链接

 前言:

        本题依然是一道利用双指针算法解决的较为经典的题目,主要难在如何控制快慢指针的移动,根据快慢指针运动的关系以及在链表中相遇的位置通过数学关系确定相遇点和环形链表入口的关系。

思路分析:

        定义fast、slow两个指针,fast每次移动两个节点,slow每次移动一个节点,这样可以确保在进入环后,slow指针一定能被fast指针遇上(并且经过分析是在慢指针slow进入环后的第一圈就遇上快指针fast),通过简答的画图和数学关系可以得到fast指针和slow指针相遇的位置到环入口的距离和头结点到入口的距离有一定的关系,根据这个特定的关系可以写出具体的代码实现入口位置的确定。

算法实现:

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

难点:

  1. 要根据数学关系确定相遇点和入口位置以及头节点三者位置的关系;
  2. 要先求出快慢指针相遇的位置,再反求出环形入口的位置,与常规思路不同。

总结

        刷链表题的第二天,今天的题目多次使用双指针法和创建虚拟头结点,技巧性较强却非常经典,环形链表的思路较为难想,后期还需要多加练习。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值