链表排序最优算法_链表算法题

链表排序最优算法_链表算法题链表排序算法总结概述问题描述:给定一个链表,请将这个链表升序排列。节点定义:structListNode{intval;ListNode*next;ListNode(intx):val(x),next(NULL){}};1链表插入排序题目描述:Leetcode0147链表进行插入排序分析因为头结点可能会改变,因此需要设置一个虚拟头结点dummy。我们从前向后遍历整个链表,假设当前考察节点为p,我们需要从

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

链表排序算法总结

概述


问题描述:给定一个链表,请将这个链表升序排列。

  • 节点定义:
struct ListNode { 
   
    int val;
    ListNode *next;

    ListNode(int x) : val(x), next(NULL) { 
   }
};

1 链表插入排序

题目描述:Leetcode 0147 链表进行插入排序

在这里插入图片描述

分析

  • 因为头结点可能会改变,因此需要设置一个虚拟头结点dummy

  • 我们从前向后遍历整个链表,假设当前考察节点为p,我们需要从dummy开始遍历,找到第一个大于p->val的前一个节点cur,然后将p插入到cur后面。

代码

  • C++
class Solution { 
   
public:
    ListNode* insertionSortList(ListNode* head) { 
   
        auto dummy = new ListNode(-1);
        for (auto p = head; p; ) { 
   
            auto cur = dummy, next = p->next;  // next是下一个需要考察的节点
            while (cur->next && cur->next->val <= p->val) cur = cur->next;
            p->next = cur->next;
            cur->next = p;
            p = next;
        }
        return dummy->next;
    }
};

时空复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)n为链表长度。

  • 空间复杂度: O ( 1 ) O(1) O(1)

2 链表归并排序

题目描述:Leetcode 0148 排序链表

在这里插入图片描述

分析

  • 因为要求时间O(1),因此就不能使用递归的写法,这一题可以使用归并排序的迭代写法(自底向上)。

  • 这一题十分类似于Leetcode 0023 合并K个有序链表,我们可以使用LC23的思路求解。代码中的变量如下图所示:

在这里插入图片描述

  • 上面的做法用C++演示。

  • Java演示一下递归(自顶向下)的写法,但是空间复杂度不是 O ( 1 ) O(1) O(1)的。关键在于找到链表的中点,与Leetcode 0109 将有序链表转换二叉搜索树类似,这两题都需要找到中点,不同于LC109,LC109的终止条件是f != null && f->next != null,这里使用的终止条件是f.next != null && f->next->next != null,两者的区别为:

在这里插入图片描述

代码

  • C++
class Solution { 
   
public:
    ListNode* sortList(ListNode* head) { 
   
        int n = 0;
        for (auto p = head; p; p = p->next) n++;  // 求出节点数目

        for (int len = 1; len < n; len += len) { 
     // 枚举合并长度
            // 下面循环一次代表向上递推一层
            auto dummy = new ListNode(-1), cur = dummy;  // 因为头结点可能变,因此需要虚拟头结点
            for (int j = 1; j <= n; j += 2 * len) { 
     // 枚举待合并链表的起点, j不会再下面用到
                auto p = head, q = head;
                for (int i = 0; i < len && q; i++) q = q->next;
                auto o = q;  // o为下次合并的起点
                for (int i = 0; i < len && o; i++) o = o->next;
                // 归并p、q开头的链表
                int l = 0, r = 0;
                while (l < len && r < len && p && q)    
                    if (p->val <= q->val) cur = cur->next = p, p = p->next, l++;
                    else cur = cur->next = q, q = q->next, r++;
                while (l < len && p) cur = cur->next = p, p = p->next, l++;
                while (r < len && q) cur = cur->next = q, q = q->next, r++;
                head = o;  // 进行后面两段链表的合并
            }
            cur->next = NULL;
            head = dummy->next;
        }
        return head;
    }
};
  • Java
class Solution { 
   
    public ListNode merge(ListNode l1, ListNode l2) { 
   
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val < l2.val) { 
   
            l1.next = merge(l1.next, l2);
            return l1;
        } else { 
   
            l2.next = merge(l1, l2.next);
            return l2;
        }
    }

    public ListNode sortList(ListNode head) { 
   
        if (head == null || head.next == null) return head;
        // 快慢指针,寻找中间点
        ListNode s = head, f = head;
        while (f.next != null && f.next.next != null) { 
   
            s = s.next; f = f.next.next;
        }
        ListNode newHead = s.next;
        s.next = null;  // 断开链表,分成前后两部分

        ListNode left = sortList(head), right = sortList(newHead);
        return merge(left, right);  // 返回合并后的链表头指针
    }
}

时空复杂度分析

  • 时间复杂度: O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))n为链表长度。

  • 空间复杂度:C++ O ( 1 ) O(1) O(1)Java O ( l o g ( n ) ) O(log(n)) O(log(n))

