当前位置: 首页 > news >正文

数据结构(20)

目录

插入排序

3、表插入排序

(1)核心思想

(2)具体步骤(示例演示)

(3)算法实现(单链表代码示例)

(4)算法特性

(5)与直接插入排序(数组)的对比

4、希尔(shell)排序

(1)核心思想

(2)增量序列的选择

(3)具体步骤(示例演示)

(4)算法实现(代码示例,Shell 增量)

(5)算法特性

(6)与直接插入排序的对比

插入排序

3、表插入排序

表插入排序是直接插入排序在链表(线性表的链式存储结构) 上的实现,核心思想与直接插入排序一致 —— 将待排序元素逐个插入到已排序部分的合适位置,但利用链表的指针特性避免了直接插入排序中大量的元素后移操作,更适合链式存储的场景。

(1)核心思想

  1. 初始状态:将链表的第一个节点视为 “已排序部分”(仅含一个节点,天然有序),剩余节点组成 “待排序部分”。
  2. 逐个插入:从待排序部分的第一个节点开始,依次取出每个节点(称为 “当前节点”),与已排序部分的节点从前往后(或从后往前)比较,找到其在已排序部分中的正确位置。
  3. 调整指针:通过修改链表的指针,将当前节点插入到正确位置,使已排序部分始终保持有序。由于链表的节点物理地址不连续,插入时无需移动元素,只需调整前后节点的指针即可。

(2)具体步骤(示例演示)

以单链表 5 → 3 → 8 → 4 → 2(箭头表示 next 指针)为例,排序目标是升序,步骤如下:

1. 初始状态

  • 已排序部分:5(头节点为 5)。
  • 待排序部分:3 → 8 → 4 → 2(当前待插入节点为 3)。

2. 插入节点 3

  • 比较 3 与已排序部分的 53 < 5,需插入到 5 之前。
  • 调整指针:将 3 的 next 指向 5,已排序部分变为 3 → 5
  • 待排序部分剩余:8 → 4 → 2

3. 插入节点 8

  • 比较 8 与已排序部分的 38 > 3,继续比较下一个节点 58 > 5,需插入到 5 之后。
  • 调整指针:5 的 next 指向 88 的 next 为 None,已排序部分变为 3 → 5 → 8
  • 待排序部分剩余:4 → 2

4. 插入节点 4

  • 比较 4 与 34 > 3,继续比较 54 < 5,需插入到 3 与 5 之间。
  • 调整指针:3 的 next 指向 44 的 next 指向 5,已排序部分变为 3 → 4 → 5 → 8
  • 待排序部分剩余:2

5. 插入节点 2

  • 比较 2 与 32 < 3,需插入到 3 之前。
  • 调整指针:2 的 next 指向 3,已排序部分变为 2 → 3 → 4 → 5 → 8
  • 待排序部分为空,排序结束。

例子:

(3)算法实现(单链表代码示例)

class ListNode:def __init__(self, val):self.val = valself.next = Nonedef list_insertion_sort(head):if not head or not head.next:return head  # 空链表或单个节点无需排序# 已排序部分的头节点(初始为第一个节点)sorted_head = head# 待排序部分的第一个节点(初始为第二个节点)current = head.nextsorted_head.next = None  # 断开已排序部分与待排序部分的连接while current:# 保存下一个待排序节点next_node = current.next# 寻找插入位置:prev是插入位置的前一个节点prev = Nonecurr_sorted = sorted_head# 遍历已排序部分,找到第一个大于current.val的节点while curr_sorted and curr_sorted.val <= current.val:prev = curr_sortedcurr_sorted = curr_sorted.next# 插入current到已排序部分if prev is None:# 插入到已排序部分的头部current.next = sorted_headsorted_head = currentelse:# 插入到prev与curr_sorted之间prev.next = currentcurrent.next = curr_sorted# 处理下一个待排序节点current = next_nodereturn sorted_head# 测试:构建链表 5→3→8→4→2
head = ListNode(5)
head.next = ListNode(3)
head.next.next = ListNode(8)
head.next.next.next = ListNode(4)
head.next.next.next.next = ListNode(2)# 排序
sorted_head = list_insertion_sort(head)# 输出排序结果
result = []
while sorted_head:result.append(sorted_head.val)sorted_head = sorted_head.next
print(result)  # 输出:[2, 3, 4, 5, 8]

