C++编程能力提升--Day4

本文介绍了LeetCode中的五个链表问题:交换链表节点、删除倒数第N个节点、寻找相交链表、环形链表II(双指针法和哈希表法)以及寻找重复数。通过双指针技巧和哈希表分析了这些问题的核心算法和解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、24 两两交换链表中的节点

题目链接:24 两两交换链表中的节点

核心:使用虚拟节点dummyHead
第一,当前cur从dummyHead开始(且总是指向交换两节点的前一个节点),且需要临时保存当前组交换的第一个节点和下一组交换的第一个节点;
第二,交换有三步:前一个节点连接到交换组的第二个节点,交换组的第二个节点连接到交换组的第一个节点,交换组的第一个节点连接到下一组交换的第一个节点;
第三,当前cur更新到下一组交换的前一个节点。

    ListNode* swapPairs(ListNode* head) {
    //使用dummyHead
    ListNode* dummyHead = new ListNode(0);
    dummyHead->next=head;   //虚拟节点,返回dummyHead->next即可,不管链表如何操作
    ListNode* cur=dummyHead;    //注意cur总是指向待交换节点的前一个节点
    while(cur->next && cur->next->next)
    {//cur->next是交换的第一个节点,cur->next->next是第二个节点
        ListNode* temp1=cur->next; //先保存两两交换的第一个节点,交换后在链表中是第二个节点
        ListNode* temp2=cur->next->next->next;  //同时保存下一组待交换节点的第一个

        cur->next=temp1->next;  //交换后cur的下一个是cur->next->next
        cur->next->next=temp1;  //交换后cur的下一个的下一个则是temp1
        temp1->next=temp2;      //完成交换,将交换后的第二个节点连接到下一组待交换节点

        cur=cur->next->next;    //注意cur总是指向待交换节点的前一个节点
    }
    return dummyHead->next; 
    }

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

题目链接:19 删除链表的倒数第N个节点

核心:虚拟节点和双指针
第一,fast指针先移动N步(从head开始),然后slow和fast同时移动(slow从head开始),直到fast为NULL时slow指向待删除的节点;
第二,实际删除节点时slow需指向待删除节点的前一个节点,因此slow需初始化为head的前一个节点,即dummyHead。

    ListNode* removeNthFromEnd(ListNode* head, int n) {
    //虚拟头节点+双指针,fast先移动n步,然后slow和fast同时移动,直至fast指向null,此时slow即要删除的元素
    ListNode* dummyHead=new ListNode(0);
    dummyHead->next=head;
    ListNode* slow=dummyHead;   //注意slow起始与fast不同,不是从head开始
    ListNode* fast=dummyHead->next; //fast从head开始,先移动n步
    while(n-- && fast)
        fast=fast->next;
    while(fast)
    {//fast为空时退出,此时slow指向待删除节点的前一个节点
        slow=slow->next;
        fast=fast->next;
    }
    ListNode* temp=slow->next;  //待删除节点
    slow->next=temp->next;
    delete temp;

    return dummyHead->next;
    }

三、160 相交链表

题目链接:160 相交链表

核心:双指针,指向两个链表的指针从距离末尾的同一起始位置开始,逐一遍历剩余的节点,若存在节点相同(非val相同)则两个链表相交。
第一,求解两个链表的长度;
第二,较长链表的起始位置不再是head,而是两个链表长度差的节点位置,较短链表的起始位置依然是head,两个指针同时移动检查是否存在相同节点。

    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    //双指针:分别指向两个链表,思想是两指针指向距离链表末尾的同一起始位置,然后逐个节点比较
    ListNode* curA=headA;
    ListNode* curB=headB;
    int sizeA=0,sizeB=0;
    while(curA)
    {//遍历A计算其长度
        sizeA++;
        curA=curA->next;
    }
    while(curB)
    {//遍历B计算其长度
        sizeB++;
        curB=curB->next;
    }
    curA=headA;
    curB=headB;
    //固定A总是较长的链表,若B长度大于A需要进行交换
    if(sizeB>sizeA)
     {//交换后A较长
         swap(sizeA,sizeB);
         swap(curA,curB);
     }
     int diff=sizeA-sizeB;
     while(diff--)
     {//令较长的A与B位于同一起点,即链表末尾对齐
         curA=curA->next;
     }
     while(curA)
     {
        if(curA==curB)
             return curA;   //找到相交的起始节点,注意不是val相等

        curA=curA->next;    //同时后移继续寻找相交节点
        curB=curB->next;
     }
    return NULL;
    }

