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

24. 两两交换链表中的节点(中等)

题目链接: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* _dummyhead = new ListNode();//声明一个虚拟头节点指向head
        _dummyhead->next = head;//虚拟头节点指向head
        ListNode* pre = _dummyhead;//前指针指向虚拟头节点
        ListNode* cur = pre->next;//后指针
        ListNode* temp = nullptr;//用于存放cur->next
        while(cur != nullptr && cur->next != nullptr) {//若当前节点和下一个节点均为非空节点则开始交换
            temp = cur->next;
            pre->next = temp;
            cur->next = temp->next;
            temp->next = cur;
            
            pre = cur;
            cur = cur->next;
        }
        return _dummyhead->next;
    }
};

注意:while中的判断条件不能仅仅判断cur->next非空,必须cur和cur->next同时非空才进行交换。试想一下若是cur为空又何来cur->next呢?这里就会出现一个bug。

19.删除链表的倒数第N个节点(中等)

题目链接:删除链表的倒数第 N 个结点
题目描述: 给你一个链表,删除链表的倒数第 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) {}
 * };
 */
 //双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
 //我自己的理解是这样的,比如链表中为_summyhead->1->2->3->4->5->null,slow和fast指针初始指向_summyhead
 //若要删除倒数第2个节点,即n=2,此时我们要让slow移动到倒数第3个节点,易知slow要走sum-n步,即5-2=3步
 //本例中链表长度为sum=5, 先让fast指针走n步,再多走一步,此时fast走了n+1步,再让slow和fast走到fast=null,此时fast要再走num-n(5-2)步才能到null。
 //故让slow和fast同时向后遍历,恰好fast和slow都走num-n步,此时slow恰好指向要删除节点的前一个节点。
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* _dummyhead = new ListNode();
        _dummyhead->next = head;
        ListNode* slow = _dummyhead;
        ListNode* fast = _dummyhead;
        while(n-- && fast != nullptr) {//让fast指针走n步
            fast = fast->next;
        }
        fast = fast->next;//再多走一步
        while(fast != nullptr) {//让slow和fast走num-n步
            slow = slow->next;
            fast = fast->next;
        }
        //进行删除操作
        ListNode* temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        return _dummyhead->next;//返回修改后的链表头节点
    }
};

面试题 02.07. 链表相交(中等)

题目链接:面试题 02.07. 链表相交
题目描述: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
思路:找交点容易想到快慢指针法,要考虑到两个单链表的长度,从而知道需要两个变量lenA和lenB,接着为了使两个工作指针能从后半段长度一致处开始同时后移,自然就需要求lenA和lenB的差值gap,让长链表的指针先走gap。

/**
 * 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;//curA指向A链表首节点
        ListNode* curB = headB;//curB指向B链表首节点
        int lenA = 0;//A链表长度
        int lenB = 0;//B链表长度
        while (curA != NULL) {//计算A链表长度
            curA = curA->next;
            lenA++;
        }
        while (curB != NULL) {//计算B链表长度
            curB = curB->next;
            lenB++;
        }
        curA = headA;//算完链表长度后再调回到首节点
        curB = headB;
        if (lenB > lenA) {//让curA指向长度更大的链表,同理lenA和lenB也得swap
            swap (lenA,lenB);
            swap (curA,curB);
        }
        int gap = lenA-lenB;//链表差值
        while(gap--) {//将长的链表中的指针先走gap步
            curA = curA->next;
        }
        while(curA != NULL) {//寻找链表交点
            if(curA == curB){//找到交点
                return curA;//直接return
            }
            curA = curA->next;//AB各自的指针都往后移动
            curB = curB->next;
        }
        return NULL;//没找到交点return NULL
    }
};

142.环形链表Ⅱ (中等)

题目链接:142.环形链表Ⅱ
题目描述: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。

思路: 显然要使用快慢指针法,定义快慢指针slow和fast。要解决两个问题
1.判断链表是否有环。2.若有环,找到这个环的入口。

1.判断链表是否有环: 从头结点出发,fast每次移动两个结点,slow每次移动一个结点,如果fast和slow指针在途中相遇,说明链表一定有环
容易知道,若链表有环,则fast指针一定先进入环中,如果fast指针和slow指针相遇的话,必然在环中相遇。由于fast每次都比slow多移动一个节点,所以对于slow指针而言,fast指针是一步一步在追赶slow指针的,因此有环存在的话,fast总能在某一时刻恰好追上slow指针。至此,便可判断链表是否有环。
如下图所示:在这里插入图片描述
2.判断环的入口是链表的第几个结点。
如图所示,头结点到环形入口节点的节点数为x,入口节点到相遇节点的节点数为y,相遇节点到入口节点的节点数为z。
则在相遇时:slow指针移动了x+y个节点,fast指针移动了x+y+n(y+z) 个节点,n为fast走的环数,显然n>=1。
又由于fast走的步数为slow 的两倍,从而有2*(x+y) = x+y+n(y+z),要求的是x,两边将x+y消去可得x+y = n(y+z),进而有x = (n-1)(y+z) + z。
在这里插入图片描述
不妨设n=1,此时有x=z,即从相遇节点到入口节点和头节点到入口节点的距离相等,故再声明两个指针,一个指向头节点,一个指向相遇节点,同时后移,当两指针指向同一个节点屎,就得到了环形入口节点。
在这里插入图片描述
代码如下:

/**
 * 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;//声明两个指针,slow和fast
        ListNode* fast = head;
        while (fast != NULL && fast->next != NULL) {//由于fast步长更大因此只需判断fast和fast->next是否为空
            slow = slow->next;//慢指针每次走一个结点
            fast = fast->next->next;//快指针每次走两个结点
            if (slow == fast) {//指针相遇,证明有环
                ListNode* index1 = fast;//index1指向相遇结点
                ListNode* index2 = head;//index2指向头结点
                while (index1 != index2) {//未相遇则向后移动
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;//返回相遇处的节点
            }
        }//fast为NULL或者指针永不相遇说明链表无环
        return NULL;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值