(4)算法特性

  1. 时间复杂度

    • 比较次数:与直接插入排序相同,最坏和平均情况均为 O(n2)(需逐个比较已排序部分的节点)。
    • 移动次数:无需移动元素,仅需调整指针(O(1) 操作),因此实际效率优于数组上的直接插入排序(尤其数据量大时,避免了大量元素后移的开销)。
    • 整体复杂度:O(n2)。
  2. 空间复杂度:仅需常数级额外空间(存储指针变量),空间复杂度为 O(1)(原地排序,通过修改指针实现)。

  3. 稳定性:稳定排序。当待插入节点与已排序节点的值相等时,会插入到相等节点的后面(因比较条件为 <=),保持原有相对顺序。

  4. 适用场景

    • 链式存储结构(单链表、双链表),不适合顺序表(数组)。
    • 数据量较小或接近有序的链表(大规模数据时 O(n2) 复杂度仍效率较低)。
    • 需频繁插入元素的场景(链表插入操作代价低)。

(5)与直接插入排序(数组)的对比

对比维度直接插入排序(数组)表插入排序(链表)
存储结构顺序表(数组)链表
插入操作需移动大量元素(O(n))仅调整指针(O(1))
比较次数O(n2)O(n2)
空间开销低(随机访问)稍高(需存储指针)
适用场景数组、小规模或接近有序数据链表、需频繁插入的场景

表插入排序是直接插入排序在链表上的适配实现,利用链表的指针特性消除了元素后移的开销,在链式存储场景中效率优于数组上的直接插入排序。其核心仍是 “逐个插入构建有序序列”,但通过指针操作优化了插入过程,适合处理链式结构的小规模或接近有序数据。

4、希尔(shell)排序

希尔排序(Shell Sort)是对直接插入排序的改进,由 Donald Shell 于 1959 年提出。其核心思想是通过 “分组插入排序” 逐步缩小间隔(增量),使序列整体逐渐接近有序,最后再进行一次直接插入排序(间隔为 1)。这种 “先宏观调整,再微观优化” 的策略,大幅提升了插入排序的效率,尤其适合中等规模数据的排序。

(1)核心思想

  1. 分组排序:将整个序列按某个间隔(增量)gap 分成若干子序列,每个子序列由 “间隔为gap的元素” 组成(如gap=3时,子序列为(a0​,a3​,a6​,...)、(a1​,a4​,a7​,...)等)。
  2. 逐组插入:对每个子序列分别执行直接插入排序,使每组内的元素初步有序。
  3. 缩小间隔:减小增量gap(如取gap=gap/2),重复步骤 1-2,直到gap=1。
  4. 最终排序:当gap=1时,整个序列被视为一个子序列,执行一次直接插入排序,此时序列已接近有序,插入排序效率极高。

(2)增量序列的选择

增量gap的选择直接影响希尔排序的效率,常见的增量序列有:

  • Shell 初始增量:gap=n/2,gap=gap/2,直至gap=1(简单但效率一般)。
  • Hibbard 增量:gap=2k−1(如 1,3,7,15,...),效率优于 Shell 增量。
  • Sedgewick 增量:gap=4k+3×2k−1+1(如 1,5,19,41,...),在实践中效率较高。

:增量序列需满足 “最后一个增量必须为 1”,且通常 “增量互质”(避免重复排序)。

(3)具体步骤(示例演示)

以序列 [10, 1, 9, 2, 8, 3, 7, 4, 6, 5](n=10)为例,采用 Shell 初始增量(gap=5→2→1):

1. 第一趟:gap=5(分组排序)

  • 分组规则:间隔 5 取元素,共分成 5 组:组 1:[10, 3]、组 2:[1, 7]、组 3:[9, 4]、组 4:[2, 6]、组 5:[8, 5]
  • 每组执行直接插入排序:组 1:[3, 10]、组 2:[1, 7]、组 3:[4, 9]、组 4:[2, 6]、组 5:[5, 8]
  • 排序后序列:[3, 1, 4, 2, 5, 10, 7, 9, 6, 8](整体更接近有序)。

2. 第二趟:gap=2(分组排序)

  • 分组规则:间隔 2 取元素,共分成 2 组:组 1:[3, 4, 5, 7, 6](索引 0,2,4,6,8)、组 2:[1, 2, 10, 9, 8](索引 1,3,5,7,9)。
  • 每组执行直接插入排序:组 1:[3, 4, 5, 6, 7]、组 2:[1, 2, 8, 9, 10]
  • 排序后序列:[3, 1, 4, 2, 5, 8, 6, 9, 7, 10](有序度进一步提升)。

3. 第三趟:gap=1(直接插入排序)

  • 此时序列已接近有序,执行一次直接插入排序:最终结果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

例子:

