算法:常见的链表算法

文章目录

  • 链表算法
  • 两数相加
    • 两两交换链表中的节点
    • 重排链表
    • 合并K个升序链表
    • K个一组翻转链表
  • 总结

本篇总结常见的链表算法题和看他人题解所得到的一些收获

链表算法

关于链表的算法:

  1. 画图:画图可以解决绝大部分的数据结构的问题,任何的算法题借助图都可以有一个比较清晰的思路
  2. 引入哨兵位头节点:头节点可以避免处理很多边界情况,在解决一些问题前很方便
  3. 多定义几个变量:可以很方便的解决问题
  4. 快慢指针:在处理带环的问题,环的入口,倒数节点的时候很好用

两数相加

在这里插入图片描述

在这里积累这个题的目的是学习代码的写法,学习他人的代码

这是自己的代码:

class Solution 
{
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* newhead = new ListNode();ListNode* cur1 = l1;ListNode* cur2 = l2;ListNode* cur = newhead;int carry = 0, sum = 0, ret = 0;while(cur1 && cur2){// 计算出这个节点要存的值是多少sum = cur1->val + cur2->val + carry;carry = sum / 10;ret = sum % 10;// 插入节点ListNode* newnode = new ListNode(ret);cur->next = newnode;cur = newnode;// 迭代cur1 = cur1->next;cur2 = cur2->next;}while(cur1){// 计算出这个节点要存的值是多少sum = cur1->val + carry;carry = sum / 10;ret = sum % 10;// 插入节点ListNode* newnode = new ListNode(ret);cur->next = newnode;cur = newnode;// 迭代cur1 = cur1->next;}while(cur2){// 计算出这个节点要存的值是多少sum = cur2->val + carry;carry = sum / 10;ret = sum % 10;// 插入节点ListNode* newnode = new ListNode(ret);cur->next = newnode;cur = newnode;// 迭代cur2 = cur2->next;}if(carry){// 插入节点ListNode* newnode = new ListNode(carry);cur->next = newnode;cur = newnode;}return newhead->next;}
};

运行可以通过,但是代码量比较冗余,其实可以发现while循环和后面的if语句有相当多的相似语句,因此看下面的代码:

class Solution 
{
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* newhead = new ListNode();ListNode* cur1 = l1;ListNode* cur2 = l2;ListNode* cur = newhead;int sum = 0;while(cur1 || cur2 || sum){// 先加第一个链表if(cur1){sum += cur1->val;cur1 = cur1->next;}// 再加第二个链表if(cur2){sum += cur2->val;cur2 = cur2->next;}// 处理节点数据cur->next = new ListNode(sum % 10);cur = cur->next;sum /= 10;}// 处理掉不必要的内存空间cur = newhead->next;delete newhead;return 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* swapPairs(ListNode* head) {if(head == nullptr || head->next == nullptr)return head;ListNode* newhead = new ListNode();newhead->next = head;ListNode* prev = newhead;ListNode* cur = newhead->next;ListNode* next = cur->next;ListNode* nnext = next->next;while(cur && next){// 交换节点prev->next = next;next->next = cur;cur->next = nnext;// 迭代prev = cur;cur = prev->next;if(cur) next = cur->next;if(next) nnext = next->next;}ListNode* res = newhead->next;delete newhead;return res;}
};

不带虚拟头节点:

/*** 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) {if(head == nullptr || head->next == nullptr)return head;// 先找到新的头节点ListNode* newhead = head->next;// 找到当前待交换的两个节点ListNode* left = head;ListNode* right = head->next;while(left && right){// 交换两个节点left->next = right->next;right->next = left;// 进行迭代ListNode* prev = left;left = left->next;if(left){right = left->next;if(right)prev->next = right;}}return newhead;}
};

递归解题

/*** 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) {if(head == nullptr || head->next == nullptr)return head;ListNode* left = head;ListNode* right = left->next;ListNode* tmp = right->next;right->next = left;left->next = swapPairs(tmp);return right;}
};

重排链表

在这里插入图片描述
积累本题的意义在于,本题综合了逆序链表,快慢指针,链表插入的问题,是一个综合性的题

/*** 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) {// 利用快慢双指针找到中间节点ListNode* slow = head;ListNode* fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}// 得到两个新链表ListNode* newheadleft = head;ListNode* newheadright = Reverse(slow->next);slow->next = nullptr;// 把这两个链表再组装起来ListNode* newhead = new ListNode();ListNode* tail = newhead;ListNode* cur1 = newheadleft;ListNode* cur2 = newheadright;while(cur1 || cur2){if(cur1){tail->next = cur1;tail = tail->next;cur1 = cur1->next;}if(cur2){tail->next = cur2;tail = tail->next;cur2 = cur2->next;}}ListNode* res = newhead->next;delete newhead;head = res;}ListNode* Reverse(ListNode* head){ListNode* newhead = new ListNode();ListNode* cur = head;while(cur){ListNode* next = cur->next;cur->next = newhead->next;newhead->next = cur;cur = next;}ListNode* res = newhead->next;delete newhead;return res;}
};

合并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:// 利用合成的思想解题ListNode* mergeKLists(vector<ListNode*>& lists) {ListNode* newhead = new ListNode();ListNode* tail = newhead;for(int i = 0; i < lists.size(); i++){merge(newhead->next, lists[i]);}return newhead->next;}void merge(ListNode*& head, ListNode* cur){ListNode* newhead = new ListNode();ListNode* tail = newhead;while(head && cur){if(head->val < cur->val){tail->next = head;tail = tail->next;head = head->next;}else{tail->next = cur;tail = tail->next;cur = cur->next;}}if(head) tail->next = head;if(cur) tail->next = cur;head = newhead->next;}
};

利用优先级队列的思想解题

/*** 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:struct cmp{bool operator()(ListNode* l1, ListNode* l2){return l1->val > l2->val;}};// 利用优先级队列进行优化ListNode* mergeKLists(vector<ListNode*>& lists) {priority_queue<ListNode*, vector<ListNode*>, cmp> pq;// 把每一个链表都塞进去for(int i = 0; i <lists.size(); i++){if(lists[i])pq.push(lists[i]);}// 创建虚拟头节点,开始链入到链表中ListNode* newhead = new ListNode();ListNode* tail = newhead;// 找到最小的节点,拿出来,再把它的下一个节点再塞进去,直到队列为空while(!pq.empty()){ListNode* top = pq.top();pq.pop();if(top != nullptr){ListNode* next = top->next;top->next = nullptr;// 塞到目标链表中tail->next = top;tail = tail->next;// 再把剩下的部分再塞进去if(next)pq.push(next);}}// 返回信息ListNode* res = newhead->next;delete newhead;return res;}
};

利用归并的思想解题

/*** 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* mergeKLists(vector<ListNode*>& lists) {return merge(lists, 0, lists.size() - 1);}// 归并排序ListNode* merge(vector<ListNode*>& lists, int left, int right){// 处理特殊情况if(left > right) return nullptr;if(left == right) return lists[left];// 拆分数组int midi = left + (right - left) / 2;// [left, midi][midi + 1, right]ListNode* l1 = merge(lists, left, midi);ListNode* l2 = merge(lists, midi + 1, right);// 排序return mergetwonode(l1, l2);}// 合并链表ListNode* mergetwonode(ListNode* l1, ListNode* l2){ListNode* newhead = new ListNode();ListNode* cur1 = l1, *cur2 = l2, *tail = newhead;while(cur1 && cur2){if(cur1->val <= cur2->val){tail->next = cur1;cur1 = cur1->next;tail = tail->next;}else{tail->next = cur2;cur2 = cur2->next;tail = tail->next;}}if(cur1) tail->next = cur1;if(cur2) tail->next = cur2;ListNode* res = newhead->next;delete newhead;return res;}
};

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 
{// 思路:把链表k个一组拿出来,再翻转,再链入一个新的链表中
public:ListNode* reverseKGroup(ListNode* head, int k) {ListNode* newhead = new ListNode();ListNode* tail = newhead;ListNode* left = head, *right = left;while(left && right){// 把链表k个一组分出来for(int i = 1; i < k; i++){// 如果此时已经到最后了,那么这最后一组就不需要进行翻转if(right == nullptr){tail->next = left;return newhead->next;}right = right->next;}// 断链if(right){ListNode* newleft = right->next;right->next = nullptr;// 现在从[left, right]就是一段完整的不带头节点的链表了// 把这段链表进行翻转,并链入到新的链表中tail->next = reverse(left);tail = left;// 迭代,找下一组k个节点left = newleft, right = left;}}tail->next = left;return newhead->next;}// 进行链表的反转ListNode* reverse(ListNode* cur){ListNode* newhead = new ListNode();// 把链表中的节点头插到新链表中while(cur){ListNode* next = cur->next;cur->next = newhead->next;newhead->next = cur;cur = next;}ListNode* res = newhead->next;delete newhead;return res;}
};

这个题的难点在于细节问题的处理,其实思路并不难

总结

其实对于链表这一块的算法主要是体现在需要处理一些边界情况和循环调用带来的问题,通过借助虚拟头节点和多创建几个指针可以缓解这种情况,主要是细节要处理好,对于思维的需求没有那么高

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

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

相关文章

视觉学习笔记12——百度飞浆框架的PaddleOCR 安装、标注、训练以及测试

系列文章目录 虚拟环境部署 参考博客1 参考博客2 参考博客3 参考博客4 文章目录 系列文章目录一、简单介绍1.OCR介绍2.PaddleOCR介绍 二、安装1.anaconda基础环境1&#xff09;anaconda的基本操作2&#xff09;搭建飞浆的基础环境 2.安装paddlepaddle-gpu版本1&#xff09;安装…

语言模型GPT与HuggingFace应用

受到计算机视觉领域采用ImageNet对模型进行一次预训练&#xff0c;使得模型可以通过海量图像充分学习如何提取特征&#xff0c;然后再根据任务目标进行模型微调的范式影响&#xff0c;自然语言处理领域基于预训练语言模型的方法也逐渐成为主流。以ELMo为代表的动态词向量模型开…

在线教育小程序正在成为教育行业的新生力量

教育数字化转型是目前教育领域的一个热门话题&#xff0c;那么到底什么是教育数字化转型&#xff1f;如何做好教育数字化转型&#xff1f; 教育数字化转型是利用信息技术和数字工具改变和优化教育的过程。主要特征包括技术整合、在线学习、个性化学习、大数据分析、云计算、虚拟…

【C++学习手札】基于红黑树封装模拟实现map和set

​ &#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 &#x1f49c;本文前置知识&#xff1a; 红黑树 ♈️今日夜电波&#xff1a;漂流—菅原纱由理 2:55━━━━━━️&#x1f49f;──────── 4:29 …

Appium获取toast方法封装

一、前置说明 toast消失的很快&#xff0c;并且通过uiautomatorviewer也不能获取到它的定位信息&#xff0c;如下图&#xff1a; 二、操作步骤 toast的class name值为android.widget.Toast&#xff0c;虽然toast消失的很快&#xff0c;但是它终究是在Dom结构中出现过&…

【计算机网络】HTTP请求

目录 前言 HTTP请求报文格式 一. 请求行 HTTP请求方法 GET和POST的区别 URL 二. 请求头 常见的Header 常见的额请求体数据类型 三. 请求体 结束语 前言 HTTP是应用层的一个协议。实际我们访问一个网页&#xff0c;都会像该网页的服务器发送HTTP请求&#xff0c;服务…

使用Java将图片添加到Excel的几种方式

1、超链接 使用POI&#xff0c;依赖如下 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency>Java代码如下,运行该程序它会在桌面创建ImageLinks.xlsx文件。 …

GPT-4V 在机器人领域的应用

在科技的浩渺宇宙中&#xff0c;OpenAI如一颗璀璨的星辰&#xff0c;于2023年9月25日&#xff0c;以一种全新的方式&#xff0c;向世界揭示了其最新的人工智能力作——GPT-4V模型。这次升级&#xff0c;为其旗下的聊天机器人ChatGPT装配了语音和图像的新功能&#xff0c;使得用…

『Linux升级路』进度条小程序

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、预备知识 &#x1f4d2;1.1缓冲区 &#x1f4d2;1.2回车和换行 二、倒计…

修改正点原子综合实验的NES模拟器按键控制加横屏

​​​​​​​ 开发板&#xff1a;stm32f407探索者开发板V2 屏幕是4.3寸-800-480-MCU屏 手头没有V3开发板&#xff0c;只有V2&#xff0c;所以没法测试 所以只讲修改哪里&#xff0c;请自行修改 先改手柄部分&#xff0c;把手柄改成按键 找到左边的nes文件夹中的nes_mai…

采用轨到轨输出设计 LTC6363HMS8-2、LTC6363HMS8-1、LTC6363HRD、LTC6363IDCB差分放大器I

产品详情 LTC6363 系列包括四个全差分、低功耗、低噪声放大器&#xff0c;具有经优化的轨到轨输出以驱动 SAR ADC。LTC6363 是一款独立的差分放大器&#xff0c;通常使用四个外部电阻设置其增益。LTC6363-0.5、LTC6363-1 和 LTC6363-2 都有内部匹配电阻&#xff0c;可分别创建…

C++数据结构:B树

目录 一. 常见的搜索结构 二. B树的概念 三. B树节点的插入和遍历 3.1 插入B树节点 3.2 B树遍历 四. B树和B*树 4.1 B树 4.2 B*树 五. B树索引原理 5.1 索引概述 5.2 MyISAM 5.3 InnoDB 六. 总结 一. 常见的搜索结构 表示1为在实际软件开发项目中&#xff0c;常用…

博途PLC SCL间接寻址编程应用

这篇博客里我们将要学习Pointer和Any指针&#xff0c;PEEK和POKE指令&#xff0c;当然我们还可以数组类型数据实现数组指针寻址&#xff0c;具体应用介绍请参考下面文章链接&#xff1a; https://rxxw-control.blog.csdn.net/article/details/134761364https://rxxw-control.b…

一文讲解如何从 Clickhouse 迁移数据至 DolphinDB

ClickHouse 是 Yandex 公司于2016年开源的 OLAP 列式数据库管理系统&#xff0c;主要用于 WEB 流量分析。凭借面向列式存储、支持数据压缩、完备的 DBMS 功能、多核心并行处理的特点&#xff0c;ClickHouse 被广泛应用于广告流量、移动分析、网站分析等领域。 DolphinDB 是一款…

【Hadoop_02】Hadoop运行模式

1、Hadoop的scp与rsync命令&#xff08;1&#xff09;本地运行模式&#xff08;2&#xff09;完全分布式搭建【1】利用102将102的文件推到103【2】利用103将102的文件拉到103【3】利用103将102的文件拉到104 &#xff08;3&#xff09;rsync命令&#xff08;4&#xff09;xsync…

使用 HTML 地标角色提高可访问性

请务必确保所有用户都可以访问您的网站&#xff0c;包括使用屏幕阅读器等辅助技术的用户。 一种方法是使用 ARIA 地标角色来帮助屏幕阅读器用户轻松浏览您的网站。使用地标角色还有其他好处&#xff0c;例如改进 HTML 的语义并更轻松地设置网站样式。在这篇博文中&#xff0c;我…

深度探索Linux操作系统 —— 构建initramfs

系列文章目录 深度探索Linux操作系统 —— 编译过程分析 深度探索Linux操作系统 —— 构建工具链 深度探索Linux操作系统 —— 构建内核 深度探索Linux操作系统 —— 构建initramfs 文章目录 系列文章目录前言一、为什么需要 initramfs二、initramfs原理探讨三、构建基本的init…

tomcat篇---第二篇

系列文章目录 文章目录 系列文章目录前言一、tomcat容器是如何创建servlet类实例?用到了什么原理?二、tomcat 如何优化?三、熟悉tomcat的哪些配置?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女…

Web应用JSON数据保护(密码算法、密钥、数字签名和数据加密)

1.JSON&#xff08;JavaScript Object Notation&#xff09; JSON是一种轻量级的数据交换格式&#xff0c;采用完全独立于编程语言的文本格式来存储和表示数据。JSON通过简单的key-value键值对来描述数据&#xff0c;可以被广泛用于网络通信、数据存储等各种应用场景&#xff0…

Python面向对象基础

Python面向对象基础 一、概念1.1面向对象的设计思想1.2 面向过程和面向对象1.2.1 面向过程1.2.2 面向对象1.2.3 面向过程和面向对象的优缺点 二、类和对象2.1 概念2.2 类的定义2.3 对象的创建2.3.1 类中未定义构造函数2.3.2 类中定义构造函数 2.4 类的设计 三、类中的成员3.1 变…