2025/12/31 8:19:56
网站建设
项目流程
东莞网站建设东莞,关于市场营销的培训课程,能看的网站给我一个呗,用闲置的安卓手机做网站上次分享了6种基础排序算法后#xff0c;不少同学催更进阶款——毕竟面试和作业里#xff0c;基数排序、快速排序、随机快速排序、堆排序这些“高效选手”才是常客。作为被数据结构折磨过的大学生#xff0c;我把这4种进阶排序的原理、代码和运行过程扒得明明白白#xff0…上次分享了6种基础排序算法后不少同学催更进阶款——毕竟面试和作业里基数排序、快速排序、随机快速排序、堆排序这些“高效选手”才是常客。作为被数据结构折磨过的大学生我把这4种进阶排序的原理、代码和运行过程扒得明明白白这篇一次性讲透基数排序定义与基本思想非比较型整数排序算法按照低位先排序再收集再按照高位排序再收集依次类推直到最高位算法步骤取得数组中的最大数确定位数从最低位开始使用稳定的排序算法如计数排序对每一位进行排序重复过程直到最高位完成排序时间复杂度分析时间复杂度O(d*(nk))其中d为最大位数k为基数范围空间复杂度O(nk)适用场景与优缺点适用于整数或字符串排序优点线性时间复杂度稳定排序缺点对非整数数据不适用空间开销较大快速排序定义与基本思想分治策略的排序算法通过选取基准元素将数组分为两部分递归排序子数组算法步骤选择基准元素通常为第一个或最后一个元素分区操作将小于基准的元素移到左侧大于基准的元素移到右侧递归排序左右子数组时间复杂度分析平均时间复杂度O(n log n)最坏时间复杂度O(n²)当数组已有序时空间复杂度O(log n)递归栈空间适用场景与优缺点适用于大规模数据排序优点平均性能优秀原地排序缺点最坏情况下性能较差不稳定排序随机快速排序定义与基本思想快速排序的优化版本通过随机化选择基准元素避免最坏情况算法步骤随机选择基准元素分区操作与快速排序相同递归排序左右子数组时间复杂度分析平均时间复杂度O(n log n)最坏时间复杂度概率极低空间复杂度O(log n)适用场景与优缺点适用于需要避免最坏情况的场景优点性能稳定避免极端情况缺点随机化增加额外开销堆排序定义与基本思想基于二叉堆的排序算法通过构建最大堆或最小堆实现排序算法步骤构建最大堆或最小堆将堆顶元素与末尾元素交换调整剩余元素为新堆重复过程直到堆为空时间复杂度分析时间复杂度O(n log n)建堆和调整堆空间复杂度O(1)原地排序适用场景与优缺点适用于需要原地排序的场景优点时间复杂度稳定不需要额外空间缺点不稳定排序缓存不友好对比与总结性能对比基数排序线性时间但限制较多快速排序平均性能最优但最坏情况差随机快速排序避免最坏情况堆排序稳定但缓存效率低选择建议整数排序基数排序通用排序快速排序或随机快速排序内存受限堆排序应用场景举例基数排序电话号码排序快速排序大规模数据排序堆排序优先级队列实现一、基数排序“按位拆分逐位排序”核心思路大学生白话版像整理学号先按个位把数字分到0-9号桶里排好序再按十位重新分桶排序直到最高位处理完整个数组就有序了。本质是“按位拆分桶排序”的组合专克多位数排序。代码与运行过程def RadixSort(a): base 1 # 初始按个位排序base1 maxv max(a) while base maxv: bucket [] # 初始化0-9共10个桶 for idx in range(10): bucket.append([]) # 按当前位base的值分桶 for num in a: idx (num // base) % 10 # 取当前位的数字 bucket[idx].append(num) # 把桶里的元素按顺序放回原数组 l 0 for idx in range(10): for v in bucket[idx]: a[l] v l 1 print(a) # 打印每轮排序结果 base * 10 # 切换到更高位个位→十位→百位... a [3121,897,3122,3,23,5,55,7,97,123,345,1423] RadixSort(a)每轮按一位排序逐步让数组整体有序[3121, 3122, 3, 23, 123, 1423, 5, 55, 345, 897, 7, 97] # 按个位排序 [3, 5, 7, 3121, 3122, 23, 123, 1423, 345, 55, 897, 97] # 按十位排序 [3, 5, 7, 23, 55, 97, 3121, 3122, 123, 345, 1423, 897] # 按百位排序 [3, 5, 7, 23, 55, 97, 123, 345, 897, 1423, 3121, 3122] # 按千位排序最终有序特点时间复杂度$O(d \times (n k))$$d$是最高位数$k10$空间复杂度$O(n k)$需要10个桶稳定排序适合多位数整数排序如学号、手机号二、快速排序“选基准分左右递归排序”核心思路大学生白话版像分水果选一个“基准”比如苹果把比它小的放左边比它大的放右边再对左右两堆分别选基准、重复分堆直到每堆只有一个水果——这就是“分治”思想的经典应用。代码与运行过程def QuickSortPivot(a, start, end): pivot start # 选第一个元素做基准 j start 1 # 把比基准小的元素移到左边 for i in range(start1, end1): if a[i] a[pivot]: a[j], a[i] a[i], a[j] j 1 # 把基准放到左右区间的中间 a[pivot], a[j-1] a[j-1], a[pivot] pivot j - 1 print(a[pivot], a[start:pivot], a[pivot1:end1]) # 打印基准和左右区间 return pivot def QuickSort(a, start, end): if start end: return pivot QuickSortPivot(a, start, end) QuickSort(a, start, pivot-1) # 递归排序左区间 QuickSort(a, pivot1, end) # 递归排序右区间 a [8,5,12,6,4,3,7,9,2,1,10,11] QuickSort(a, start0, end11) print(a)每轮选基准、分左右逐步缩小排序区间8 [1,5,6,4,3,7,2] [12,9,10,11] # 基准8左小右大 1 [] [5,6,4,3,7,2] # 左区间基准1右边都是大的 5 [2,4,3] [7,6] # 左区间基准5再分左右 ...最终得到有序数组[1,2,3,4,5,6,7,8,9,10,11,12]特点时间复杂度$O(n\log n)$平均$O(n^2)$最坏数组已近有序空间复杂度$O(\log n)$递归栈不稳定排序实际应用中效率极高是工业界常用排序算法三、随机快速排序“随机选基准避免最坏情况”核心思路大学生白话版解决普通快速排序的“致命缺点”如果数组已近有序选第一个元素当基准会导致时间复杂度飙升到$O(n^2)$。随机选基准能避免这种极端情况让时间复杂度稳定在$O(n\log n)$。代码与运行过程import random def QuickSortPivot(a, start, end): # 随机选一个元素和第一个元素交换作为基准 randIdx random.randint(start, end) a[start], a[randIdx] a[randIdx], a[start] pivot start j start 1 for i in range(start1, end1): if a[i] a[pivot]: a[j], a[i] a[i], a[j] j 1 a[pivot], a[j-1] a[j-1], a[pivot] pivot j - 1 print(a[pivot], a[start:pivot], a[pivot1:end1]) return pivot def QuickSort(a, start, end): if start end: return pivot QuickSortPivot(a, start, end) QuickSort(a, start, pivot-1) QuickSort(a, pivot1, end) a [8,5,12,6,4,3,7,9,2,1,10,11] QuickSort(a, start0, end11) print(a)随机选基准后区间划分更均匀6 [5,4,3,2,1] [8,12,7,9,10,11] # 随机选基准6左小右大 3 [2,1] [5,4] # 左区间随机选基准3划分均衡 ...最终同样得到有序数组[1,2,3,4,5,6,7,8,9,10,11,12]特点时间复杂度$O(n\log n)$平均/期望空间复杂度$O(\log n)$不稳定排序比普通快速排序更稳定是实际开发的首选快速排序版本四、堆排序“利用堆结构依次取最值”核心思路大学生白话版先把数组构建成大顶堆堆顶是最大值然后把堆顶最大值和堆尾元素交换再把堆的规模缩小1重新调整成大顶堆重复这个过程直到堆为空——最终数组就是升序排列的。代码与运行过程def maxHeapify(heap, start, end): 堆调整函数将以start为根的子树调整为大顶堆 son start * 2 # 打印当前要调整的节点和其子节点范围 print(f 调整堆节点 {start} (子节点范围: {son}~{end}) | 当前堆: {heap}) while son end: # 选择左右子节点中较大的那个 if son 1 end and heap[son 1] heap[son]: son 1 print(f 选择右子节点 {son} (值更大)) # 如果子节点大于父节点交换 if heap[son] heap[start]: print(f 交换节点 {start}({heap[start]}) 和 {son}({heap[son]})) heap[start], heap[son] heap[son], heap[start] start, son son, son * 2 # 继续向下调整 print(f 调整后堆: {heap}) else: print(f 无需交换当前子树已是大顶堆) break def HeapSort(a): 堆排序主函数 heap [None] a # 堆从索引1开始方便计算父子节点 root 1 l len(heap) print( 第一步构建大顶堆 ) # 从最后一个非叶子节点开始自下而上构建大顶堆 for i in range(l//2, root-1, -1): print(f\n开始调整非叶子节点 {i}:) maxHeapify(heap, i, l-1) print(\n 第二步堆排序逐步取出最大值 ) # 逐步将堆顶最大值放到数组末尾然后调整剩余堆 for i in range(l-1, root, -1): print(f\n将堆顶({heap[root]})与末尾节点 {i}({heap[i]}) 交换 | 交换前: {heap}) heap[i], heap[root] heap[root], heap[i] print(f交换后: {heap} (已确定位置 {i} 的值为 {heap[i]})) print(f调整剩余堆范围: 1~{i-1}:) maxHeapify(heap, root, i-1) return heap[root:] # 测试数组 a [1,2,3,4,5,6,7,8,9,10,11,12,13,14] print(原始数组:, a) result HeapSort(a) print(\n 最终排序结果 ) print(result)构建大顶堆后依次取最值得到有序数组原始数组: [1,2,3,4,5,6,7,8,9,10,11,12,13,14] 第一步构建大顶堆 开始调整非叶子节点 7: 调整堆节点 7 (子节点范围: 14~14) | 当前堆: [None, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 无需交换当前子树已是大顶堆 开始调整非叶子节点 6: 调整堆节点 6 (子节点范围: 12~14) | 当前堆: [None, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 无需交换当前子树已是大顶堆 ... 第二步堆排序逐步取出最大值 将堆顶(14)与末尾节点 14(7) 交换 | 交换前: [None, 14, 13, 12, 10, 11, 6, 7, 8, 9, 4, 5, 2, 3, 1] 交换后: [None, 1, 13, 12, 10, 11, 6, 7, 8, 9, 4, 5, 2, 3, 14] (已确定位置 14 的值为 14) 调整剩余堆范围: 1~13: 调整堆节点 1 (子节点范围: 2~13) | 当前堆: [None, 1, 13, 12, 10, 11, 6, 7, 8, 9, 4, 5, 2, 3, 14] 选择左子节点 2 (值更大) 交换节点 1(1) 和 2(13) 调整后堆: [None, 13, 1, 12, 10, 11, 6, 7, 8, 9, 4, 5, 2, 3, 14] ... 最终排序结果 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]特点时间复杂度$O(n\log n)$最好/最坏/平均空间复杂度$O(1)$原地排序不稳定排序适合需要找最值的场景比如TopK问题4大进阶排序算法选型总结算法时间复杂度空间复杂度稳定性适用场景基数排序$O(d \times (nk))$$O(nk)$稳定多位数整数排序快速排序$O(n\log n)$平均$O(\log n)$不稳定大规模数据实际效率高随机快速排序$O(n\log n)$期望$O(\log n)$不稳定大规模数据更稳定堆排序$O(n\log n)$$O(1)$不稳定大规模数据、TopK问题到这里基础进阶共10种排序算法就全讲完啦从$O(n^2)$的简单排序到$O(n\log n)$的高效排序每种算法都有自己的“生存空间”——理解它们的适用场景写代码时才能既高效又省心。