C++刷题第四天 链表 24. 两两交换链表中的节点 ● 19.删除链表的倒数第N个节点 ● 160. 链表相交 ● 142.环形链表II

本文介绍了四种关于链表的操作:两两交换节点,删除倒数第N个节点,查找相交链表的起始节点以及寻找环形链表的入口。每种操作都采用了双指针法,通过调整指针的移动来解决相应问题,如交换节点时需考虑链表长度和节点位置,删除节点时需确保正确定位,相交链表和环形链表则通过快慢指针寻找特定节点。
摘要由CSDN通过智能技术生成

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

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例:

原列表为:1-2-3-4-5

交换一次后为:2-1-3-4-5

交换结束为:2-1-4-3-5

 就是下标为偶数的节点与后一节点交换位置

解题思路:

 如图所示,先把cur指针域指向2,再把2指针域指向1,最后1指针域指向3,完成这一交换;但是实际写代码过程中,因为链表的特性,要考虑实施过程中每个节点指向的变化。

在步骤一执行后,节点1如果不预先存储给其他临时节点,节点1就找不到了;

执行完步骤二,节点3的位置也是无法直接寻得的,也需要临时节点存储节点3;

并且整个循环的截止条件是什么?

当链表节点总数为奇数时,最后一个节点保持不动,那cur的位置就是倒数第二个节点,判断条件为cur->next->next != nullptr;

当链表节点数为偶数时,所有节点完成替换,cur为最后一个节点,判断条件为cur->next != nullptr;

整个交换过程完成后,返回的应该是虚拟头节点dummyhead->next。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode*dummyhead = new ListNode(0);
        dummyhead->next = 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;
    }
};

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]


示例 2:

输入:head = [1], n = 1
输出:[]


示例 3:

输入:head = [1,2], n = 1
输出:[1]

解题思路:双指针法

限定条件一:

用两个指针,一快一慢,快指针慢指针初始为同一位置后,快节点先移动n步,然后再同时移动快慢指针直到快指针节点的指针域指向空,这时,慢指针的next就是倒数第n个节点,将slow->next = slow->next->next即可。

避坑:

1、快慢指针初始位置应该是什么呢?

如果都初始为头节点,那如示例二,就没有办法完成任务了。所以要使用虚拟头节点,在头节点前面增加一个临时节点,指向头节点,并且将快慢指针都是初始化为该虚拟头节点,可以避免示例二的情况。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode*dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode*fast = dummyhead;
        ListNode*slow = dummyhead;
        while(n-- && fast != NULL){
            fast = fast->next;
        }
        
        while(fast->next != nullptr){
            fast = fast->next;
            slow = slow->next;
        }
        ListNode*temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        return dummyhead->next;
    }
};

限定条件二:

其实这里有另外一套限定条件,如下:

用两个指针,一快一慢,快指针慢指针初始为虚拟头节点,快节点先移动n+1步,然后再同时移动快慢指针直到快指针节点为空,这时,慢指针的next就是倒数第n个节点,将slow->next = slow->next->next即可。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
        //也可以在前方定义fast时将fast定义为head节点
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        
        // ListNode *tmp = slow->next;  C++释放内存的逻辑
        // slow->next = tmp->next;
        // delete nth;
        
        return dummyHead->next;
    }
};

160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

示例一:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'

示例二:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null

解题思路:

如果两个链表在节点E相交,那么E之后的节点肯定都是相同的,所以可以考虑求出两个链表的长度,然后计算长度差值,然后让curA移动到,和curB 末尾对齐的位置,如图:

 

 然后从此处开始对比curA和curB,如果两个节点相同,那么此节点就是两链表相交的节点。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int m=0,n=0;
        ListNode*cura = headA;
        while(cura){   //计算链表A的长度
            m++;
            cura = cura->next;
        }
        ListNode*curb = headB;
        while(curb){     //计算链表B的长度
            n++;
            curb = curb->next;
        } 
        cura = headA;
        curb = headB;
        if(m>n){            //如果A较长,那移动cura到与curb同长的位置
            int x = m-n;
            while(x-- > 0){
                cura = cura->next;
            }
        }
        else{           //如果B较长,将curb移动到与cura同长度的位置,如果A=B,那么X=0,不会进行任何操作
            int x = n-m;
            while(x-- > 0){
                curb = curb->next;
            }
        }
        while(cura != NULL){      //然后同时移动cura和curb,直到cura=curb,返回当前节点
            if(cura == curb ){
                return cura;
            }
            cura = cura->next;
            curb = curb->next;
        }
        return NULL;       //如果在上个while循环没有return,那么就没有找到相交节点,return null
    }
};

142.环形链表II 

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

 解题思路:

 这题相对来说解题思想比较重要,知道这个思想后,代码并不复杂,这个解题思路建议看这个:代码随想录 (programmercarl.com)

讲解的很详细。

整体过程就是用两个指针,一快一慢,然后快指针一次移动两位,慢指针一次移动一位,如果有环,那么快慢指针一定会在环内某个节点相遇,因为只要慢指针入环,那快指针相对于慢指针就是每次循环靠近慢指针一次,所以只要在环内,就肯定会相遇并且是在慢指针在环内循环一遍之前。

快慢指针相遇就代表有环,那么怎么找到环的入口呢?定义一个新的节点等于快慢指针相遇的节点,另外一个新节点等于头节点,然后两个节点同时向后移动,直到相遇,这个相遇的节点就是环的入口。详细原理可以看上面详解的链接。

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* p = fast;
                ListNode* q = head;
                while(p != q){
                    p = p->next;
                    q = q->next;
                }
                return p;
            }
        }
        return NULL;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值