【优选算法】(第二十七篇)

news2025/1/17 6:03:29

目录

重排链表(medium)

题目解析

讲解算法原理

编写代码

合并K个升序链表(hard)

题目解析

讲解算法原理

编写代码


重排链表(medium)

题目解析

1.题目链接:. - 力扣(LeetCode)

2.题目描述

给定⼀个单链表 L 的头节点 head ,单链表 L 表⽰为:L(0)→L(1)→…→L(n-1)→L(n)
请将其重新排列后变为:
L(0)→L(n)→L(1)→L(n-1)→L(2)→L(n-2)→…
不能只是单纯的改变节点内部的值,⽽是需要实际的进⾏节点交换。
⽰例1:

输⼊:head=[1,2,3,4]
输出:[1,4,2,3]
⽰例2:


输⼊:head=[1,2,3,4,5]
输出:[1,5,2,4,3]

提⽰:
• 链表的⻓度范围为 [1, 5 * 10(4)]
• 1 <= node.val <= 1000

讲解算法原理

解法:
算法思路:

画图画图画图,重要的事情说三遍~
1. 找中间节点;
2. 中间部分往后的逆序;
3. 合并两个链表

编写代码

c++算法代码:

/**
 * 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;
 }
};

java算法代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 * int val;
 * ListNode next;
 * ListNode() {}
 * ListNode(int val) { this.val = val; }
 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution
{
 public void reorderList(ListNode head) 
 {
 // 处理边界情况
 if(head == null || head.next == null || head.next.next == null) return;
 // 1. 找链表的中间节点 - 快慢双指针(⼀定要画图分析 slow 的落点)
 ListNode slow = head, fast = head;
 while(fast != null && fast.next != null)
 {
 slow = slow.next;
 fast = fast.next.next;
 }
 // 2. 把 slow 后⾯的部分给逆序 - 头插法
 ListNode head2 = new ListNode(0);
 ListNode cur = slow.next;
 slow.next = null; // 把两个链表分离
 while(cur != null)
 {
 ListNode next = cur.next;
 cur.next = head2.next;
 head2.next = cur;
 cur = next;
 }
 // 3. 合并两个链表 - 双指针
 ListNode cur1 = head, cur2 = head2.next;
 ListNode ret = new ListNode(0);
 ListNode prev = ret;
 while(cur1 != null)
 {
 // 先放第⼀个链表
 prev.next = cur1;
 prev = cur1;
 cur1 = cur1.next;
 // 在合并第⼆个链表
 if(cur2 != null)
 {
 prev.next = cur2;
 prev = cur2;
 cur2 = cur2.next;
 }
 }
 }
}

合并K个升序链表(hard)

题目解析

1.题目链接:. - 力扣(LeetCode)

2.题目描述

给你⼀个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到⼀个升序链表中,返回合并后的链表。
⽰例1:

输⼊:lists=[[1,4,5],[1,3,4],[2,6]]

输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:[
1->4->5,
1->3->4,
2->6
]
将它们合并到⼀个有序链表中得到。1->1->2->3->4->4->5->6
⽰例2:输⼊:lists=[]输出:[]
⽰例3:输⼊:lists=[[]]输出:[]

提⽰:k==lists.length0<=k<=10^40<=lists[i].length<=500-10^4<=lists[i][j]<=10^4lists[i]按升序排列
lists[i].length的总和不超过10^4

讲解算法原理
解法⼀(利⽤堆):

算法思路:

合并两个有序链表是⽐较简单且做过的,就是⽤双指针依次⽐较链表 1 、链表 2 未排序的最⼩元素,选择更⼩的那⼀个加⼊有序的答案链表中。
合并 K 个升序链表时,我们依旧可以选择 K 个链表中,头结点值最⼩的那⼀个。那么如何快速的得到头结点最⼩的是哪⼀个呢?⽤堆这个数据结构就好啦~
我们可以把所有的头结点放进⼀个⼩根堆中,这样就能快速的找到每次 K 个链表中,最⼩的元素是哪个。

解法⼆(递归/分治):


算法思路:
逐⼀⽐较时,答案链表越来越⻓,每个跟它合并的⼩链表的元素都需要⽐较很多次才可以成功排序。⽐如,我们有8个链表,每个链表⻓为100。
逐⼀合并时,我们合并链表的⻓度分别为(0,100),(100,100),(200,100),(300,100),(400,100),(500,100),(600,100),(700,100)。所有链表的总⻓度共计3600。
如果尽可能让⻓度相同的链表进⾏两两合并呢?这时合并链表的⻓度分别是(100,100)x4,(200,200)x2,(400,400),共计2400。⽐上⼀种的计算量整整少了1/3。
迭代的做法代码细节会稍多⼀些,这⾥给出递归的实现,代码相对简洁,不易写错。
算法流程:
1. 特判,如果题⽬给出空链表,⽆需合并,直接返回;
2. 返回递归结果。
递归函数设计:
1. 递归出⼝:如果当前要合并的链表编号范围左右值相等,⽆需合并,直接返回当前链表;2. 应⽤⼆分思想,等额划分左右两段需要合并的链表,使这两段合并后的⻓度尽可能相等;3. 对左右两段分别递归,合并[l,r]范围内的链表;
4. 再调⽤mergeTwoLists函数进⾏合并(就是合并两个有序链表)

编写代码
解法一代码:

c++算法代码:

/**
 * 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
{
 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;
 }
};

java算法代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 * int val;
 * ListNode next;
 * ListNode() {}
 * ListNode(int val) { this.val = val; }
 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution
{
 public ListNode mergeKLists(ListNode[] lists) 
 {
 PriorityQueue<ListNode> heap = new PriorityQueue<>((v1, v2) -> v1.val 
- v2.val);
 // 将所有头结点加⼊到⼩根堆中
 for(ListNode l : lists)
 if(l != null)
 heap.offer(l);
 
 // 合并
 ListNode ret = new ListNode(0);
 ListNode prev = ret;
 while(!heap.isEmpty())
 {
 ListNode t = heap.poll();
 prev.next = t;
 prev = t;
 if(t.next != null) heap.offer(t.next);
 }
 return ret.next;
 }
}
解法二代码:

c++算法代码:

/**
 * 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;
 }
};

java算法代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 * int val;
 * ListNode next;
 * ListNode() {}
 * ListNode(int val) { this.val = val; }
 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution
{
 public ListNode mergeKLists(ListNode[] lists) 
 {
 return merge(lists, 0, lists.length - 1);
 }
 public ListNode merge(ListNode[] lists, int left, int right)
 {
 if(left > right) return null;
 if(left == right) return lists[left];
 // 1. 平分数组
 int mid = (left + right) / 2;
 // [left, mid] [mid + 1, right]
 // 2. 递归处理左右两部分
 ListNode l1 = merge(lists, left, mid);
 ListNode l2 = merge(lists, mid + 1, right);
 // 3. 合并两个有序链表
 return mergeTwoList(l1, l2);
 }
 public ListNode mergeTwoList(ListNode l1, ListNode l2)
 {
 if(l1 == null) return l2;
 if(l2 == null) return l1;
 // 合并两个有序链表
 ListNode head = new ListNode(0);
 ListNode cur1 = l1, cur2 = l2, prev = head;
 
 while(cur1 != null && cur2 != null)
 {
 if(cur1.val <= cur2.val)
 {
 prev.next = cur1;
 prev = cur1;
 cur1 = cur1.next;
 }
 else
 {
 prev.next = cur2;
 prev = cur2;
 cur2 = cur2.next;
 }
 }
 if(cur1 != null) prev.next = cur1;
 if(cur2 != null) prev.next = cur2;
 return head.next;
 }
}

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

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

相关文章

数据结构与算法——Java实现 31.阻塞队列

—— 24.10.8 一、问题提出 目前队列存在的问题 1.很多场景要求分离生产者、消费者两个角色、它们需要由不同的线程来担当&#xff0c;而之前的实现根本没有考虑线程安全问题 2.poll方法&#xff0c;队列为空&#xff0c;那么在之前的实现里会返回null&#xff0c;如果就是硬…

Spring Boot 进阶-深入SpringBoot的数据校验原理

在之前的分析中我们知道要想了解一个场景启动器的原理就必须要找到它对应的自动配置类。下面我们就来探索一下数据校验spring-boot-starter-validation场景启动器的原理吧? ValidationAutoConfiguration 配置类 首先我们来看在这个配置类上都有哪些条件注解,并且这些条件注解…

鸿蒙开发实战项目【硅谷租房】--- 项目介绍

目录 一、简述 二、项目资料 2.1 UI设计稿 2.2 服务器 2.3 Apifox接口JSON文件 使用 Apifox 测试接口 一、简述 这是一个基于 鸿蒙 API12 开发的移动端租房 App&#xff0c;用户可以使用该应用搜索租房列表、查看房屋详情、预约租房等。 该项目的tabbar包含五部分&…

OpenTelemetry 演示与 OpenTelemetry 的 Elastic 分发

作者&#xff1a;来自 Elastic Roger Coll 了解 Elastic 如何致力于支持用户使用 OpenTelemetry。探索我们对 OpenTelemetry Demo 的公开部署&#xff0c;并了解 Elastic 的解决方案如何增强你的可观察性体验。 最近&#xff0c;Elastic 为各种 OpenTelemetry 组件引入了 Elas…

谨防网络诈骗,天上不会掉馅儿饼

“网络诈骗现已是国际性问题。” 面对网络诈骗&#xff0c;风险管理工具要选且必须选。网络诈骗日益猖獗&#xff0c;当前背景下&#xff0c;IP风险画像也开始成为了防范网络诈骗的重要工具之一。 近期&#xff0c;一起利用AI换脸拟声技术实施的诈骗案件引起了广泛关注。 一名…

C# 获取可执行文件目录

---------------------------------------------------------------------------

OCP迎来新版本,让OceanBase的运维管理更高效

近期&#xff0c;OceanBase的OCP发布了新版本&#xff0c;全面支持 OceanBase 内核 4.3.2 及更低版本。新版本针对基础运维、性能监控、运维配置、外部集成等多个方面实现了 20余项的优化及强化措施&#xff0c;增强产品的易用性和稳定性&#xff0c;从而帮助用户更加高效地管理…

前端 接口扁平化转换树数据的方法并添加序号

最终实现图 . 1. 接口扁平化数据转树型结构数据 &#xff08;递归&#xff09; // 转换树行数据的方法 export const transTree (list, pidKey, idKey id) > {// 最终生成的树行结构const treeData []// 对传入进来的 数据进行遍历&#xff0c;查找对应的子级list.for…

tauri开发Mac电脑Safari浏览器一个很奇怪的问题:在 input 输入框输入的是全小写英文字母,会自动将首字母转换为大写解决办法

问题原因 在 Mac 系统中默认使用 Safari 的内核 WKWebView 作为渲染引擎&#xff0c;而 Safari 浏览器的一些 “人性化” 机制&#xff1a;如果输入框中输入的是全小写英文&#xff0c;会自动将首字母转换为大写。 解决办法 我只需要禁止这个默认的行为&#xff0c;即可解决这…

循环神经网络-RNN

文章目录 前言一、RNN介绍1.基本结构2.隐藏态特点3.输出计算 二、RNN循环由来与局限三、RNN延申 前言 因为传统神经网络无法训练出具有顺序的数据且模型搭建时没有考虑数据上下之间的关系。所以我们提出了循环神经网络。 一、RNN介绍 循环神经网络&#xff08;Recurrent Neu…

LabVIEW惯性导航系统仿真平台

LabVIEW开发捷联惯性导航系统仿真平台&#xff0c;采用模块化设计&#xff0c;利用LabVIEW的图形化编程特性&#xff0c;提高了系统仿真的效率和精度&#xff0c;同时具备良好的可扩展性和用户交互性。 项目背景 当前&#xff0c;惯性导航系统&#xff08;INS&#xff09;的研…

EMC-LISN是什么

LISN&#xff0c;Line Impedance Stabilization Network的缩写&#xff0c;即线路阻抗稳定网络 LISN主要是用于EMI测试中传导发射&#xff08;CE&#xff09;的 我们希望只测试到待测产品传导出来的干扰&#xff0c;能够不测试到电源输入本身上面的干扰。 测试场地不同&…

如何避免PuTTY的连接超时

问题&#xff1a;使用PuTTY默认创建的SSH连接&#xff0c;过一会就会提示“Remote side unexpectedly closed network connection" 解决方法&#xff1a; 要防止PuTTY会话由于空闲而断开连接&#xff0c;可以通过启用keep-alives功能&#xff0c;使PuTTY定期向远程主机发…

【实时计算 Flink】检查点和快照超时的诊断方法与调优策略

Flink的状态管理是一个复杂而关键的领域&#xff0c;涉及到作业的性能、稳定性和资源利用等多个方面。通过对状态生成机制和优化策略地深入理解与正确应用&#xff0c;结合实时计算Flink版提供的产品能力&#xff0c;可以帮您有效地优化Flink作业以应对大规模状态作业带来的挑战…

PigGo的安装配置

TyporaPigGo的使用 1、下载PigGo 1.1、先打开Typora的偏好设置 1.2 下载PigGo app 1.3 下载Setup-xxx.exe版本 1.4安装成功后打开PigGo 2、配置PigGo 1、安装gitee-uploader插件 2、 PicGo设置 ​ ​ 3、图床设置 3.1配置SM.MS 3.1.1 登录sm,生成秘钥 如果没有注册&…

期权懂|期权交易涨跌幅限制会随时调整吗?

本期让我懂 你就懂的期权懂带大家来了解&#xff0c;期权交易涨跌幅限制会随时调整吗&#xff1f;有兴趣的朋友可以看一下。期权小懂每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 期权交易涨跌幅限制会随时调整吗&#xff1f; 涨跌幅…

天玑 9400 基本确认:4大升级,一代“冰龙”来了

去年&#xff0c;天玑9300 破釜沉舟&#xff0c;打破了A系不可击败的神话。但今年&#xff0c;对安卓阵营来说&#xff0c;才是扬眉吐气的时刻。 因为芯片人才的流失&#xff0c;果子已经雄风不再。即使是 4nm 工艺打3nm工艺&#xff0c;天玑 9300 的 GPU效能&#xff0c;也压…

机器学习笔记-1

文章目录 前言一、How to find a function二、Define Loss from Training Data三、Optimization总结 前言 机器学习&#xff08;Machine Learning, ML&#xff09;是一门让计算机通过数据来自动学习和改进的技术。它的核心理念是通过分析大量的历史数据来找到其中的规律&#…

YOLOv10改进策略【注意力机制篇】| CVPR2024 CAA上下文锚点注意力机制

一、本文介绍 本文记录的是基于CAA注意力模块的YOLOv10目标检测改进方法研究。在远程遥感图像或其他大尺度变化的图像中目标检测任务中&#xff0c;为准确提取其长距离上下文信息&#xff0c;需要解决大目标尺度变化和多样上下文信息时的不足的问题。CAA能够有效捕捉长距离依赖…

Spark算子使用-Map,FlatMap,Filter,diatinct,groupBy,sortBy

目录 Map算子使用 FlatMap算子使用 Filter算子使用-数据过滤 Distinct算子使用-数据去重 groupBy算子使用-数据分组 sortBy算子使用-数据排序 Map算子使用 # map算子主要使用长场景&#xff0c;一个转化rdd中每个元素的数据类型&#xff0c;拼接rdd中的元素数据&#xf…