Swift实现高效链表排序:一步步解读

在这里插入图片描述
在这里插入图片描述

文章目录

    • 前言
    • 摘要
    • 问题描述
    • 题解
      • 解题思路
      • Swift 实现代码
      • 代码分析
      • 示例测试与结果
    • 时间复杂度
    • 空间复杂度
    • 总结
    • 关于我们

前言

本题由于没有合适答案为以往遗留问题,最近有时间将以往遗留问题一一完善。

148. 排序链表

不积跬步,无以至千里;不积小流,无以成江海,Swift社区 伴你前行。如果大家有建议和意见欢迎在文末留言,我们会尽力满足大家的需求。

难度水平:中等

摘要

如何高效地对链表进行升序排序一直是算法中的经典问题之一。本文将深入解析如何使用Swift语言实现基于归并排序的链表排序算法,满足O(n log n)的时间复杂度和常数级空间复杂度的需求。同时,提供一段可运行的代码模块,帮助读者快速理解和实战。

问题描述

给定一个单链表的头结点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 = []
    输出:[]

约束条件

  • 链表中的节点数范围:[0, 5 * 10^4]
  • 节点值范围:[-10^5, 10^5]

进阶要求

O(n log n)时间复杂度和常数级空间复杂度下完成链表排序。

题解

解题思路

归并排序是链表排序的理想选择,因其适合链表的顺序特性,并能够在O(n log n)时间复杂度内完成排序:

  1. 分割链表:递归地将链表从中间分为两个子链表,直到每个子链表只有一个节点。
  2. 合并有序链表:将两个已排序的链表按照升序合并成一个新的有序链表。

Swift 实现代码

class ListNode {
    var val: Int
    var next: ListNode?

    init(_ val: Int) {
        self.val = val
        self.next = nil
    }
}

func sortList(_ head: ListNode?) -> ListNode? {
    // 基本情况:空链表或只有一个节点
    guard let head = head, head.next != nil else {
        return head
    }

    // 使用快慢指针找到链表中点
    let mid = findMiddle(head)
    let rightHead = mid.next
    mid.next = nil

    // 递归对两部分排序
    let left = sortList(head)
    let right = sortList(rightHead)

    // 合并排序后的两部分
    return merge(left, right)
}

func findMiddle(_ head: ListNode) -> ListNode {
    var slow = head
    var fast = head

    while fast.next != nil, fast.next?.next != nil {
        slow = slow.next!
        fast = fast.next!.next!
    }

    return slow
}

func merge(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
    let dummy = ListNode(0)
    var current = dummy
    var l1 = l1
    var l2 = l2

    while let node1 = l1, let node2 = l2 {
        if node1.val < node2.val {
            current.next = node1
            l1 = node1.next
        } else {
            current.next = node2
            l2 = node2.next
        }
        current = current.next!
    }

    current.next = l1 ?? l2
    return dummy.next
}

代码分析

  1. 查找链表中点

    • 使用快慢指针找到链表的中间节点并将链表分成两部分。
    • 时间复杂度:O(n)
    • 空间复杂度:O(1)
  2. 递归排序

    • 不断分割链表直到只有一个节点时停止递归,然后逐步合并已排序的链表。
  3. 合并有序链表

    • 使用双指针遍历两个已排序链表,按值大小将节点逐个合并。
    • 时间复杂度:O(n)
    • 空间复杂度:O(1)

示例测试与结果

测试代码

func printList(_ head: ListNode?) {
    var current = head
    var result: [Int] = []
    while let node = current {
        result.append(node.val)
        current = node.next
    }
    print(result)
}

// 创建示例链表:[4, 2, 1, 3]
let head = ListNode(4)
head.next = ListNode(2)
head.next?.next = ListNode(1)
head.next?.next?.next = ListNode(3)

// 调用排序函数
let sortedHead = sortList(head)

// 打印结果
printList(sortedHead) // 输出:[1, 2, 3, 4]

测试结果

  • 输入:[4, 2, 1, 3]
    输出:[1, 2, 3, 4]
  • 输入:[-1, 5, 3, 4, 0]
    输出:[-1, 0, 3, 4, 5]
  • 输入:[]
    输出:[]

