归并排序 - 排序链表

LeetCode 排序链表(归并排序) 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。 示例 1: 输入: 4->2->1->3 输出: 1->2->3->4 示例 2: 输入: -1->5->3->4->0 输出: -1->0->3->4->5 思路分析:由于题目特意要求 O(n log n) 时间复杂度和常数级空间复杂 阅读详情

题目:给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

要求:在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

  1. 由于时间复杂度要在o(n log n)内 所以冒泡排序就不在选择范围内了 这里采用归并排序。
  2. 归并排序分为 迭代法 和 递归法 下面俩种方法的代码都将展示。
  3. 递归的核心思路就是:二分法
    从中间切割 然后左右俩边的一路按中切割到最小单位 然后排序返回
    举例 42507163
    42507163
    4250 7163
    42 50 71 63 切割到最小单位排序
    24 05 17 36 再合并排序
    0245 1376
    01234567
  4. 迭代法是在链表长度内进行切割
    先 2 2比较完后排序 再 4 4比较 再8 8…
    举例: 42507163
    先按42 50 71 63切割后排序得 24 05 17 36
    再按2405 1736切割后排序 得 0245 1367
    最后再按02451367比较排序 得 01234567
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode {
    int val;
    struct ListNode *next;
};

struct ListNode* FindLinkMid(struct ListNode* head) // 快慢指针法寻中点
{
    if (head == NULL) {
        return head;
    }

    struct ListNode* fast = head->next; // 快指针, 从第二个指针开始每次走俩步
    struct ListNode* slow = head;       // 慢指针, 从头开始每次走一步

    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
    }

    return slow;
}

struct ListNode* Merger(struct ListNode* left, struct ListNode* right) // 有序链表归并排序
{
    struct ListNode* head = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode* temp = head;

    head->next = NULL;

    while (left != NULL && right != NULL) { //左右有序链表比较后, 较小值归入新建链表
    
        if (left->val <= right->val) {
            temp->next = left;
            left = left->next;
        } else {
            temp->next = right;
            right = right->next;
        }

        temp = temp->next;
    }

    if (left != NULL) { // 由于是有序, 可把剩余内容归入新链表
        temp->next = left;
    }

    if (right != NULL) { // 同上
        temp->next = right;
    }

    return head->next;
}


//递归的核心思路就是:二分法
//从中间切割 然后左右俩边的一路按中切割到最小单位 然后排序返回
//举例 42507163
//      42507163
//  4250        7163
//  42  50  71  63  切割到最小单位排序
//  24  05  17  36   再合并排序
//  0245 1376
//  01234567

struct ListNode* MergerSort_Recursion(struct ListNode* head) //归并排序-递归
{
    if (head == NULL || head->next == NULL) {//当链表被分割的只剩一个时 无需排序直接返回
        return head;
    }

    struct ListNode* mid = FindLinkMid(head); //找到中节点
    struct ListNode* next = mid->next; // 存储右边节点

    mid->next = NULL; // 把链表从中间分割开
    struct ListNode* left  = MergerSort_Recursion(head); // 左排序
    struct ListNode* right = MergerSort_Recursion(next); // 右排序 

    return Merger(left, right); // 合并
}

struct ListNode* Cut_List(struct ListNode* head, int cut) // 切割掉链表head 前cut个
{
    while (head!=NULL && --cut) {
        head = head->next;
    }

    if (head == NULL) {
        return NULL;
    }

    struct ListNode*temp = head->next;
    head->next = NULL;

    return temp;
}

// 迭代法是在链表长度内进行切割 
// 先 2 2比较完后排序 再 4 4比较 再8 8...
// 举例: 42507163
// 先按42 50 71 63切割后排序得 24 05 17 36
// 再按2405 1736切割后排序 得 0245 1367
// 最后再按02451367比较排序 得 01234567

struct ListNode* MergerSort_Iteration(struct ListNode* head) //归并排序-迭代
{
    if (head == NULL || head->next == NULL) {//当链表长度小于等于1 直接返回
        return head;
    }

