算法_链表专题---持续更新

news2024/11/28 2:33:12

文章目录

  • 前言
  • 两数相加
    • 题目要求
    • 题目解析
    • 代码如下
  • 两两交换链表中的结点
    • 题目要求
    • 题目解析
    • 代码如下
  • 重排链表
    • 题目要求
    • 题目解析
    • 代码如下
  • 合并K个升序链表
    • 题目要求
    • 题目解析
  • K个一组翻转链表
    • 题目要求
    • 题目解析
    • 代码如下

前言

本文将记录leetcode链表算法题解,包含题目有:两数相加、两两交换链表中的节点、重排链表、合并K个升序链表、K个一组翻转链表

两数相加

https://leetcode.cn/problems/add-two-numbers/

题目要求

在这里插入图片描述

题目解析

已经给你两个逆序的链表,如果不是给逆序的,还需要自己逆序一下,因为加法是从低位到高位相加的,重点是低位到高位的过程中可能会有进位,关键的就是这个进位,逆序后(就相当于正常顺序的低位开始相加,因为我们的逻辑就是从链表的头部开始加,依次遍历向后走),链表从头部依次相加向后走,有进位进位即可 题目给的两个链表是已经逆序过的,包括最后的要求结果也是逆序的,不需要做任何修改

在这里插入图片描述

代码如下

/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* newhead = new ListNode(0);   //新链表
        ListNode* tail = newhead;    //尾节点
        ListNode * cur1 = l1;
        ListNode * cur2 = l2;
        int ret = 0;    //进位
        //注意这种情况:当两个链表节点都为空时,进位不为空,需要进位
        while(cur1 || cur2 || ret)
        {
            //第一个链表中节点不为空
            if(cur1)
            {
                ret += cur1->val;
                cur1 = cur1->next;
            }
            //第二个链表中节点不为空
            if(cur2)
            {
                ret += cur2->val;
                cur2 = cur2->next;
            }
            tail->next = new ListNode(ret % 10);
            tail = tail->next;
            ret /= 10;  //进位
        }
        //释放new出的内存
        tail = newhead->next;
        delete newhead;
        return tail;
    }
};

两两交换链表中的结点

https://leetcode.cn/problems/swap-nodes-in-pairs/description/

题目要求

在这里插入图片描述

题目解析

题目要求两两交换相邻的节点,且不能修改节点的值,我们只需要改变节点的next指针的指向即可,为了方便两两交换我们引入一个哨兵位(当交换1、2节点时,只需要让哨兵位->next指向2这个节点....省略,这样方便很多) 要求是两两交换,实际上会影响到四个节点,哨兵位、cur、next、nnext,因为交换节点的时候,我们需要修改对应的next指向

在这里插入图片描述

当需要进行下一次两两交换的时候,先把prev向后移动两位
因为进行3、4节点交换的时候,1节点的next会指向4节点了
因此我们需要一个prev

代码如下

