算法--单链表

news2024/11/20 0:29:27

算法–单链表

1.合并链表

1.合并两个排序的链表

  • 解法:这个比较容易,直接对比两个两个链表节点,小的节点直接插入到返回的新链表上

    /**
     * struct ListNode {
     *  int val;
     *  struct ListNode *next;
     *  ListNode(int x) : val(x), next(nullptr) {}
     * };
     */
    class Solution {
      public:
        /**
         * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
         *
         *
         * @param pHead1 ListNode类
         * @param pHead2 ListNode类
         * @return ListNode类
         */
        ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
            ListNode* dummy = new ListNode(0);
            ListNode* p = dummy;
    
            if(pHead1 ==nullptr){
                return pHead2;
            }
            if(pHead2 == nullptr){
                return pHead1;
            }
    
            while (pHead1 && pHead2) {
                if(pHead1->val > pHead2->val){
                    p->next = pHead2;
                    p = p->next;
                    pHead2= pHead2->next;
                }
                else {
                    p->next = pHead1;
                    p= p->next;
                    pHead1= pHead1->next;
                }
            }
    
            if(pHead1 ==nullptr){
                p->next = pHead2;
            }
            if(pHead2==nullptr){
                p->next = pHead1;
            }
    
            return dummy->next;
        }
    };
    

2.合并K个有序链表

  • 解法:这个稍微复杂一些,需要找到k个链表中节点的最小值。所以我们需要借助优先队列(小根堆),先获取每个链表的第一个节点的大小关系,然后获取当前最小节点链表的后续节点加入到队列。依赖优先队列的自动排序的特性,很容易获得从小到大的链表。

    /**
     * struct ListNode {
     *  int val;
     *  struct ListNode *next;
     *  ListNode(int x) : val(x), next(nullptr) {}
     * };
     */
    #include <list>
    #include <queue>
    class Solution {
      public:
        /**
         * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
         *
         *
         * @param lists ListNode类vector
         * @return ListNode类
         */
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            if (lists.size() == 0) return nullptr;
            // 申请虚拟头节点,需要传出函数。
            ListNode* dummy = new ListNode(-1);
            ListNode* p = dummy;
    
            // 重载比较方式
            struct cmp {
                bool operator()(ListNode* a, ListNode* b) {
                    return a->val > b->val;
                }
            };
    		
            // 队列里存放的是链表的头指针,并按照我们自定义的方式排序。
            priority_queue<ListNode*, vector<ListNode*>, cmp> pq;
    
            for (int i = 0; i < lists.size(); ++i) {
                if (lists[i] != nullptr) {   // 需要考虑到空的链表
                    pq.push(lists[i]);
                }
            }
    
            while (!pq.empty()) {
                ListNode* temp = pq.top();
                pq.pop();
    
                p->next = temp;
                p = p->next;
                
                // 如果当前最小节点的链表后续有节点,加入到队列中排序,这样
                // 就可以得到当前最小的节点
                if (temp->next) {
                    pq.push(temp->next);
                }
            }
            return dummy->next;
        }
    };
    

    或者,你可以把所有链表节点值摘下来,放到优先队列里排序(或者你放数组里然后排序都行),然后再重新建立链表

    ListNode* mergeKLists(vector<ListNode*>& lists) {
            ListNode* dummy = new ListNode(-1);
            ListNode* p = dummy;
    
            priority_queue<int, vector<int>, greater<int>> pq;
            for (int i = 0; i < lists.size(); ++i) {
                if (lists[i] != nullptr) {
                    while (lists[i]) {
                        pq.push(lists[i]->val);
                        lists[i] = lists[i]->next;
                    }
                }
            }
    
            while (!pq.empty()) {
                // 需要重新申请节点,否则无法连接
                p->next = new ListNode(pq.top());
                pq.pop();
                p = p->next;
            }
            return dummy->next;
    }
    

2.环形链表

1.判断链表是否有环

  • 解法:这道题,有同学可能采用暴力解法,把链表节点依次放入到数组中,每放入一个就遍历数组查看是否存在该节点,如果存在就有环,否则没有。这样的确可以通过,但是速度会大大折扣,我们这里采用快慢指针的思想。快慢指针,故名思意一个指针走的快,一个走得慢,如果两个指针同时从链表头部开始走,如果有环一定会在某个地方相遇。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    class Solution {
    public:
        bool hasCycle(ListNode *head) {
            ListNode *fast = head;
            ListNode *slow = head;
            // 注意这里,快指针在没有环的情况下,最先到达表尾,且要排除单个节点无环情况
            while(fast && fast->next){
                slow = slow->next;
                fast = fast->next->next;
                if(slow == fast){
                    return true;
                }
            }
            return false;
        }
    };
    