    int len = 1;
    struct ListNode* temp = head;
    while(temp->next != NULL) { //计算链表长度
        len++;
        temp = temp->next;
    }
    
    struct ListNode* dummy = (struct ListNode *)malloc(sizeof(struct ListNode)); //排序后的链表头
    dummy->next = head;
    
    for (int cut = 1; cut < len; cut<<=1) {     //循环遍历链表 log2(len)次
        struct ListNode* curr = dummy->next;    // 当前链表头
        struct ListNode* tail = dummy;          // 临时链表 用于拼接排序合并后的链表
        while (curr != NULL) {                  //遍历当前链表
            struct ListNode* left = curr;
            struct ListNode* right = Cut_List(left, cut);   //把左边切割出去 获取右边首地址
            curr = Cut_List(right, cut);        //把右边切割出去 留下剩下未排序的链表下轮继续切割排序
            tail->next = Merger(left, right);   //合并排序
            while(tail->next != NULL) {         //尾插法 把合并排序后的链表插入新链表temp
                tail = tail->next;
            }
            
        }
    }

    return dummy->next;
    
}

链表排序——归并排序、快速排序 目录 1 归并排序 1.1 实现思路 1.2 代码实现 2 快速排序 2.1 实现思路 2.2 代码实现 1 归并排序 归并排序基本原理https://blog.youkuaiyun.com/qq_28114615/article/details/86345717 1.1 实现思路        与数组归并排序相同,主要分为以下三个步骤:       ①找到链表中点。最常用的方法是双指针... 阅读详情

相关推荐

C++链表实现归并排序