时间复杂度

  • 分割链表:每次递归调用的时间为O(n),最多递归log n次,总复杂度为O(n log n)
  • 合并链表:每层递归的合并操作处理所有节点,总复杂度为O(n)

总时间复杂度:O(n log n)

空间复杂度

  • 除递归调用外无需额外的存储空间,递归栈空间为O(log n)

总空间复杂度:O(log n)

总结

本文展示了如何在Swift中使用归并排序对链表进行高效排序,从分割链表到合并排序,算法步骤清晰,代码简洁。无论是处理小型链表还是大型数据集,该方法都能够在O(n log n)的时间内完成排序,是面试和实际开发中的理想解决方案。

关于我们

我们是由 Swift 爱好者共同维护,我们会分享以 Swift 实战、SwiftUI、Swift 基础为核心的技术内容,也整理收集优秀的学习资料。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小程序-基于java+SpringBoot+Vue的校园快递平台系统设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

网页开发的http基础知识

请求方式-GET&#xff1a;请求参数在请求行中&#xff0c;没有请求体&#xff0c;如&#xff1a;/brand/findAll?nameoPPo&status1。GET请求大小在浏览器中是有限制的请求方式-POST&#xff1a;请求参数在请求体中&#xff0c;POST请求大小是没有限制的 HTTP请求&#xf…

Qt自定义 Qt Designer 插件

创建 Qt Designer 插件项目 Qt 提供两种设计插件的 API&#xff0c;可以用于扩展 Qt 的功能。高级 API 用于设计插件以扩展 Qt 的功能&#xff0c;例如定制数据库驱动、图像格式、文本编码、定制样式等。Qt Designer 里大量采用了插件&#xff0c;点击 Qt Creator 的“Help”-…

周鸿祎再次“创业”,盯上百度

周鸿祎特地拍了部短剧来推广的新产品&#xff0c;终于上线了。 11月27日晚间&#xff0c;360正式发布多模态内容创作引擎“纳米搜索”。 作为当前AI应用最红的赛道之一&#xff0c;AI搜索已经有腾讯、秘塔、商汤、抖音等公司入局。传统搜索老大百度也在发力。竞争不妨碍有搜索…

003 MATLAB基础计算

01 方程组的求解 多项式及其运算 多项式在MATLAB中以向量形式存储。 即n次多项式用一个长度为n1的系数向量来表示&#xff0c;且按降幂&#xff0c;缺少的幂次对应的向量元素为0。 多项式的运算主要包括多项式的四则运算、求导、求值和求根运算 多项式的四则运算&#xff1a…

金蝶云苍穹:个人上传授权文件

金蝶云苍穹开发者门户--下载文件地址。

解决windows下php8.x及以上版本,在Apache2.4中无法加载CURL扩展的问题

本文已首发于&#xff1a;秋码记录 若你也想搭建一个个人博客&#xff0c;可参考&#xff1a;国内 gitee.com Pages 下线了&#xff0c;致使众多站长纷纷改用 github、gitlab Pages 托管平台 在日新月异的信息化下&#xff0c;软件也在跟随着互联网的脚步&#xff0c;逐步推进…

数据库管理-第267期 23ai:Oracle Data Redaction演示(20241128)

数据库管理267期 2024-11-286 数据库管理-第267期 23ai&#xff1a;Oracle Data Redaction演示&#xff08;20241128&#xff09;1 示例表及数据2 创建编校策略2.1 名字全编校2.2 电话部分编校 3 DML演示3.1 场景13.2 场景2 总结 数据库管理-第267期 23ai&#xff1a;Oracle Da…

根据电池容量及功耗估算充电及放电时间

根据电池容量和功耗估算充电和放电时间的方法可以通过以下简单的公式进行&#xff1a; 1. 估算放电时间 放电时间是指电池在一定功耗下&#xff0c;能够持续供应电力的时间。可以使用以下公式&#xff1a; 解释&#xff1a; 电池容量&#xff1a;电池的容量一般以毫安时&…

【Maven】继承和聚合

