【算法训练-链表 七】【排序】:链表排序、链表的奇偶重排、重排链表

news2025/1/20 13:25:56

废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【链表的排序】,使用【链表】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为:目标公司+最近一年+出现频率排序,由高到低的去牛客TOP101去找,只有两个地方都出现过才做这道题(CodeTop本身汇聚了LeetCode的来源),确保刷的题都是高频要面试考的题。

在这里插入图片描述
以及重排链表
在这里插入图片描述

附上题目链接,后期可以依据解题思路反复快速练习,题目按照题干的基本数据结构分类,且每个分类的第一篇必定是对基础数据结构的介绍

链表排序【MID】

单链表的升序排列

题干

直接粘题干和用例

解题思路

而链表中我们也可以用【合并K个有序链表】同样的方式,当链表被划分到只剩一个节点时,就一定有序。只需要找到中间个元素的前一个节点,将其断开,就可以将链表分成两个子链表,然后继续划分,直到最小,然后往上依次合并。

  • 终止条件: 当子链表划分到为空或者只剩一个节点时,不再继续划分,往上合并。
  • 返回值: 每次返回两个排好序且合并好的子链表。
  • 本级任务: 找到这个链表的中间节点,从前面断开,分为左右两个子链表,进入子问题排序。

怎么找中间元素呢?我们可以使用快慢双指针,快指针每次两步,慢指针每次一步,那么快指针到达链表尾的时候,慢指针正好走了快指针距离的一半,为中间元素。

具体做法:

  1. step 1【入参判断】:首先判断链表为空或者只有一个元素,直接就是有序的。
  2. step 2【找中间节点】:准备三个指针,快指针right每次走两步,慢指针mid每次走一步,前序指针left每次跟在mid前一个位置。三个指针遍历链表,当快指针到达链表尾部的时候,慢指针mid刚好走了链表的一半,正好是中间位置。
  3. step 3【划分子问题】:从left位置将链表断开,刚好分成两个子问题开始递归。
  4. step 4【合并子问题】:将子问题得到的链表合并,参考合并两个有序链表。

代码实现

给出代码实现基本档案

基本数据结构链表
辅助数据结构
算法分治
技巧双指针(快慢指针)

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList (ListNode head) {
        // 1 入参判断,如果链表只剩一个节点或者空,则直接有序,到达终止条件
        if (head == null || head.next == null) {
            return head;
        }
        // 2 中间位置为慢指针的下一个节点,奇数节点链表为正中间,偶数则为另一半的开头,所以后半段一定是mid.next
        ListNode mid= findMidNode(head);
        // 3 设置后半段链表,断开前后半段链表
        ListNode newHead = mid.next;
        mid.next = null;
        // 4 合并两段有序链表
        return mergeSortList(sortInList(head), sortInList(newHead));
    }

    // 寻找链表中间节点
    private ListNode findMidNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        // 3 中间位置为慢指针的下一个节点,奇数节点链表为正中间,偶数则为另一半的开头
        return slow;
    }

    // 合并两个有序链表
    private ListNode mergeSortList(ListNode pHead1, ListNode pHead2) {
        // 1 入参判断
        if (pHead1 == null && pHead2 == null) {
            return null;
        }
        if (pHead1 == null) {
            return pHead2;
        }
        if (pHead2 == null) {
            return pHead1;
        }
        // 2 设置结果链表
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        while (pHead1 != null && pHead2 != null) {
            if (pHead1.val <= pHead2.val) {
                p.next = pHead1;
                pHead1 = pHead1.next;
            } else {
                p.next = pHead2;
                pHead2 = pHead2.next;
            }
            p = p.next;
        }

        // 3 如果其中一个链表为空,后续直接接另一个
        if (pHead1 == null) {
            p.next = pHead2;
        }
        if (pHead2 == null) {
            p.next = pHead1;
        }
        return dummy.next;
    }
}

复杂度分析

在这里插入图片描述

链表的奇偶重排【MID】

需要注意的是节点的位置而非节点的值