C++ 链表实现的归并排序 首先是定义和创建两个链表用来排序 typedef int ElemType; typedef struct LNode { ElemType data; struct LNode * next; }LinkNode; void CreateListF(LinkNode * &L, ElemType a[], int n) { LinkNode...

看起来喜欢做枯燥事情的人或许只是更清楚自己想要什么罢了 734

归并排序及其优化(数组归并/链表归并,自顶向下/自底向上等)

  归并排序时一种使用分治思想实现的高效的排序算法,它的最好/最坏/平均时间复杂度都是O(nlogn),而且是一种稳定的排序算法。然而归并排序并没有得到广泛的应用,今天我们来看看归并排序的特点、优化和应用。 1. 算法简介 归并排序的原理   归并排序(Merge Sort)的核心思想就是一个典型的分治算法。如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。算法示意图如下:   代码实现如下: class Solution

bitkevin的博客 2168

单向链表排序-归并排序

链表排序中使用归并排序:归并排序不仅保持了它的 O( nlog(n) ) 的时间复杂度,而且它在数组排序中广受诟病的空间复杂度,在链表排序中从 O(n) 降低到 O( log(n) )。    对于单向链表的二路归并排序,首先是要找到链表的中间节点,将链表等分。我们可以使用快慢指针的方式找到链表的中间节点,即一个指针以一个步长移动,另一个指针使用两个步长移动,当快指针到结尾位置时,慢指针正好在中

luckyone的专栏 1507

归并排序链表的实现——学习笔记

归并排序是效率非常高的一种排序方式,和快速排序一样用了分治的思想。分治法的精髓在于将一个复杂问题分割成为多个简单的子问题,然后将子问题逐个解决,最终合并在一起以后就是复杂问题的解了。 这篇文章主要讲归并排序链表的实现方式,如果想了解数组的归并排序,可移步这里。 归并排序的思想其实挺简单的,简而言之就是将数列分割成子序列,先对子序列进行排序,再将排好序的子序列合并成一个完整的数列。具体例子...

jjwwwww的博客 5808

基于Simulink的二相混合式步进电机驱动器快速建模与仿真实践

本文详细介绍了如何利用Simulink对二相混合式步进电机驱动器进行快速建模与仿真。通过从零搭建电机数学模型、设计H桥控制逻辑,并集成步进时序,手把手指导读者创建可运行的仿真模型文件。该方法能有效验证控制策略,避免硬件调试风险,是学习与实践电机驱动开发的理想途径。

weixin_29171087的博客 147

一文搞定 GitHub SSH Key:生成、配置、好处全攻略

本文详细介绍了如何配置GitHub SSH Key,实现免密安全认证。主要内容包括:SSH协议的四大优势(免密登录、安全性高、稳定省心、支持自动化);Git身份配置步骤;检查本地SSH Key;生成RSA密钥对;添加密钥到SSH代理;复制公钥到GitHub;测试连接。配置完成后,开发者可通过SSH地址克隆仓库,实现push/pull全程免密操作。该方案显著提升Git使用效率,同时保障安全性,是开发者必备技能。

qq_62904883的博客 957

LeetCode 148. 排序链表(归并排序、快速排序)

1. 题目 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。 示例 1: 输入: 4->2->1->3 输出: 1->2->3->4 示例 2: 输入: -1->5->3->4->0 输出: -1->0->3->4->5 来源:力扣(LeetCode) 链接:https://leet...

Michael是个半路程序员 1473

RS-485调试踩坑实录:MAX13487数据回环问题排查与修复(附电路图)

本文深入解析了RS-485通信中MAX13487芯片的数据回环问题,提供了详细的排查过程和解决方案。通过分析芯片特性和自动换向机制,揭示了RO引脚配置不当是导致问题的关键原因,并给出硬件(添加上拉电阻)和软件(调整MCU配置)两种修复方案,附有优化设计建议和实际调试技巧。

play7的博客 1297

对链表进行归并排序

题干:对一个链表进行归并排序。 对于数组,归并排序的思想是先将数据分成两部份,分别排序。然后再对已经排好序的两个数组归并。其中在对两部份数组排序时进行递归,最后得到的即是排好序的数组。 对于链表,归并的思想也是如此。需要解决的问题有两个,第一个是如何将链表平均分成两部份。 第二个是将两个有序链表合并成一个有序链表。 解决了这两个问题,归并排序链表就迎仞而解了。 对于第一个问题,可以采用快慢指针的方...

wh义华的专栏 1524

Plecs电力电子仿真专业教程-第一季 补充课程 Plecs的安装教程与软件下载

PLECS4.1.64免安装版本–点我下载 Plecs.Standalone-3.7.2版本--关注加私信给我可以提供下载链接 补充课程 Plecs的安装教程与软件下载 详细介绍Plecs软件的安装过程

1万+

IPD流程中的RR\OR\IR\PB\SR\AR\CR等名词汇总(基于新的产品包需求架构)

RR:Raw Requirement,原始需求=》IR:Initial Requirement,初始需求=》SR:System Requirement,系统需求=》AR:Allocated Requirement,分配需求=》CR:Process Change Requirement,流程变更需求。

a1809032425的博客 3188

链表的归并排序

链表的归并排序是一种高效、稳定的排序算法,非常适合链表这种数据结构。通过分治思想,将链表不断分割并合并,实现了对链表的排序。

2401_86909484的博客 1173

【Ranger】mac下Apache Ranger编译安装

a、编译过程中出现测试用例不通过和文件认证错误错误:带上参数-DskipTests=true -Drat.skip=true 重新编译;b、若编译过程中出现缺少jar包的情况,可能下载不了,需要手动自动添加;c、编译所需的其他工具如maven等安装配置略。采用这个会报错,因此我才用了下面一个命令。初始化设置Ranger Admin:执行。然后进入target目录。

九师兄 2103

链表的归并排序与链表数组的归并排序

链表的归并排序 就是排序找中点,然后中点位置后一个为null,切断链表,也就是分而治之每次分割一半,一直往后分割,直到只剩下一个节点,或者没有节点停止。 public class ListNode{ int val; ListNode next; public ListNode(int x){ this.val = x; ...

AaronCao 343

华为OD机试真题2025双机位B卷 Java&Go 实现【朋友圈个数】

给定一组朋友关系,统计一下该朋友关系网中的朋友圈个数。 朋友圈的定义:一个朋友圈至少由3个朋友组成,且要求同一个朋友圈中的任意两个人都具有直接的朋友关系。 输入描述 输入一个朋友关系列表,如 Fiends =A.B],[A.C],IB,DI,其中的每一个元素 Friendsi表示 Friends[i][0)和 Friends [i][1] 是朋友关系 先输入一个数字 N 代表关系的总数,后面每条关系一行,两个成员以逗号分隔

MISAYAONE的博客 517

【系统架构设计师】案例分析考点情况分析和解题技巧(包括2009~2024年考点详情)

复习方法建议仍然是以真题为主,只不过和综合题不同,标准答案并不会涵盖所有可能的考点。案例的考察方法和论文不太一样,它考察两大块的内容:

数据知道的博客 1万+

单链表归并排序

单链表归并排序需要掌握的知识点。 1.归并排序的思想 2.如何合并两个有序的单链表 3.如何找到一个链表的中间节点,这里是为了断链。将链表一分为二。 (1)合并两个有序的链表,主要有两种思路。递归和迭代 递归方法的代码: struct node { int val; node *next; }; //注意此时链表a和链表b均为递增有序的单链表 node *merge(node *a, node...

乐行僧的博客 1275

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

链表的快排和归并排序

链表快排 对一个单链表用快排的方式排序 快排的关键在于partition函数,因为单链表是不能倒序遍历的,因此不能通过头尾双指针向内夹的partition函数,而是用都是从头开始的双指针方式,具体的两种partition函数可以参考:快排的两种partition函数 这里链表快排和数组快排partition函数中关键的不同点在于:partition函数中遍历结束的方式,和递归结束的方式 # 链表快...

海晨威 1178

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C++链表的归并排序

void sort(node* plst,int low,iny high,int *parray)//low与high是闭区间[low,high]{ if(low { int mid=(low+high)/2;//取出数组的中间值 sort(plst,low,mid,parray);//递归调用左半边链表进行排序 sort(plst,mid+1,high,parray);//递归调用右半边

安子健的专栏 1584

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C++链表归并排序

说在前面: 你需要先会写数组的归并排序。 除此之外,你还要对递归的过程有很清晰的认识。 你需要会用熟练的使用指针,这是C++玩家必备技能。 代码里的List,可能命名成Node更加合适,我也不知道我当时这么想的,命了个这鼠名字,看着挺别扭的。 掌握归并排序的核心思想就行了,将大区间分解成两个小区间之后呢,双指针,把小的数值一个一个的压入新的容器(这里自然是指链表了)里面,等待两个子区间其中有一个全部压入了之后,把另外一个区间剩余的全部压入,返回。这就完成归并的基本逻辑

Hayakawa123的博客 2466

排序链表 归并排序

给你链表的头结点head,请将其按升序排列并返回排序后的链表。 示例 1: 输入:head = [4,2,1,3]输出:[1,2,3,4] 示例 2: 输入:head = [-1,5,3,4,0]输出:[-1,0,3,4,5] 示例 3: 输入:head = []输出:[] 思路: 这个题乍一看是将链表进行排序。以前好像从来没有接触过如何排序链表,可以用像排序普通数组那样的排序方法来排序吗?是可以的。 这里我们可以使用归并排序的思路来解决。归...

TranSad的博客 352

链表归并排序

#include using namespace std; /** * 对链表进行原地排序 * 回忆数组归并最重要的就是:找到数组中点,以及归并两个有序数组。 * 类似地这里有两个关键点: * (1)找到链表的中间节点,用一快一慢两个指针往前走; * (2)归并两个已经排好序的链表,指向归并结果的指针总是变化的,所以需要返回一个指向归并好的链表的头指针; * (3)最后注意,将

jiq•钦's technical Blog 1070

对链表进行排序(归并排序)

归并排序(算法交换链表节点,时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1))                        本文地址 首先用快慢指针的方法找到链表中间节点,然后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表。归并排序应该算是链表排序最佳的选择了,保证了最好和最坏时间复杂度都是nlogn,而且它在数组排序中广受诟病的空间复杂度在链表排序中

iamxiaoguizi的博客 1149
上一篇: MFI认证
下一篇: 15765-2 协议解读
四夕、
博客等级 码龄8年 19粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值