5. Maven的继承和聚合 5.1 什么是继承 Maven 的依赖传递机制可以一定程度上简化 POM 的配置&#xff0c;但这仅限于存在依赖关系的项目或模块中。当一个项目的多个模块都依赖于相同 jar 包的相同版本&#xff0c;且这些模块之间不存在依赖关系&#xff0c;这就导致同一个依赖…

对抗攻击算法:FGSM和PGD

FGSM 传送门 FGSM 利用了梯度上升的思想&#xff0c;通过损失函数相对于输入图像的梯度来找到 最容易 迷惑网络的方向&#xff0c;并沿着这个方向对图像进行微小的扰动。 FGSM 的基本想法是&#xff0c;沿着这个梯度的符号方向对图像进行微调&#xff0c;以最大化损失函数。具…

Matlab mex- setup报错—错误使用 mex,未检测到支持的编译器...

错误日志&#xff1a; 在使用mex编译时报错提示&#xff1a;错误使用 mex&#xff0c;未检测到支持的编译器。您可以安装免费提供的 MinGW-w64 C/C 编译器&#xff1b;请参阅安装 MinGW-w64 编译器。有关更多选项&#xff0c;请访问https://www.mathworks.com/support/compile…

内网穿透步骤

步骤 第一次需要验证token window和linux的方法不同。 然后 启动 cpolar 服务&#xff1a; 在命令窗口中输入 cpolar.exe htttp 8080&#xff0c;启动内网穿透服务。确保命令窗口保持开启状态&#xff0c;以维持穿透效果。 cpolar.exe hhttp 8080 成功后 注意事项 命令窗口…

系统架构:MVVM

引言 MVVM 全称 Model-View-ViewModel&#xff0c;是在 MVP&#xff08;Model-View-Presenter&#xff09;架构模式基础上的进一步演进与优化。MVVM 与 MVP 的基本架构相似&#xff0c;但 MVVM 独特地引入了数据双向绑定机制。这一创新机制有效解决了 MVP 模式中 Model 与 Vie…

网络协议(TCP/IP模型)

目录 网络初识 网络协议 协议分层 协议拆分 分层 协议分层的优势 1.封装效果 2.解耦合 TCP/IP五层模型 协议之间配合工作&#xff08;详解&#xff09; 网络初识 网络核心概念&#xff1a; 局域网&#xff1a;若干电脑连接在一起&#xff0c;通过路由器进行组网。 …

网络安全之IP伪造

眼下非常多站点的涉及存在一些安全漏洞&#xff0c;黑客easy使用ip伪造、session劫持、xss攻击、session注入等手段危害站点安全。在纪录片《互联网之子》&#xff08;建议搞IT的都要看下&#xff09;中。亚伦斯沃茨&#xff08;真实人物&#xff0c;神一般的存在&#xff09;涉…

软件工程之静态建模

静态模型&#xff1a;有助于设计包、类名、属性和方法特征标记&#xff08;但不是方法体&#xff09;的定义&#xff0c;例如UML类图。 用例的关系&#xff1a; 扩展关系&#xff1a; 扩展关系允许一个用例&#xff08;可选&#xff09;扩展另一个用例&#xff08;基用例&…

JS听到了爆燃的回响

Window对象 BOM&#xff08;浏览器对象模型&#xff09; BOM是浏览器对象模型 Window对象是一个全局对象&#xff0c;也可以说是JS中的顶级对象 像是document、alert()、console.log()都是window的属性 所有通过var定义在全局作用域的变量、函数都会变成window对象的属性和…

【Linux】死锁、读写锁、自旋锁

文章目录 1. 死锁1.1 概念1.2 死锁形成的四个必要条件1.3 避免死锁 2. 读者写者问题与读写锁2.1 读者写者问题2.2 读写锁的使用2.3 读写策略 3. 自旋锁3.1 概念3.2 原理3.3 自旋锁的使用3.4 优点与缺点 1. 死锁 1.1 概念 死锁是指在⼀组进程中的各个进程均占有不会释放的资源…

单片机学习笔记 15. 串口通信(理论)

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…