2.链表中环的入口结点

  • 解法:在这里插入图片描述

    /*
    struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) :
            val(x), next(NULL) {
        }
    };
    */
    class Solution {
      public:
        ListNode* EntryNodeOfLoop(ListNode* pHead) {
            ListNode* slow = pHead, *fast = pHead;
    		
            // 大于一个节点才能算环
            while (fast && fast->next) {
                slow = slow->next;
                fast = fast->next->next;
                if (slow == fast) {
                    break;
                }
            }
            
            // 无环情况
            if (fast == nullptr || fast->next == nullptr) return nullptr;
    
            fast = pHead;
            while (slow != fast) {
                slow = slow->next;
                fast = fast->next;
            }
            return fast;
        }
    };
    

    还可以采用哈希来解答,把节点依次加入到哈希中,如果某个元素重复了,那么即为入口。

    #include <unordered_set>
    class Solution {
    public:
        ListNode* EntryNodeOfLoop(ListNode* pHead) {
            unordered_set<ListNode*>set;
            while(pHead){
                if(set.count(pHead)) return pHead; // 也可以使用contains(c++20)
                set.insert(pHead);
                pHead = pHead->next;
            }
            return nullptr;
        }
    };
    

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

  • 解法:采用双指针,由于要删除倒数第n个节点,所以我们一定要获取到待删除节点的前一个节点,所以可以让其中一个指针先走n步,然后两个指针同时走,这样当先走的指针走到链表末尾,后走的指针刚刚好走到倒数第n个节点的前一个节点。算法中我们创建一个虚拟节点,这样方便处理删除第一个头节点的情况。
/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 *  ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
#include <list>
class Solution {
  public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode * dummy =new ListNode(-1);
        dummy->next = head;
        
        ListNode* p1 = dummy, * p2 = dummy;

        if (head == nullptr) return nullptr;
        // 快指针先走n步
        while (n--) {
            p2 = p2->next;
        }
        // 同时走,直到链表结尾
        while (p2->next) {
            p1 = p1->next;
            p2 = p2->next;
        }
        // 删除节点
        p1->next = p1->next->next;
        return dummy->next;
    }
};

4.判断两个链表是否相交,相交则返回相交节点

  • 解法: 36
/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
  public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        ListNode* L1 = pHead1, *L2 = pHead2;
        while (L1 != L2) {
            L1 = (L1 == nullptr) ? pHead2 : L1->next;
            L2 = (L2 == nullptr) ? pHead1 : L2->next;
        }
        return L1;
    }
};

同样也可以用哈希做,但会产生额外空间,同前所述。

4.删除有序链表中重复的元素

1.删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次

  • 解法:双指针法,两个指针开始都指向头节点,当后续节点值相同时,快指针向后移动,满指针不动。当节点值不同时,当前慢指针的next指向快指针,然后移动到快指针节点处。

    /**
     * struct ListNode {
     *  int val;
     *  struct ListNode *next;
     *  ListNode(int x) : val(x), next(nullptr) {}
     * };
     */
    class Solution {
      public:
        ListNode* deleteDuplicates(ListNode* head) {
            if (head == nullptr) return nullptr;
            ListNode* fast = head, *slow = head;
            while (fast) {
                if (fast->val == slow->val) {
                    fast = fast->next;
                } else {
                    slow ->next = fast;
                    slow = fast;
                }
            }
            slow->next = nullptr;
            return head;
        }
    };
    