/**
 * 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(0);    //虚拟头节点
        dummyHead->next = head;
        ListNode* prev = dummyHead;
        //cur、next不为空
        while(prev->next !=nullptr && prev->next->next != nullptr)
        {
            ListNode* cur = prev->next;
            ListNode* next = cur->next;
            ListNode* nnext = next->next;
            //两两交换(cur、next)
            prev->next = next;
            next->next = cur;
            cur->next = nnext;
            //向后移动
            prev = prev->next->next;
        }
        return dummyHead->next;
    }
};

重排链表

https://leetcode.cn/problems/reorder-list/description/

题目要求

在这里插入图片描述

题目解析

这道题本质上是一道模拟题,根据题目以及示例模拟出设计过程,这道题比较综合,会运用到链表的中间节点、反转链表这两道基础题的方法
模拟
找到中间节点,分割成两个链表,并将后面一个链表反转
按照先添加第一个链表的第一个节点,再添加第二个链表的第一个节点,先添加第一个链表的第二个节点
再添加第二个链表的第二个节点的顺序以此类推
在这里插入图片描述

1、首先利用快慢指针找到中间位置(快指针一次前进两步,慢指针一步,这样快指针每次都是慢指针的二倍,当快指针走到链表结尾时,慢指针就走到中间位置)
2、链表分割,这里非常重要,我第一次做的时候就忘记将链表断开了,记得将第一个链表的结尾节点的next置空,这里的逻辑是将slow->next位置开始作为第二个链表(不包含当前slow指针指向的节点)
当然也可以用包含slow以及后面的所有节点作为第二个链表
3、链表反转,也就是逆序,不断地将节点头插到新空间即可
4、按照模拟顺序依次重组链表
由于将slow->next位置开始作为第二个链表,所以无论是奇数个还是偶数个
节点,都是第一个链表长于第二个链表,因此当重组的时候,循环条件是第一个链表节点不为空,这样当第一个链表节点出现为空的时候,第二个链表肯定早早就结束了,就可以重组所有节点了
以下是使用快慢双指针找中间节点(slow指针指向的位置)的奇数以及偶数讨论
在这里插入图片描述

代码如下

/**
 * 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:
    void reorderList(ListNode* head) 
    {
        //当链表节点数为1或2时,直接返回
        if(head->next == nullptr || head->next->next == nullptr) return;
        //利用快慢指针找到中间位置
        ListNode* fast = head;
        ListNode* slow = head;
        //链表中的节点为奇数/偶数
        while(fast&&fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //分割断开为两个链表
        ListNode* cur = slow->next;
        slow->next = nullptr;
        //翻转第二个链表
        ListNode* head2 = new ListNode(0);
        ListNode* curr = cur->next; //保存下一个节点
        while(cur)
        {            
            curr = cur->next;
            cur->next = head2->next;
            head2->next = cur;
            cur = curr;
        }
        ListNode* ret = new ListNode(0);
        ListNode* prev = ret;
        ListNode* cur1 = head;
        ListNode* cur2 = head2->next;
        //分割时按照slow->next慢指针的后一个节点进行分割,所以第一个链表是最长的,第一个链表遍历完毕就结束
        while(cur1)
        {
            prev->next = cur1;
            prev = prev->next;
            cur1 = cur1->next;
            if(cur2)
            {
                prev->next = cur2;
                prev = prev->next;
                cur2 = cur2->next;
            }
        }
        head = ret;
    }
};

合并K个升序链表

https://leetcode.cn/problems/merge-k-sorted-lists/

题目要求

在这里插入图片描述

题目解析

合并K个升序链表,我们自然能想到使用合并两个升序链表的方法来解决问题,但是这样事件复杂度是比较高的。 使用优先级队列,创建一个小根堆,先将每个链表的头节点放入优先级队列中,然后取出堆顶的节点(堆顶的节点就是当前堆中所有元素最小的),取出这个节点后,这个节点必然是属于某个链表的,在将该节点后一个节点加入优先级队列中(这样就可以做到所有链表的节点都能比较一遍),循环往复,持续地取出堆顶的元素
/**
 * 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:
	//C++标准库不提供对自定义数据类型的排序规则(ListNode),所以需要重载operator()
    struct cmp
    {
        bool operator()(const ListNode* l1, const ListNode* l2)
        {
            return l1->val > l2->val;
        }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        //创建一个小根堆(根据给定的比较规则自动对元素进行排序)
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap;
        //将所有链表的头节点先添加入小根堆中
        for(auto l : lists)
        {
            //链表可能为空
            if(l)
            heap.push(l);
        }
        ListNode* ret = new ListNode(0);    //虚拟头节点
        ListNode* prev = ret;
        while(!heap.empty())
        {
            ListNode* t = heap.top();
            prev->next = t;
            heap.pop();
            prev = prev->next;  //更新
            if(t->next) heap.push(t->next); //将每个链表都向后推进
        }
        prev = ret->next;
        delete ret;
        return prev;
    }
};

K个一组翻转链表

https://leetcode.cn/problems/reverse-nodes-in-k-group/description/

题目要求

在这里插入图片描述

题目解析

这道题不需要什么技巧,只需要把过程模拟出来就好,首先读题,k个节点为一组,并按照一组为单位进行逆序,那么我们需要先将总节点的个数算出,再计算出一共需要逆序多少组,两个循环就可以搞定
for(逆序多少组)
{
	for(每一组需要逆序多少个节点) {}
}
最后将剩下不需要逆序的节点接在最后面

逆序逻辑
在这里插入图片描述
在这里插入图片描述
注意
当逆序到下一组的时候,我们是需要提前保存第一组逆序的第一个节点的,ListNode* tmp = cur,因为第一个
节点逆序后一定是在第一组的最后一个位置(紧接第二组)
在这里插入图片描述

代码如下

/**
 * 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* reverseKGroup(ListNode* head, int k) 
    {
        //遍历链表计算总节点数
        ListNode* num = head;
        int n = 0;  //总节点数
        while(num)
        {
            num = num->next;
            n++;
        }
        int group = n/k;
        ListNode* dummyHead = new ListNode(0);  //虚拟头节点
        ListNode* prev = dummyHead; //cur的前一个节点
        ListNode* cur = head;    //当前节点
        for(int i = 0; i < group; i++)
        {
            ListNode* tmp = cur;    //保存前一个位置
            for(int j = 0; j < k; j++)
            {
                ListNode *next = cur->next; //保存下一个节点
                cur->next = prev->next;
                prev->next = cur;
                cur = next;
            }
            prev = tmp; //更新下一组逆序的前一个位置
        }
        //加上不需逆序的节点
        if(cur) prev->next = cur;
        //释放,防止内存泄漏
        prev = dummyHead->next;
        delete dummyHead;
        return prev;
    }
};

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

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

相关文章

Why Memory Matters?(记忆力为何如此重要?)

What is memory? The general consensus is that memory is a multitude of cognitive systems which allow us to store information for certain periods of time so that we can learn from our past experiences and predict the future. 什么是记忆?人们普遍的共识是&am…

《PostgreSQL 数据库在国内的发展前景》

从DB-engines这张2024年8月的最新排名图上可以看出&#xff0c;PostgreSQL数据库的发展趋势还是非常好的&#xff0c;在国内&#xff0c;PostgreSQL数据库也展现出令人振奋的发展前景&#xff0c;非常明显的一种表现就是腾讯云、人大金仓、阿里云、华为等众多厂商都有基于Postg…

推荐一个uniapp选择文件上传的插件

插件地址&#xff1a;文件选择、文件上传组件&#xff08;图片&#xff0c;视频&#xff0c;文件等&#xff09; - DCloud 插件市场 支持 H5 / App / 微信小程序

警惕!SCI投稿也有“假网址”!3秒教你查询正确的期刊官网网址

【SciencePub学术】很多没有发表过SCI论文的学者&#xff0c;对于投稿是非常陌生的。首先第一步&#xff0c;对于寻找正确的SCI/SSCI期刊官网都是一项难题。 01 假网站泛滥 • 目前市面上很多假的期刊官网&#xff0c;甚至于界面都所差无几&#xff0c;但是网址仅仅相差一个“…

【两周年纪念日】我将竭尽全力,只为和最美丽的自己早日汇合

​ 您好&#xff0c;我是程序员小羊&#xff01; 存在有其原因&#xff0c;经历有其始终&#xff0c;年华有其始末&#xff0c;拼搏要有结果。 2023来去匆匆&#xff0c;2024奋斗始终&#xff0c;献出一份感情&#xff0c;收获一份心情&#xff0c;拼出一段经验&#xff0c;收获…

两个方法 搞定伦敦金涨跌预测

受美联储降息预期和地缘局势紧张的关系影响&#xff0c;近期伦敦金价格再次出现了强势的上涨&#xff0c;盘中攀升超过30美元。这波涨势的出现&#xff0c;实在是在很多人的意料之外&#xff0c;那么下一步投资者就要开始考虑伦敦金的上涨的终点在哪里&#xff1f;实际上这就是…

计算机组成原理 - 中央处理器

中央处理器 考纲内容 CPU的功能和基本结构指令执行过程数据通路的功能和基本结构控制器的功能和工作原理异常和中断机制 异常和终端的基本概念&#xff1b;异常和中断的分类&#xff1b;异常和中断的检测与响应指令流水线 指令流水线的基本概念&#xff1b;指令流水线的基本实…

动态规划:买卖股票系列

目录 1. 买卖股票的最佳时机1-只能买卖一次(LeetCode121) 解法1&#xff1a;暴力解法 解法2&#xff1a;贪心算法 解法3&#xff1a;动态规划 2. 买卖股票的最佳时机2-可以买卖多次(LeetCode122) 解法1&#xff1a;贪心算法 解法2&#xff1a;动态规划 3. 买卖股票的最…

【架构设计】软件设计原则中的7种耦合和内聚(详解)

文章目录 一、前言二、内聚1、定义2、7 种内聚类型及其描述3、设计要求 三、耦合1、定义2、7 种耦合类型及其描述3、设计要求 四、总结 一、前言 耦合&#xff08;Coupling&#xff09;和内聚&#xff08;Cohesion&#xff09;是衡量软件模块设计质量的两个非常重要的概念。高…

2024实验班选拔考试(热身赛)

比赛传送门 邀请码&#xff1a;2024wksyb A. 简单的数列问题 签到&#xff0c;记得开long long。 #include<bits/stdc.h> #define rep(i,a,b) for (int ia;i<b;i) #define per(i,a,b) for (int ia;i>b;--i) #define se second #define fi first #define endl …

linux进程篇总结——实战——自定义shell

前言&#xff1a;经过过去两章十二篇文章的学习&#xff0c;我们已经知道了进程的基本概念以及进程的控制方法。 本篇内容就是使用过去学习的内容自己写一个功能简单的shell外壳程序&#xff0c; 也就是我们使用的bash命令行。 本篇内容是过去进程知识的集大成者。 我们在这个实…

智慧能源管理:助力公共机构节能增效

一、背景&#xff1a; 在全球倡导绿色发展、节能减排的时代浪潮下&#xff0c;公共机构作为社会服务的重要提供者&#xff0c;能源消耗量大&#xff0c;特别是在照明方面能源消耗问题尤为突出。从政府办公楼的日常照明&#xff0c;到学校教室的学习照明&#xff0c;再到医院走…

计算机组成原理 - 存储系统

存储系统 考纲内容 存储器的分类层次化存储器的基本结构半导体随机存储器(RAM) SRAM、DRAM、Flash存储器主存储器 DRAM芯片和内存条、多模块存储器、主存储器和CPU之间的连接外部存储器 磁盘存储器、固态硬盘(SSD)高速缓冲存储器(Cache) Cache的基本原理&#xff1a;Cache和主…

解读智慧车间生产线的智慧大脑:ARMxy 工业计算机边缘控制器

在现代工业制造中&#xff0c;智慧车间生产线的建设已成为提高生产效率、降低成本、提升产品质量的关键。而 ARMxy 工业计算机边缘控制器作为智慧车间生产线的智慧大脑&#xff0c;正发挥着越来越重要的作用。 ARMxy 工业计算机边缘控制器是一种基于 ARM 架构的嵌入式工业计算机…

JavaWeb基础1:HTML/CSS/JS/HTTP

JavaWeb基础1&#xff1a;HTML/CSS/JS/HTTP (qq.com)

C-sharp-console-gui-framework:C#控制台应用程序的GUI框架

推荐一个.Net开源项目&#xff0c;方便我们基于控制台创建图形用户界面&#xff08;GUI&#xff09;应用程序。 01 项目简介 ConsoleGUI是一个简单的布局驱动.NET框架&#xff0c;用于创建基于控制台的GUI应用程序。 核心功能&#xff1a; **布局驱动&#xff1a;**与WPF或H…

WCN7851 WIFI7适配RK3588实战

一、平台信息 平台:触觉智能IDO-EVB3588-V1 WIFI模组:欧飞信O7851PM Kernel版本:GNU/Linux 5.10.110 aarch64 系统版本:Ubuntu 20.04.6 LTS 搭载RK3588高性能SOC,集成了四核Cortex-A76和四核Cortex-A55 CPU,主频高达2.4G O7851PM与开发板连接实物图如下,模块通过M.2转…

ctfhub文件上传

⽆验证 上传⼀句话⽊⻢&#xff0c;发现上传成功 1.php ⼀句话⽊⻢内容&#xff1a; <?php eval($_POST[cmd]);?> 上传⼀句话⽊⻢&#xff0c;发现上传成功 http://challenge-8b27d18368ecc25c.sandbox.ctfhub.com:10800/upload/1.ph p 前端验证 开启题⽬ 上传⼀个…

学习日志8.5--ARP攻击与防范

目录 ARP欺骗攻击 ARP泛洪防范&#xff08;动态ARP检测&#xff09; ARP欺骗攻击 ARP中间人攻击&#xff0c;中间人可以通过交换机查询交换表获取主机和网关的IP地址信息中间者通过ARP的查询可以知道PC2的IP地址和MAC地址&#xff0c;知道R2的IP地址和MAC地址&#xff0c;攻…

CVE-2023-1313

开启靶场 url访问/install来运行安装 http://eci-2ze0wqx38em0qticuhug.cloudeci1.ichunqiu.com/install/ 得知其用户和密码为admin 登录 查找文件上传位置 上传一句话木马文件 <?php echo phpinfo();eval($_POST[flw]);?> 下载查看上传木马路径 复制路径 /storag…