题干

直接粘题干和用例

解题思路

核心思路就是奇数位节点和偶数位节点断掉重连

  1. step 1:判断空链表的情况,如果链表为空,不用重排。如果链表只有一个节点也不用重排
  2. step 2:使用双指针odd和even分别遍历奇数节点和偶数节点,并给偶数节点链表一个头。
  3. step 3:上述过程,每次遍历两个节点,且even在后面,因此每轮循环用even检查后两个元素是否为NULL,如果不为再进入循环行上述连接过程。
  4. step 4:将偶数节点头接在奇数最后一个节点后,再返回头部。

代码实现

给出代码实现基本档案

基本数据结构链表
辅助数据结构
算法迭代
技巧双指针(同向指针)

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 *   public ListNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param head ListNode类
     * @return ListNode类
     */
    public ListNode oddEvenList (ListNode head) {
        // 1 入参条件判断
        if (head == null) {
            return null;
        }
        if (head.next == null) {
            // 只有1个节点,不用重排
            return head;
        }
        // 2 设置偶数虚拟头节点,并设定奇偶双指针
        ListNode odd = head;
        ListNode evenDummy = head.next;
        ListNode even = evenDummy;
        // 3 遍历链表,往奇数和偶数节点上补充对应节点,因为even在后边,所以要以even做判断,否则对于1-2-3-4这种情况
        //odd结束时指向null,null.next会有问题,返回仍然是null
        while (even != null && even.next != null) {
            // 奇数位断开指向偶数的指针并指向下一个奇数位
            odd.next = even.next;
            odd = odd.next;
            // 偶数位断开指向奇数的指针并指向下一个偶数位
            even.next = odd.next;
            even = even.next;
        }
        // 4 遍历完后偶数整体接到奇数后
        odd.next = evenDummy;

        return head;
    }
}

复杂度分析

时间复杂度:O(n),遍历一次链表的所有节点
空间复杂度:O(1),常数级指针,无额外辅助空间

重排链表【MID】

复杂度升级了一些,不过套路类似

题干

直接粘题干和用例

解题思路

注意到目标链表即为将原链表的左半端和反转后的右半端合并后的结果。这样我们的任务即可划分为三步:

  1. 找到中点】找到原链表的中点(参考「876. 链表的中间结点」)。我们可以使用快慢指针来 O(N)O(N)O(N) 地找到链表的中间节点。
  2. 反转右半边】将原链表的右半端反转(参考「206. 反转链表」)。我们可以使用迭代法实现链表的反转。
  3. 合并两个链表】将原链表的两端合并。因为两链表长度相差不超过 11,因此直接合并即可。

以上需要注意的是,找链表中点时,对于奇数个节点,中点为前半段链表所有(从题目示例可以看出)

代码实现

给出代码实现基本档案

基本数据结构链表
辅助数据结构
算法迭代
技巧双指针

其中数据结构、算法和技巧分别来自:

  • 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树
  • 10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法
  • 技巧:双指针、滑动窗口、中心扩散

当然包括但不限于以上

