首页 > 其他分享 >leetcode148. 排序链表-归并法

leetcode148. 排序链表-归并法

时间:2024-03-22 14:34:57浏览次数:20  
标签:归并 null leetcode148 head next 链表 排序

148. 排序链表

题干

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

示例 1:

image

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

示例 2:

image

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 104]
  • -105 <= Node.val <= 105

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

思路

  1. 从链表中点处断开链表
  2. 递归左右链表,递归的返回值为排序之后的左链表与右链表
  3. 同时遍历左右链表,将左右链表重新排序成一个新链表
  4. 返回这个新链表

假设有一个数值为[5, 2, 1, 4, 3]的链表需要排序。每一个彩色框代表进入一次递归:
image

代码&gpt总结

这段代码实现了对链表的排序算法,采用了归并排序的思想。以下是对这段算法的详细分析:

  1. 算法逻辑

    算法首先检查输入的链表头节点是否为空,如果为空,则直接返回null。使用快慢指针方法找到链表中点,快指针每次移动两步,慢指针每次移动一步,当快指针无法继续前进时,慢指针所在的位置即为中点。这样,链表被分为左右两部分,分别对这两部分递归地进行排序。最后,使用归并的方式合并两个已排序的链表部分,得到完全排序后的链表。

  2. 变量解释

    • head: 输入的链表头节点。
    • f (fast): 快指针,用于找到链表的中点。
    • s (slow): 慢指针,随着快指针的移动而移动,当快指针到达链表末尾时,慢指针位于链表中点。
    • l (left): 表示分割后的左半部分链表的头节点。
    • r (right): 表示分割后的右半部分链表的头节点。
    • dummyNewHead: 一个哑节点,用于辅助归并排序的过程。
    • nowNode: 当前操作的节点,用于构建排序后的链表。
  3. 关键代码分析

    • while (f.next?.next): 快慢指针移动,直到快指针到达链表末尾或无法继续前进。
    • s.next = null: 将链表从中点断开,分为左右两部分。
    • 递归调用sortList(l)sortList(r): 对左右两部分链表分别进行排序。
    • while (l && r): 归并排序的主要步骤,比较左右两部分链表的头节点,将较小的节点连接到结果链表上,直到一方为空。
    • nowNode.next = l || r: 将剩余的非空链表部分连接到结果链表的尾部。
  4. 性能分析

    • 时间复杂度:O(n log n),其中n是链表的长度。归并排序的时间复杂度为O(log n),对于每一层归并,需要遍历所有节点,故总的时间复杂度为O(n log n)。
    • 空间复杂度:O(log n),递归调用的栈空间。由于是使用递归实现的,空间复杂度主要由递归深度决定,即链表的分割次数,为O(log n)。
  5. 适用场景

    • 对不支持随机访问的数据结构,如链表进行排序时,归并排序是非常合适的选择,因为它不依赖于随机访问。
    • 在需要稳定排序算法的场景下,归并排序保持了相同元素之间的相对顺序,适用于此类需求。
    • 当处理的数据量很大,且数据无法全部加载到内存中时,归并排序能够有效地分治处理,适合外部排序。
function sortList(head: ListNode | null): ListNode | null {
  if (head === null) return null;

  let f: ListNode | null = head;
  let s: ListNode = head;

  // 寻找链表中点
  while (f.next?.next) {
    f = f.next?.next ?? null;
    s = s.next!;

    if (!f?.next) break;
  }

  let l: ListNode | null = head;
  let r = s.next;
  // 断开链表
  s.next = null;
  // 左右链表递归的进行排序,返回排序完成的左右链表
  if (l?.next) l = sortList(l);
  if (r?.next) r = sortList(r);
  const dummyNewHead = new ListNode(-1);
  let nowNode = dummyNewHead;
  // 遍历排序完成的左右链表,将两个链表合并成一个排序完成的链表
  while (l && r) {
    let nextNode = null;
    if (l?.val < r?.val) {
      nextNode = l;
      l = l!.next ?? null;
    } else {
      nextNode = r;
      r = r!.next ?? null;
    }
    nowNode.next = nextNode as ListNode;
    nowNode = nowNode.next;
  }
  nowNode.next = l || r;

  // 返回排序完成的链表
  return dummyNewHead.next;
}

标签:归并,null,leetcode148,head,next,链表,排序
From: https://www.cnblogs.com/CatCatcher/p/18089398

相关文章

  • 删除有序链表中重复的元素-1
    描述删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次例如:给出的链表\(1\to1\to2\),返回\(1\to2\)给出的链表为\(1\to1\to2\to3\to3\),返回\(1\to2\to3\)数据范围:链表长度满足\(0\len\le100\),,链表中任意节点的值满足\(\midval\mid\le1......
  • 合并两个排序的链表
    合并两个排序的列表题目描述输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。数据范围链表长度[0,500][0,500]。样例输入:1->3->5,2->4->5输出:1->2->3->4->5->5代码实现迭代版本structListNode{intval;ListNode*nex......
  • leedcode- 回文链表
    毫无创意的一版:#定义一个类SolutionclassSolution:#定义一个方法isPalindrome,用于检查链表是否为回文defisPalindrome(self,head:Optional[ListNode])->bool:#如果链表为空,则它是一个回文ifnothead:returnTrue......
  • 归并排序算法 java实现
    publicstaticvoidmain(String[]args){int[]arr={9,5,7,3,1,6,8,4,2};mergeSort(arr,0,arr.length-1);}/***归并排序*注意:归并的拆分数组和合并数组是从左到右依次进行的,网上很多文章都是错误的*并不是左右一起拆分,网上很多文章都是这样的......
  • 数据结构(C语言版)——单链表的查找
    1.按位查找//按位查找,返回第i个元素(带头结点)LNode*GetElem(LinkListL,inti){ if(i<0) returnfalse; LNode*p;//指针p指向当前扫描到的结点 intj=0;//当前p指向的是第几个结点 p=L;//L指向头结点,头结点是第0个结点(不存数据) while(p!=NULL&&j<i)......
  • 判断链表中是否有环
    描述判断给定的链表中是否有环,如果有环则返回True,否则返回False数据范围:链表长度\(0\len\le1000\),链表中任意节点的值满足\(\midval\mid\le100000\)输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的head头结点传入到函数里面。-1代表无环,其它的数字代表有......
  • 142. 环形链表 II
    /***Definitionforsingly-linkedlist.*structListNode{*intval;*structListNode*next;*};*/structListNode*detectCycle(structListNode*head){if(!head)returnNULL;structListNode*slow=head,*fast=head;while(fa......
  • C++STL第五篇(链表List的使用方法)
    list链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。相......
  • 【算法】多路归并(鱼塘钓鱼)
    有 N 个鱼塘排成一排,每个鱼塘中有一定数量的鱼,例如:N=5 时,如下表:鱼塘编号12345第1分钟能钓到的鱼的数量(1..1000)101420169每钓鱼1分钟钓鱼数的减少量(1..100)24653当前鱼塘到下一个相邻鱼塘需要的时间(单位:分钟)3544即:在第 11 个鱼塘中钓鱼第 11 分钟内可钓到 1010 条鱼,......
  • [学习记录]带头指针的单向链表的基本功能
    效果代码#include"stdio.h"#include"stdlib.h"typedefstructlinknode{ intdata; structlinknode*next;}LinkNode;LinkNode*CreateHeadNode(void);//创建头结点voidCreateNewNode(LinkNode*H);//创捷节点voidPrintList(LinkNode*H);//打印链表v......