2.删除有序链表中重复的元素-II(给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。)

  • 解法:本题是上一道题的进阶版,上一题删除元素让所有元素只出现一次,这道题是,将所有重复的删除。最常规的还是双指针法,一个指针遍历的元素,并设置标志位,标记元素是否重复,一个指针偏移

    /**
     * struct ListNode {
     *  int val;
     *  struct ListNode *next;
     *  ListNode(int x) : val(x), next(nullptr) {}
     * };
     */
    #include <cmath>
    #include <type_traits>
    class Solution {
      public:
    
        ListNode* deleteDuplicates(ListNode* head) {
            ListNode* dummy = new ListNode(-1);
            if (head == nullptr) return nullptr;
            dummy->next = head;
            bool single = true; // 元素是否是只有一个
            ListNode* p1 = dummy, *p2 = head;
            while(p2->next){
                // 如果p2的当前值和下一个值相同就标记这个元素不是唯一,并移动p2
                if(p2->val == p2->next->val){
                    single = false;
                    p2=p2->next;
                }
                // 当p2与后面的值不同时,需要判断p2当前指定的值是否是唯一的,
                // 如果不是唯一的,p1的next指向p2的next(注意,此时不移动p1),实现删除重复元素,并重置标记
                // 如果是唯一的,保留元素,那么p1指向p2,并移动到p2,然后p2指向下一个节点。
                else {
                    if(!single){
                        p1->next = p2->next;
                        p2 = p2->next;
                        single = true;
                    }
                    else {
                        p1->next = p2;
                        p1 = p2;
                        p2= p2->next;
                    }
                }
            }
    
            // 出现重复元素后面无其它元素时。
            if(!single){
                p1->next = nullptr;
            }
            return dummy->next;
        }
    };
    
  • 解法2:其实这道题还可以使用哈希来做,记录元素出现次数大于1的元素,并删除,重新建立链表

    /**
     * struct ListNode {
     *	int val;
     *	struct ListNode *next;
     *	ListNode(int x) : val(x), next(nullptr) {}
     * };
     */
    #include <unordered_map>
    class Solution {
    public:
        ListNode* deleteDuplicates(ListNode* head) {
            if(head == nullptr )return nullptr;
            unordered_map<int,int>map;
            ListNode *dummy = new ListNode(-1);
            dummy->next = head;
    
            ListNode* cur = head;
            // 统计每个元素出现的次数。
            while(cur){
                map[cur->val]++;
                cur = cur->next;
            }
    
            cur = dummy;
            // 重新建立链表
            while(cur->next){
                if(map[cur->next->val] != 1){
                    cur->next = cur->next->next;
                }else {
                    cur = cur->next;
                }
            }
            return dummy->next;
        }
    };
    

5.判断一个单链表是否为回文结构(正反顺序都一样,例如12321)

  • 解法:前面我们使用了大量的双指针来解题,这个同样可以用双指针来解决,但是由于是单链表,所以我们需要借助数组,来存储元素,然后使用两个指针从数组的两边来对比元素。

    /**
     * struct ListNode {
     *  int val;
     *  struct ListNode *next;
     *  ListNode(int x) : val(x), next(nullptr) {}
     * };
     */
    #include <vector>
    class Solution {
      public:
        bool isPail(ListNode* head) {
            vector<int>vec;
            while (head) {
                vec.push_back(head->val);
                head = head->next;
            }
            int p = vec.size() - 1;
            for (int i = 0;  i < (vec.size() - 1) / 2; ++i) {
                if (vec[i] != vec[p - i]) {
                    return false;
                }
            }
            return true;
        }
    };
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1137999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023高频前端面试题-TCP

1. TCP 协议是什么? TCP(Transmission Control Protocol 传输控制协议) 是一种面向连接(连接导向) 的、可靠的、 基于 IP 的传输层协议。 TCP 使⽤校验、确认和重传机制来保证可靠传输 而 HTTP 协议 就是建立在 TCP / IP 协议 之上的一种应用。 TCP: 三次握手, 四次挥手~ …

关于本地项目上传到gitee的详细流程

如何上传本地项目到Gitee的流程&#xff1a; 1.Gitee创建项目 2. 进入所在文件夹&#xff0c;右键点击Git Bash Here 3.配置用户名和邮箱 在gitee的官网找到命令&#xff0c;注意这里的用户名和邮箱一定要和你本地的Git相匹配&#xff0c;否则会出现问题。 解决方法如下&…

支付宝证书到期更新完整过程

如果用户收到 支付宝公钥证书 到期通知后&#xff0c;可以根据如下指引更新证书 确认上传成功后就会生成新的证书&#xff0c;把新的证书替换到生产环境就可以了

python DataFrame的用法

pandas 首先要import pandas as pd&#xff0c;如果运行时报错找不到pandas则需要下载对应库&#xff0c;下载流程参考以下链接《Phthon下载库函数》 https://blog.csdn.net/qq_33308135/article/details/134054352?spm1001.2014.3001.5502 创建一个DataFrame import pandas…

kubernetes(3)

目录 service微服务 ipvs模式 clusterip headless nodeport loadbalancer metallb nodeport默认端口 externalname ingress-nginx 部署 基于路径访问 基于域名访问 TLS加密 auth认证 rewrite重定向 canary金丝雀发布 基于header灰度 基于权重灰度 业务域拆分…

【C++】变参函数va_start,va_arg,va_end介绍及实现方式

如果写过JS的话&#xff0c;就知道在JS中定义一个函数&#xff0c;就算输入的实参和形参不一致&#xff0c;也可以同过arguments获取参数 function abc(x) {console.log(x)console.log(arguments[0])console.log(arguments[1])console.log(arguments[2]) } abc(1,2,3)上例输出…

算法通关村第三关-青铜挑战数组专题

本期大纲 线性表基础线性表概念数组概念 数组的基本操作数组创建和初始化查找一个元素增加一个元素修改一个元素删除一个元素 小题一道 - - 单调数组问题小题一道 - - 数组合并问题小结 线性表基础 线性表概念 我们先搞清楚几个基本概念&#xff0c;在很多地方会看到线性结构…

