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

链表排序最优算法_链表算法题链表排序算法总结概述问题描述:给定一个链表,请将这个链表升序排列。节点定义: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))

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Java课程设计【学生信息管理系统】[通俗易懂]

    Java课程设计【学生信息管理系统】[通俗易懂]课程设计目录一、问题描述二、基本要求三、需求分析四、概要设计1、类之间的调用关系2、学生信息模块3、管理系统模块4、详细设计①主程序LoginGUI的代码②程序View的代码③程序Student的代码④程序ConnectSQLServer的代码五、调试分析六、用户使用说明1、登录2、添加3、查询4、修改5、删除6、退出七、测试结果八、课程设计总结九、参考文献一、问题描述如何实现一个功能简单的学生信息管理系统,能够对学生信息(包括照片)进行添加、删除、修改和查询等操作。二、基本要求实现一个功能简单的学

    2022年10月17日
  • 全新E:网站不是之前排名浮动 相比于竞争对手究竟缺少了什么?

    全新E:网站不是之前排名浮动 相比于竞争对手究竟缺少了什么?

  • 测试用例设计的八大要素及ANSI/IEEE 829标准和编写示例[通俗易懂]

    测试用例设计的八大要素及ANSI/IEEE 829标准和编写示例[通俗易懂]1、测试用例的八大要素1.用例编号和其他编号一样,测试用例编号是用来唯一识别测试用例的编号,要求具有易识别和易维护性,用户可以很容易根据用例编号获取到相应用例的目的和作用,在系统测试用例中,编号的一般格式为A-B-C-D这几部分的作用分别如下:A:产品或项目类型,如CMS(内容管理系统)、CRM(客户关系管理系统)B:一般用来说明用例的属性,如ST(系统测试)、IT(集成测试)、UT(单元测试)C:测试需求的表示,说明该用例针对的需求点,可包括测试项和测试子项等,如文档管理、客户投诉信息管理

  • 管理大数据存储的十大技巧「建议收藏」

    管理大数据存储的十大技巧「建议收藏」数据本地化是为了确保大数据集存储在计算节点附近便于分析。对于Hadoop,这意味着管理数据节点,向MapReduce提供存储以便充分执行分析。它实用有效但也出现了大数据存储集群的独立操作问题。以下十项是Hadoop环境中管理大数据存储技巧。在1990年,每一台应用服务器都倾向拥有直连式系统(DAS)。SAN的构建则是为了更大的规模和更高的效率提供共享的池存储。Hadoop已经逆转了这一趋势回归DA…

  • JSP简介

    JSP简介JSP简介

  • 动手小游戏_飞机大战激活成功教程版

    动手小游戏_飞机大战激活成功教程版一、关于飞机大战要说微信中最火爆的小游戏是哪款,可能既不是精心打造的3D大作,也不是《植物大战僵尸2》,而是微信5.0刚开启时的《飞机大战》。就是这样一款铅笔手绘风格的简单到不能再简单的“打飞机”

发表回复

您的电子邮箱地址不会被公开。

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