/**
 * 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) {
        // 1 入参校验
        if (head == null || head.next == null) {
            return ;
        }

        // 2 找到链表中点,断开后半段链表,这次要找的mid要偏右一些,前半段保留偏长,这样后续交替衔接才没问题
        ListNode mid = middleNode(head);
        ListNode l1 = head;
        ListNode l2 = mid.next;
        mid.next = null;

        // 3 反转后半段链表
        l2 = reverse(l2);

        // 4 将反转后链表连接到原链表中
        mergeList(l1, l2);
    }

    // 交替合并两个链表
    private void mergeList(ListNode l1, ListNode l2) {
        ListNode pNext1;
        ListNode pNext2;
        // 因为两个链表仅仅相差1,所以直接交替合并即可
        while (l1 != null && l2 != null) {
            // 暂存两个链表的下一个节点
            pNext1 = l1.next;
            pNext2 = l2.next;

            // 交替衔接l1和l2
            l1.next = l2;
            l1 = pNext1;

            l2.next = l1;
            l2 = pNext2;
        }
    }

    // 寻找中间节点
    private ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }


    // 反转链表
    private ListNode reverse(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode pNext = cur.next;
            cur.next = pre;
            pre = cur;
            cur = pNext;
        }
        return pre;
    }
}

复杂度分析

时间复杂度:遍历了链表,时间复杂度为O(N)
空间复杂度:没有使用额外空间,空间复杂度为O(1)

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

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

相关文章

【LeetCode每日一题合集】2023.9.4-2023.9.10(⭐二叉树的重建二分答案拓扑排序)

文章目录 449. 序列化和反序列化二叉搜索树⭐⭐⭐⭐⭐&#xff08;二叉树的重建&#xff09;解法相关题目——297. 二叉树的序列化与反序列化⭐⭐⭐⭐⭐解法——深度优先搜索 2605. 从两个数字数组里生成最小数字哈希表分情况讨论位运算表示集合&#xff0c;分情况讨论&#x1…

Day60|单调栈part03:84.柱状图中最大的矩形

柱状图中最大的矩形 leetcode链接&#xff1a;力扣题目链接 视频链接&#xff1a;单调栈&#xff0c;又一次经典来袭&#xff01; LeetCode&#xff1a;84.柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;…

【多线程】线程安全的单例模式

线程安全的单例模式 饿汉模式懒汉模式单线程版多线程版多线程版(改进) 单例模式能保证某个类在程序中只存在 唯一 一份实例, 而不会创建出多个实例&#xff0c;从而节约了资源并实现数据共享。 比如 JDBC 中的 DataSource 实例就只需要一个. 单例模式具体的实现方式, 分成 “饿…

Unity3D URP 仿蜘蛛侠风格化BloomAO

Unity3D URP 仿蜘蛛侠风格化Bloom&AO BloomBloom效果流程&#xff1a;制作控制面板VolumeComponent.CSCustom Renderer FeatherCustom Renderer PassBloom ShaderComposite Shader 完善Custom Feather风格化AO 总结 本篇文章介绍在URP中如何进行风格化后处理&#xff0c;使…

【MATLAB第74期】#源码分享 | 基于MATLAB的ARX-ARMAX线性自回归移动平均外生模型(结合最小二乘思路)

【MATLAB第74期】#源码分享 | 基于MATLAB的ARX-ARMAX线性自回归移动平均外生模型&#xff08;结合最小二乘思路&#xff09; 根据ARX预测输出和实际输出的误差向量&#xff0c;采用ARMAX算法结合ARX误差建模&#xff0c;对预测值进一步细化。通过将误差描述为白噪声的移动平均…

Spring事务管理: 构建稳健的数据库事务处理

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

C++算法 —— 动态规划(4)子数组

文章目录 1、动规思路简介2、最大子数组和3、环形子数组的最大和4、乘积最大子数组5、乘积为正数的最长子数组长度6、等差数列划分7、最长湍流子数组8、单词拆分9、环绕字符串中唯一的子字符串 每一种算法都最好看完第一篇再去找要看的博客&#xff0c;因为这样会帮你梳理好思路…

商城系统优化

1、DB、模板的渲染速度&#xff08;thymeleaf&#xff09;、静态资源、日志、JVM 数据库的优化&#xff08;参照数据库优化课程&#xff09;使用索引&#xff0c;减少数据库的交互次数、缓存 thymeleaf使用缓存 静态资源&#xff1a;放到nginx中&#xff0c;实现动静分离 2、…

【数学】ABC 319 E

E - Bus Stops 题意&#xff1a; 思路&#xff1a; 感觉思路比较简单 首先注意到每个询问的范围是1e9&#xff0c;不难想到答案一定存在某个循环节&#xff0c;最后一定是要 %T的 那么问题就在于找到这个循环节是什么 猜想循环节为lcm(p1, p2, p3, ....) 用小数据验证 n…

一篇博客教会您SpringMVC文件上传、下载,多文件上传及工具jrebel的使用

目录 一.文件上传 二.文件下载 三.多文件上传 四&#xff0c;jrebel的介绍 前言&#xff1a; 我们之前已经实现了SpringMVC的增删改查&#xff0c;今天这一篇博客教会您SpringMVC文件上传、下载&#xff0c;多文件上传及工具jrebel的使用&#xff0c;希望这篇博客能够给正在…

二、Spark 调度系统

目录 Spark 调度系统DAGSchedulerSchedulerBackendTaskSchedulerExecutorBackendSpark 任务调度流程 Spark 调度系统 分布式计算的精髓&#xff0c;在于如何把抽象的计算图&#xff0c;转化为实实在在的分布式计算任务&#xff0c;然后以并行计算的方式交付执行。 Spark调度系…

Mojo安装使用初体验

一个声称比python块68000倍的语言 蹭个热度&#xff0c;安装试试 系统配置要求&#xff1a; 不支持Windows系统 配置要求: 系统&#xff1a;Ubuntu 20.04/22.04 LTSCPU&#xff1a;x86-64 CPU (with SSE4.2 or newer)内存&#xff1a;8 GiB memoryPython 3.8 - 3.10g or cla…

华为云云耀云服务器L实例评测 | 分分钟完成打地鼠小游戏部署

前言 在上篇文章【华为云云耀云服务器L实例评测 | 快速部署MySQL使用指南】中&#xff0c;我们已经用【华为云云耀云服务器L实例】在命令行窗口内完成了MySQL的部署并简单使用。但是后台有小伙伴跟我留言说&#xff0c;能不能用【华为云云耀云服务器L实例】来实现个简单的小游…

车载诊断数据库——诊断问卷调查表与CDD关联关系

车载诊断数据库——诊断问卷调查表与CDD关联关系 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生…

超级电容-电池-超级电容混合储能系统能量管理simulink仿真建模模型

建立混合储能系统模型 在Simulink中&#xff0c;首先需要建立一个超级电容和蓄电池并联的混合储能系统模型。其中&#xff0c;超级电容和蓄电池的荷电状态&#xff08;SOC&#xff09;需要根据实际情况进行管理。荷电状态可以通过对电池和超级电容的电压、电流等进行测量&…

说透 Nacos 一致性协议

1 Nacos ⼀致性协议 1.1 为什么 Nacos 需要⼀致性协议 Nacos尽可能减少用户部署以及运维成本&#xff0c;做到用户只需要⼀个程序包&#xff0c;就快速单机模式启动 Nacos 或集群模式启动 Nacos。而 Nacos 是⼀个需要存储数据的组件&#xff0c;为实现目标&#xff0c;就要在…

透视俄乌网络战之二:Conti勒索软件集团(上)

透视俄乌网络战之一&#xff1a;数据擦除软件 Conti勒索软件集团&#xff08;上&#xff09; 1. Conti简介2. 组织架构3. 核心成员4. 招募途径5. 工作薪酬6. 未来计划参考 1. Conti简介 Conti于2019年首次被发现&#xff0c;现已成为网络世界中最危险的勒索软件之一&#xff0…

汇川PLC学习Day3:轴控代码编写、用户程序结构说明与任务配置示例、

汇川PLC学习Day3&#xff1a;轴控代码编写、用户程序结构说明、任务配置示例 一、新建轴与轴控代码编写 1. 新建轴 (1)新建一个轴 &#xff08;2&#xff09;将轴名字更新为实际名字 可以后面实例化后再更改&#xff0c;汇川可以在更新名字时同步更新其他编写的代码名字&a…

GStreamer时钟同步

播放复杂媒体时&#xff0c;每个audio和video sample必须在特定时间按特定顺序播放。为此&#xff0c;GStreamer提供了一种同步机制&#xff0c;通过使用 GstClock object、buffer timestamps和SEGMENT event来实现&#xff1a; &#xff08;1&#xff09;GstClock&#xff1a;…