四、142 环形链表II

题目链接:142 环形链表II

1、双指针法

核心:快慢指针,
第一,fast每次移动2步,slow每次移动1步,fast和slow总会在环里相遇,若不相遇说明该链表无环;
第二,fast和slow相遇后,再令两个指针从head和此相遇点开始,同时移动1步,这两个指针的相遇点即为环的起点。

    ListNode *detectCycle(ListNode *head) {
    //快慢指针,fast每次2步,slow每次1步,相遇时一定在环内;
    //再次利用双指针分别从head和相遇处同时移动,相遇时即为环的起点
    ListNode* fast=head;
    ListNode* slow=head;
    while(fast && fast->next)
    {//注意循环条件的设置!
        fast=fast->next->next;  //fast每次走2步,slow每次走1步
        slow=slow->next;
        if(slow==fast)
        {//第一次相遇说明存在环
            ListNode* index1=head;  
            ListNode* index2=slow;
            while(index1!=index2)
            {//第二次循环目的是寻找环的起点,即相遇处
                index1=index1->next;
                index2=index2->next;
            }
            return index1;//第二次相遇时即为环的起点
        }
    }
    return NULL;
    }

2、哈希表法

核心:链表节点存储到哈希表unordered_set(注意不是unordered_map,因为无需存储键值对),一旦某个节点的出现次数超过1次,则说明该链表存在环,且第一次出现重复的节点即为环的起点。

    ListNode *detectCycle(ListNode *head) {
    //哈希表:将链表所有节点存储到哈希表,若出现重复则说明存在环,环的起点即第一次出现重复的节点
    unordered_set<ListNode*> listSet;   //只需存储节点,无需使用map存储键值对
    ListNode* cur=head;
    while(cur)
    {//cur遍历当前链表直至末尾
        if(listSet.count(cur))
            return cur; //count查找cur的个数,若不为0说明出现重复的cur,即存在环
        listSet.insert(cur);    //count为0说明此前未出现,需插入到哈希表
        cur=cur->next;
    }
    return NULL;
    }

五、287 寻找重复数

题目链接:287 寻找重复数
核心:由数组下标映射到元素值构建链表,若存在重复数说明链表存在环,那么可使用环形链表确定环起点的方法求解该重复数。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
    //环形链表:利用数组下标映射到元素值构建链表,比如下标0映射为1,下标1映射为3,则链表第一个节点是0,第二个节点是3,第三个节点是下标3映射的元素2,第四个节点是下标2映射的元素4,第五个节点是下标4映射的元素2,此时链表中出现环。环的起点即重复元素
    //环形链表的解法是利用快慢指针,当快慢指针相遇时快指针从链表起始位置开始,和慢指针同速移动,再次相遇点即环的起点
    int slow = 0; 
    int fast = 0;
    slow = nums[slow];  
    fast=nums[nums[fast]];
    while(slow!=fast)  {
        slow=nums[slow];
        fast=nums[nums[fast]];
    }//退出循环时slow==fast,此时将fast移到第1个节点重新开始
    fast = 0; //与142题不同的是,此题第一次相遇前fast比slow多走一步,那么第一次相遇后fast回到起点,同时slow需要多走一步,然后同速
    //fast = nums[fast]; //fast回到起点,同时slow向前走一步。此题的特殊性,可以把这两步放在第二个while中
    //slow = nums[slow];
    while(slow!=fast) {
        slow=nums[slow];
        fast=nums[fast];
    }//退出循环时slow=fast=环起点
    return slow;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值