3 链表快速排序

题目描述:AcWing 1451. 单链表快速排序

在这里插入图片描述

分析

  • 使用三个虚拟头指针left, mid, right,记录每次partition的结果,这里取头结点val的值作为分界线。

  • 递归的过程中,我们每次都要遍历整个链表,对节点值小于val的节点接到left中,节点值等于val的节点接到mid中,节点值大于val的节点接到right中,之后还要将三个链表的尾结点置为空。

  • 接着递归处理left、right,递归结束后将三段拼接起来即可。

代码

  • C++
class Solution { 
   
public:
    ListNode* quickSortList(ListNode* head) { 
   
        
        if (!head || !head->next) return head;
        
        auto left = new ListNode(-1), mid = new ListNode(-1), right = new ListNode(-1);
        auto ltail = left, mtail = mid, rtail = right;
        int val = head->val;
        
        for (auto p = head; p; p = p->next) { 
   
            if (p->val < val) ltail = ltail->next = p;
            else if (p->val == val) mtail = mtail->next = p;
            else rtail = rtail->next = p;
        }
        
        ltail->next = mtail->next = rtail->next = NULL;
        left->next = quickSortList(left->next);
        right->next = quickSortList(right->next);
        
        // 拼接三个链表
        get_tail(left)->next = mid->next;
        get_tail(mid)->next = right->next;
        
        auto p = left->next;
        
        delete left;
        delete mid;
        delete right;
        
        return p;
    }
    
    // 获取链表的尾节点
    ListNode* get_tail(ListNode* head) { 
   
        while (head->next) head = head->next;
        return head;
    }
};

时空复杂度分析

  • 时间复杂度: O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))n为链表长度。

  • 空间复杂度: O ( l o g ( n ) ) O(log(n)) O(log(n))

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183569.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • restful接口定义_主板上的spi接口接什么

    restful接口定义_主板上的spi接口接什么由于在实际项目中碰到的restful服务,参数都以json为准。这里我获取的接口和传入的参数都是json字符串类型。发布restful服务可参照文章http://www.cnblogs.com/jav

    2022年8月6日
    2
  • Ubuntu 优化、美化(主题、终端)[通俗易懂]

    Ubuntu 优化、美化(主题、终端)[通俗易懂]Ubuntu优化、美化(主题、终端)零效果图一优化Ubuntu\1系统更新\2安装GDebi(第三方软件安装)\3安装搜狗输入法\4软件卸载,安装4.1卸载libreOffice安装WPS4.2卸载掉亚马逊链接4.3卸载firebox浏览器安装Chrome/Chromium浏览器\5修改更新源\6vim配置\6菜单栏位置\7二美化Ubuntu\1主题1.1安装unity-tweak-tool:1.2Flatabulous主题\

    2022年7月22日
    15
  • SQLyog下载_下载地址

    SQLyog下载_下载地址下载地址:百度网盘请输入提取码链接:https://pan.baidu.com/s/1Xowhx0uuAxykHPmuXztNEQ提取码:e9o2希望对你有所帮助~

    2022年9月15日
    0
  • 集锦——浏览器每次访问自动更新网页,不用手工设置,附Google/firefox/Ie

    在做开发web开发的时候,经常要使用到浏览器来着进行调试,那么有时候自己修改了内容,可是在浏览器上还是没有显示出来,费了好久才发现是浏览器缓存的问题。还要强制刷新缓存 就是 Ctrl + F5。所以在开发时候,最好将自己的调试浏览器设置为访问自动刷新网页,不要使用页面的缓存。下面就是常用开发浏览器的设置:1. Chrome 浏览器打开到开发者模式:将Disable cache

    2022年2月24日
    265
  • 详解Jvm内存结构

    详解Jvm内存结构近期在学习研究内存,那么Jvm内存结构和Jvm的内存模型即JMM(Javamomerymodel)这两项内容都是学习java虚拟机、java内存知识的基础。为了让自己加深理解,有不至于嵌入到底层细节太深,会通过一段代码片段类比到内存结构图中的真实呈现。一、Jvm体系的内存结构图JVM运行数据区,也就是我们所说的内存结构主要分为两个大区:1、线程共享区**方法区(MethodArea)????*存储运行时的常量池、被虚拟机加载过的类信息、常量、静态变量、即时编译器编译后的代码数据。**堆区(

    2022年5月8日
    45
  • springcloud版本号

    springcloud版本号因为SpringCloud不同其他独立项目,它拥有很多子项目的大项目。所以它是的版本是版本名+版本号,下面这些都是它的一些版本名:这些Angle,Brixton,Camden等都是伦敦地铁站的名字,他们按照字母顺序发行,就是版本的演进.当一个版本的SpringCloud项目的发布内容积累到临界点或者一个严重bug解决可用后,就会发布一个“servicereleases”版本,简称SR…

    2022年5月18日
    54

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号