算法——链表

链表常用技巧

  1. 画图分析!!!!!!!!!!——直观形象,便于理解、大多数都是模拟
  2. 引入虚拟头结点(哨兵位)
    1. 典型的就是在第一个节点传空指针,此时我们如果解引用,程序直接崩掉

image.png

  1. 我们一般选择创建哨兵位头结点,这里我们哨兵位不存储数据。解释传入空链表,这里的newhead只需要特判一下即可image.png

  2. 不要吝啬空间,大胆定义变量。

    1. 我们经常会遇到这种题目,将cur插入到prev后面。途中四句是正确的顺序,在考试题目中经常会遇到将这四句顺序打乱的情况。其实我们只需要保证前两句先执行即可(五角星标志的),这样是为了防止prev找不到next。如果先执行后两句,那么prev链接cur之后,就找不到next节点

image.png

  2. 我们也可以定义next指针,这样就不需要考虑下面四句语句的执行顺序

image.png

  1. 链表快慢双指针,典型题目有:判环;找链表中环的入口;找链表中倒数第n个节点

链表中常用操作

  1. 创建一个新节点 new
  2. 尾插:先定义一个指针tail指向最后一个位置,让tail->next 指向新节点,然后将尾结点转移到新节点上。image.png
  3. 头插:让新节点的next指向newhead->next,此时如果之前给的链表为空,让新节点next指向空,不会发生访问空节点的报错。然后让newhead->next指向新节点。如果怕链表断开,可以新定义一个next指针。我们也可以利用这个方法完成逆序链表

image.pngimage.png

两数相加

两数相加

题目解析

image.png
这里给我们逆序存储链表反而有利于我们做题,我们相加时是从最低位开始加,如果题里给我们正常顺序存储的链表的话,我们还需要自己逆置。

算法原理

解法:模拟两数相加的过程

  • 这里创建一个newhead用于链接最终结果。如果这里我们不用newhead链接,我们需要先把两个链表头结点相加然后存储新的head用来记录结果(因为最后要返回链表),然后再继续往后相加。这样我们操作就多了一步。

image.png

  • 用一个变量t来标记我们的进位,然后定义两个指针模拟加法。只要cur1、cur2不为空,就把他们指向的数字移到t中,然后把t中个位数字提出来(因为数字相加可能变为两位数,牵扯到进位),然后把new一个节点7,链接到到我们的newhead后面。同理…

image.pngimage.png

代码实现