Adversarial attacks and defenses on AI in medical imaging informatics: A survey

Adversarial attacks and defenses on AI in medical imaging informatics: A survey----《AI在医学影像信息学中的对抗性攻击与防御&#xff1a;综述》 背景&#xff1a; 之前的研究表明&#xff0c;人们对医疗DNN及其易受对抗性攻击的脆弱性一直存在疑虑。 摘要&#xff1a;…

实力认可!安全狗入选2023年国产云原生安全技术代表厂商

10月25日&#xff0c;安全牛发布了《2023云原生安全技术应用指南》报告暨年度代表性厂商推荐。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借突出的云原生安全产品与实力成为2023年国产云原生安全技术代表厂商之一。 厦门服云信息科技有限公司&#xff08;品牌名&#xf…

04MQ消息队列

一、同步异步通讯 1.同步通讯和异步通讯 同步通讯&#xff1a;当A服务调用B服务时&#xff0c;一直等待B服务执行完毕&#xff0c;A才继续往下走 优点&#xff1a;时效性强&#xff0c;可以立即得到结果。 缺点&#xff1a; ①耦合度高&#xff0c;加入新的需求就要修改原…

计算机网络【CN】TCP报文段格式【20B】

序号&#xff1a;本报文段所发送的数据的第一个字节的序号确认号&#xff1a;期望收到对方下一个报文段的第一个数据字节的序号。 重要控制位&#xff1a; 紧急位URG&#xff1a;URG1时&#xff0c;标明此报文段中有紧急数据&#xff0c;是高优先级的数据&#xff0c;应尽快传送…

卡巴斯基2009杀毒软件

下载地址&#xff1a;https://user.qzone.qq.com/512526231/main https://user.qzone.qq.com/3503787372/main

冯诺依曼体系结构与操作系统

文章目录 1. 冯诺依曼体系结构概念理解冯诺依曼体系结构的优势 2. 操作系统概念设计OS的目的和定位如何理解管理 1. 冯诺依曼体系结构 概念 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 对于各个部…

java--跳转关键字和随机数

1.跳转关键字 break&#xff1a;跳出并结束当前所在循环的执行 continue&#xff1a;用于跳出当前循环的当次执行&#xff0c;直接进入循环的下一次执行 2.注意事项 break&#xff1a;只能用于结束所在循环&#xff0c;或结束所在switch语句的执行 continue&#xff1a;只能…

工业交换机常用功能有哪些?

工业交换机&#xff0c;又称工业以太网交换机&#xff0c;是一种在OSI参考模型的第二层&#xff08;数据链路层&#xff09;上工作的网络设备。它基于MAC地址识别&#xff0c;并能够封装和转发数据包。那么&#xff0c;工业交换机的常见常用功能有哪些呢&#xff1f; 工业交换…

JavaWeb——关于servlet种mapping地址映射的一些问题

6、Servlet 6.4、Mapping问题 一个Servlet可以指定一个映射路径 <servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern> </servlet-mapping>一个Servlet可以指定多个映射路径 <servlet-mapping>&…

Redis和Memcached网络模型详解

1. Redis单线程单Reactor网络模型 1.1 redis单线程里不能执行十分耗时的流程&#xff0c;不然会客户端响应不及时 解决方法一&#xff1a; beforesleep里删除过期键操作若存在大量过期键时&#xff0c;会耗费大量时间&#xff0c;redis采用的策略之一就是采用timelimit方案超过…

2011-2021年“第四期”数字普惠金融与上市公司匹配(根据城市匹配)/上市公司数字普惠金融指数匹配数据

2011-2021年“第四期”数字普惠金融与上市公司匹配&#xff08;根据城市匹配&#xff09;/上市公司数字普惠金融指数匹配数据 1、时间&#xff1a;2011-2021年 指标&#xff1a;指标&#xff1a;股票代码、年份、行政区划代码、行业名称、行业代码、所属省份、所属城市、数字…

澳大利亚专线的清关时效

随着全球贸易的不断发展&#xff0c;越来越多的企业和个人开始选择通过海运、空运等物流方式将货物运送到世界各地。而在这些运输方式中&#xff0c;澳大利亚专线以其快速、高效的特性受到了广大客户的青睐。本文将为您详细介绍澳大利亚专线的清关时效。 一、澳大利亚专线的定义…

Vben开源添加本地路由(不用显示在菜单的路由)

1.在src\router\routes\modules下创建一个article.ts import type { AppRouteModule } from //router/types;import { LAYOUT } from //router/constant; import { t } from //hooks/web/useI18n;export const article: AppRouteModule {path: /article,name: Article,compon…