(4)算法实现(代码示例,Shell 增量)

def shell_sort(arr):n = len(arr)gap = n // 2  # 初始增量while gap > 0:# 对每个子序列执行直接插入排序for i in range(gap, n):key = arr[i]  # 待插入元素j = i - gap   # 子序列中前一个元素的索引# 子序列内比较并后移while j >= 0 and key < arr[j]:arr[j + gap] = arr[j]j -= gap# 插入待排序元素arr[j + gap] = keygap = gap // 2  # 缩小增量return arr# 测试
arr = [10, 1, 9, 2, 8, 3, 7, 4, 6, 5]
print(shell_sort(arr))  # 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

(5)算法特性

  1. 时间复杂度

    • 时间复杂度与增量序列密切相关,目前尚未找到最优增量序列。
    • 最坏情况:Shell 增量下为 O(n2),Hibbard 增量下为 O(n3/2),Sedgewick 增量下接近 O(nlog2n)。
    • 平均情况:通常认为在 O(n1.3) 左右,优于直接插入排序的 O(n2)。
  2. 空间复杂度:仅需常数级额外空间(存储 keygap 等),空间复杂度为 O(1)(原地排序)。

  3. 稳定性不稳定排序。因分组排序时,相同元素可能被分到不同组,插入过程中可能改变相对顺序(如 [2, 1, 2] 以 gap=2 排序时,第一个 2 会被分到组 1,第二个 2 分到组 2,最终顺序可能颠倒)。

  4. 适用场景

    • 中等规模数据(n 为几千到几万),效率优于简单排序(插入、冒泡)。
    • 顺序表(数组)结构(依赖随机访问,不适合链表,因分组时需按间隔访问元素)。

(6)与直接插入排序的对比

对比维度直接插入排序希尔排序
核心策略逐元素插入,整体一次排序分组插入,多轮缩小间隔
时间复杂度O(n2)O(n1.3)∼O(n2)
稳定性稳定不稳定
适用数据规模小规模(n 较小)中等规模(n 中等)
有序度影响对接近有序数据效率高对无序数据效率提升明显

希尔排序通过 “分组缩小增量” 的策略,突破了直接插入排序在无序数据上的效率瓶颈,是一种 “半突破 O(n2)” 的排序算法。其核心是利用 “远距离元素先排序” 减少后续插入排序的移动次数,在中等规模数据排序中表现优异。尽管稳定性不足且增量选择复杂,但其高效性使其成为实践中常用的排序算法之一。

http://www.dtcms.com/a/596671.html

相关文章:

  • 线性代数 - 理解求解矩阵特征值的特征方程
  • Swift的逃逸闭包
  • ESP32基础-GPIO_LED进阶
  • AT指令连接onenet平台(mqtt协议)
  • 二分搜索中 `right = mid` 而非 `right = mid + 1` 的解释
  • 走进Linux的世界:进程优先级
  • 蛙蛙写作网站中国建设银行网站-个人客
  • jetson开机之前自启脚本sudo ifconfig 如何不需要输入密码
  • 环评登记表在哪个网站做php网站开发试题
  • K8S RD: Kubernetes核心技术之管理、高可用与配置详解
  • Rust:Trait 抽象接口 特征约束
  • 【Windows】tauri+rust运行打包工具链安装
  • 网站被人抄袭怎么办哪家做网站的公司
  • 在 Linux 上实现 Spring Boot 程序的自动启动与守护运行
  • 得物TiDB升级实践
  • uni-app微信小程序相机组件二次拍照白屏问题的排查与解决
  • 邯郸大名网站建设网站服务器类型查询
  • 远程在线诊疗|在线诊疗|基于java和小程序的在线诊疗系统小程序设计与实现(源码+数据库+文档)
  • Linux进程间通信(IPC)常用方法精要
  • 展望无人机的未来发展,技术趋势和应用前景
  • 黄页 网站模板什么是展示型网站
  • gov域名网站有哪些如何建一个免费试用网站
  • Vue中 class 和 style 属性的区别对比
  • 视频融合平台EasyCVR:构建智慧化城市/乡镇污水处理厂综合安防与运营监管方案
  • 【ZeroRange WebRTC】KVS WebRTC C SDK 崩溃分析报告
  • 库卡机器人编程语言 | 深入了解库卡机器人的编程方法与应用
  • 移动+协作+视觉=?复合型机器人重新定义智能产线
  • 【macOS 版】Android studio jdk 1.8 gradle 一键打包成 release 包的脚本
  • 网站关键词优化原理亳州做企业网站
  • 数据库知识整理——SQL数据定义