/*** 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* cur1 = l1, *cur2 = l2;ListNode* newhead = new ListNode(0); // 创建⼀个虚拟头结点,记录最终结果ListNode* prev = newhead; // 尾指针int t = 0; // 记录进位
while(cur1 || cur2 || t)
{// 先加上第⼀个链表if(cur1){t += cur1->val;cur1 = cur1->next;}// 加上第⼆个链表if(cur2){t += cur2->val;cur2 = cur2->next;}prev->next = new ListNode(t % 10);prev = prev->next;t /= 10;
}prev = newhead->next;delete newhead;return prev;}
};

两两交换链表中的节点

两两交换链表中的节点

题目解析

  • 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

image.png

算法原理

解法:循环迭代:

  • 这里同样引入一个虚拟节点newhead链接链表。如果不引入头结点,在我们交换前两个节点的时候,因为前面没有节点。所以我们要先定义一个变量找到最终要返回的头结点。因为前半部分1、2两个节点是不需要将修改将前面的指针指向2,但是后面部分3、4需要交换过后,让前面位置的指针指向4链接。这时需要写两段代码分别写两种情况。image.png
  • 不要吝啬我们的空间,定义四个指针,紫色情况是交换1、2,绿色情况是交换3、4image.png
  • 当3、4交换完成时,四个指针移动如下情况:
    • 当链表是奇数个节点,这时cur有可能访问空指针,所以我们需要终止循环image.png
    • 当链表是偶数个节点时,next指向空,需要终止循环。image.png

所以终止循环条件:cur或者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:ListNode* swapPairs(ListNode* head) {if(head == nullptr || head->next == nullptr) return head;ListNode* newHead = new ListNode(0);newHead->next = head;ListNode* prev = newHead, *cur = prev->next, *next = cur->next, *nnext =next->next;while(cur && next){
// 交换结点prev->next = next;next->next = cur;cur->next = nnext;
// 修改指针prev = cur; // 注意顺序cur = nnext;if(cur) next = cur->next;if(next) nnext = next->next;}cur = newHead->next;delete newHead;return cur;}
};

重排链表

重排链表

题目解析

  • 第一个、倒数第一个、第二个、倒数第二个image.png

算法原理

**解法:模拟——**其实很像两个链表合并,一个是正向的1、2,一个是倒向的链表4、3。恰好是原始链表的前半部分,和后半部分逆序。

所以我们的策略是:先找到链表的中点,然后把后面部分的链表逆序;然后把前面的部分和后面的部分一个一个交互链接,合并一起即可。image.png

  1. 找到链表的中间节点(⼀定要画图考虑 slow 的落点在哪⾥ )

    1. 利用快慢双指针。这里注意一下节点个数是奇数和偶数的情况。slow=slow->next;fast=fast->next->next;我们的停止循环的条件是当fast或fast->next为空。如下图分别是节点个数为奇数和偶数的情况。此时观察slow落在中点的情况:
      1. 我们可以让slow->next部分直接翻转。这里我们可能会有疑惑,难道不应该让slow后面的部分整体翻转吗?这是这道题的特殊性,我们还是用1234进行举例子,我们发现重排前和重拍后发现2和3的链接部分是不变的,所以我们可以把slow指向的这个节点大胆给第一个链表。重排后发现是不影响结果的。

image.png

  2. 我们也可以让slow所指的开始翻转(红色笔标注的)。此时无非是把slow所指的节点丢给后半部分,逆序合并后发现结果并不影响![image.png](https://img-blog.csdnimg.cn/img_convert/77049b1c441cb9b3336de5f341cee1c9.png)
  1. 用头插法,如果这里使用头插法,建议使用第一个方法(slow->next翻转)。因为最终是断开成两个链表,
    1. 如果从slow所指的位置开始逆置,会发现箭头所指的位置指针无法断开。有可能出现意想不到的错误;

image.png

  2. 当我们选择slow->next位置开始翻转,我们只需让slow所指的位置置空即可,

image.png

  3. 如果我们只能用slow位置开始翻转的情况时,我们可以引入一个虚拟头结点newhead(哨兵位),去链接head,然后在快慢指针找中间节点,之前是slow位置,就会变成slow->next,这时候也能将两个链表断开。
  1. 把slow后面部分逆序——头插法(使用虚拟头结点)
    • 把slow后面的节点,头插到新建的头(哨兵位)结点后面。当让cur头插到newhead后面时,先让cur->next指向空(就是newhead2后面的节点),然后newhead2->next指向cur,此时newhead2就与null(原先后面的节点)断开
    • 当newhead2后面有节点时,按照上面的操作我们会发现,newhead2会与后面的断开了,所以在刚开始头插时我们要定义一个next指针标记newhead2后面的节点。当执行完上面操作后,让cur=nextimage.png
  2. 合并两个链表
    1. 我们是创建一个虚拟节点,让他们都尾插在这后面
    2. cur1相较于cur2会长一些,所以循环判空条件为cur1为空。

代码实现

/*** 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) {// 处理边界情况if(head == nullptr || head->next == nullptr || head->next->next == nullptr){return ;}// 1. 找到链表的中间节点 - 快慢双指针(⼀定要画图考虑 slow 的落点在哪⾥)ListNode* slow = head, *fast = head;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}// 2. 把 slow 后⾯的部分给逆序 - 头插法ListNode* head2 = new ListNode(0);ListNode* cur = slow->next;slow->next = nullptr; // 注意把两个链表给断开   while(cur){ListNode* next = cur->next;cur->next = head2->next;head2->next = cur;cur = next;}// 3. 合并两个链表 - 双指针ListNode* ret = new ListNode(0);ListNode* prev = ret;ListNode* cur1 = head, *cur2 = head2->next;while(cur1){
// 先放第⼀个链表prev->next = cur1;cur1 = cur1->next;prev = prev->next;// 再放第⼆个链表if(cur2){prev->next = cur2;prev = prev->next;cur2 = cur2->next;}}
delete head2;
delete ret;
}
};

合并k个升序链表

合并k个升序链表

题目解析

image.png

算法原理

**解法一:暴力解法:**逐个合并.其中n是链表的长度,k为链表的个数image.png

解法二:优先级队列做优化
我们可以直接定义k个指针,仿照合并k个有序链表,把这些链表中较小的头结点放到newhead后面,放完之后右移,继续比较各个节点最小的,把最小的连接在newhead后面即可。这样只需要遍历一遍链表即可。O(n*k)是理想状态下的时间复杂度,我们还要找出k个节点中最小的节点。这里我们可以利用优先级队列

把k个节点丢入小根堆里,经过排序后,堆顶元素即为最小的元素,链接到newhead后面后,将移动后下一个节点放入小根堆里。这里时间复杂度O(nklogk)
image.png

解法三:分治——递归
流程:

  1. 特判,如果题⽬给出空链表,⽆需合并,直接返回;
  2. 返回递归结果。


递归函数设计:

  1. 递归出⼝:如果当前要合并的链表编号范围左右值相等,⽆需合并,直接返回当前链表;
  2. 应⽤⼆分思想,等额划分左右两段需要合并的链表,使这两段合并后的⻓度尽可能相等;
  3. 对左右两段分别递归,合并[l,r]范围内的链表
  4. 再调⽤mergeTwoLists函数进⾏合并(就是合并两个有序链表)

时间复杂度:一共k个链表,每个链表里面有n个节点,每一层我们都平均分,即高度为logk,每一个节点都执行了logk次合并(这棵树的高度次)image.png

代码实现

class Solution
{
struct cmp{bool operator()(const ListNode* l1, const ListNode* l2){return l1->val > l2->val;}};public:
ListNode* mergeKLists(vector<ListNode*>& lists)
{// 创建⼀个⼩根堆priority_queue<ListNode*, vector<ListNode*>, cmp> heap;// 让所有的头结点进⼊⼩根堆for(auto l : lists)if(l) heap.push(l);// 合并 k 个有序链表
ListNode* ret = new ListNode(0);
ListNode* prev = ret;
while(!heap.empty()){ListNode* t = heap.top();heap.pop();prev->next = t;prev = t;if(t->next) heap.push(t->next);}prev = ret->next;
delete ret;
return 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* 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];// 1. 平分数组int mid = left + right >> 1;// [left, mid] [mid + 1, right]// 2. 递归处理左右区间ListNode* l1 = merge(lists, left, mid);ListNode* l2 = merge(lists, mid + 1, right);// 3. 合并两个有序链表return mergeTowList(l1, l2);}ListNode* mergeTowList(ListNode* l1, ListNode* l2){if(l1 == nullptr) return l2;if(l2 == nullptr) return l1;// 合并两个有序链表ListNode head;ListNode* cur1 = l1, *cur2 = l2, *prev = &head;head.next = nullptr;while(cur1 && cur2){if(cur1->val <= cur2->val){prev = prev->next = cur1;cur1 = cur1->next;}else{prev = prev->next = cur2;cur2 = cur2->next;}}if(cur1) prev->next = cur1;if(cur2) prev->next = cur2;return head.next;}
};

k个一组翻转链表

k个一组翻转链表

题目解析

链表中的节点每k个一组进行翻转,如果剩余的链表不够k个,则不翻转image.png

算法原理

解法:模拟

  1. 先求出需要逆序多少组n:遍历链表一遍 n=链表节点个数/k
  2. 重复n次长度为k的链表的逆序
    1. 利用头插法逆序,创建一个newhead。头插时,cur->next=head->next;head->next = cur;此时右边节点(2)会丢失,需要先用一个next指针记录一下节点,然后头插。头插完之后cur++,直至头插3之后,这一组完成。

image.png

  1. 进行下一组头插时,需要将4头插到1的后面,所以需要一个指针标记一下1的位置,这个位置是每一组开始头插的第一个位置。所以每组第一个位置开始头插的时候,要标记好开始头插的位置(tmp),还需要定义个prev标记将要头插位置的前驱,每当一组头插完,该进行下一组时,让prev更新到tmp位置。image.pngimage.png

代码实现

/*** 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) {// 1. 先求出需要逆序多少组int n = 0;ListNode* cur = head;while(cur){cur = cur->next;n++;}n /= k;// 2. 重复 n 次:⻓度为 k 的链表的逆序即可ListNode* newHead = new ListNode(0);ListNode* prev = newHead;cur = head;for(int i = 0; i < n; 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;}// 把不需要翻转的接上prev->next = cur;cur = newHead->next;delete newHead;return cur;}
};

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

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

相关文章

智能优化算法应用:基于沙猫群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于沙猫群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于沙猫群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.沙猫群算法4.实验参数设定5.算法结果6.参考文…

javaweb初体验

javaweb初体验 文章目录 javaweb初体验前言一、流程&#xff1a;1.创建Maven的父工程2.创建Maven&#xff0c;Webapp的子工程3.在pom.xml文件中添加依赖&#xff08;父工程与子工程共用&#xff09;4.写一个helloservlet类实现httpservlet接口&#xff0c;重写doget&#xff0c…

idea中终端Terminal页面输入命令git log后如何退出

1、idea中Terminal输入命令git log后如何退出&#xff1f; 2、解决 输入q键会自动退出git log命令

【Java系列】多线程案例学习——基于阻塞队列实现生产者消费者模型

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习JavaEE的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录…

ubuntu 在线安装 python3 pip

ubuntu 在线安装 python3 pip 安装 python3 pip sudo apt -y install python3 python3-pip升级 pip python3 -m pip install --upgrade pip

【头歌实训】kafka-入门篇

文章目录 第1关&#xff1a;kafka - 初体验任务描述相关知识Kafka 简述Kafka 应用场景Kafka 架构组件kafka 常用命令 编程要求测试说明答案代码 第2关&#xff1a;生产者 &#xff08;Producer &#xff09; - 简单模式任务描述相关知识Producer 简单模式Producer 的开发步骤Ka…

ROS多机通信

1&#xff1a;安装ssh sudo apt-get install openssh-server ps -e|grep ssh2&#xff1a;网络静态IP设置 3&#xff1a;配置文件修改 sudo gedit /etc/hosts192.168.3.11 用户名 192.168.3.22 用户名另一台4&#xff1a;重启网络 sudo /etc/init.d/network-manager resta…

2023年度业务风险报告:四个新风险趋势

目录 倒票的黄牛愈加疯狂 暴增的恶意网络爬虫 愈加猖獗的羊毛党 层出不穷的新风险 业务风险呈现四个趋势 防御云业务安全情报中心“2023年业务风险数据”统计显示&#xff0c;恶意爬虫风险最多&#xff0c;占总数的37.8%&#xff1b;其次是虚假账号注册&#xff0c;占18.79%&am…

MySQL事务、四大原则、执行步骤、四种隔离级别、锁、脏读、脏写等

MySQL事务 MySQL事务1.什么是事务&#xff1f;2.事务的四大原则3.事务执行的步骤4、事务的隔离性5、MySQL中的锁 MySQL事务 模拟一个转账业务&#xff1a; 上图中的sql语句&#xff1a; update from table set money mongey - 100 where name A; update from table set mone…

RabbitMQ 报错:Failed to declare queue(s):[QD, QA, QB]

实在没想到会犯这种低级错误。 回顾整理一下吧&#xff1a; 原因&#xff1a;SpringBoot主配置类默认只会扫描自己所在的包及其子包下面的组件。其他位置的配置不会被扫描。 如果非要使用其他位置&#xff0c;就需要在启动类上面指定新的扫描位置。注意新的扫描位置会覆盖默…

PHP的Laravel的数据库迁移

1.默认迁移文件 2.数据库迁移 在终端输入以下代码 php artisan migrate 我的报错啦&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 数据库里面只有两张表&#xff0c;实际上应该有四张的&#xff01;&#xff01;&#xff01; 解决方法&#xff1a; 反正表已…

基于动态窗口的航线规划

MATLAB2016b可以运行 % ------------------------------------------------------------------------- % File : DWA 算法 % Discription : Mobile Robot Motion Planning with Dynamic Window Approach % Author :Yuncheng Jiang % License : Modified BSD Software License A…

【JDK21】详解虚拟线程

目录 1.概述 2.虚拟线程是为了解决哪些问题 2.1.线程切换的巨大代价 2.2.哪些情况会造成线程的切换 2.3.线程资源是有限的 3.虚拟线程 4.适用场景 1.概述 你发任你发&#xff0c;我用JAVA8&#xff1f;JDK21可能要对这句话say no了。 现在Oracle JDK是每4个版本&#x…

什么是https证书?

HTTPS证书&#xff0c;也称为SSL&#xff08;Secure Sockets Layer&#xff09;证书或TLS&#xff08;Transport Layer Security&#xff09;证书&#xff0c;是一种数字证书&#xff0c;用于在网络上建立安全的加密连接。它的主要目的是确保在互联网上进行的数据传输的安全性和…

工具系列:TimeGPT_(6)同时预测多个时间序列

TimeGPT提供了一个强大的多系列预测解决方案&#xff0c;它涉及同时分析多个数据系列&#xff0c;而不是单个系列。该工具可以使用广泛的系列进行微调&#xff0c;使您能够根据自己的特定需求或任务来定制模型。 # Import the colab_badge module from the nixtlats.utils pac…

AD使用的一些基本知识

主页工厂打板时&#xff0c;有些过孔要求在0.3/0.5以上&#xff0c;还有其他一些工艺要求也要注意 用keep-out layer还是mechanical layer 当做切割边线&#xff0c;都可以&#xff0c;也可以看制版工厂的要求 导出BOM表时&#xff0c;是以comment分类的&#xff0c;通常情况…

php-ssrf

漏洞描述&#xff1a; SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。 一般情况下&#xff0c;SSRF攻击的目标是从外网无法访问的内部系统。&#xff08;正是因为它是由服务端发起的&#xff0c;所以它能够请求…

FTP原理与配置

FTP是用来传送文件的协议。使用FTP实现远程文件传输的同时&#xff0c;还可以保证数据传输的可靠性和高效性。 FTP的应用 FTP 提供了一种在服务器和客户机之间上传和下载文件的有效方式。在企业网络中部署一台FTP服务器&#xff0c;将网络设备配置为FTP客户端&#xff0c;则可…

大数据开发之Sqoop详细介绍

测试环境 CDH 6.3.1 Sqoop 1.4.7 一.Sqoop概述 Apache Sqoop&#xff08;SQL-to-Hadoop&#xff09;项目旨在协助RDBMS与Hadoop之间进行高效的大数据交流。用户可以在 Sqoop 的帮助下&#xff0c;轻松地把关系型数据库的数据导入到 Hadoop 与其相关的系统 (如HBase和Hive)中&…

Android : 画布绘制矩形和文字 让其居中显示简单应用

示例图&#xff1a; CenterView.java package com.example.demo;import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.Log; import android.view.View;public class Center…