【Python 算法零基础 4.排序 ⑪ 十大排序算法总结】

news2025/6/7 11:46:20

目录

一、选择排序回顾

二、冒泡排序回顾

三、插入排序回顾

四、计数排序回顾

五、归并排序回顾

六、快速排序回顾

七、桶排序回顾

八、基数排序

九、堆排序

十、希尔排序

 十一、十大排序算法对比

十二、各算法详解与应用场景 

1. 选择排序(Selection Sort)

2. 冒泡排序(Bubble Sort)

3. 插入排序(Insertion Sort)

4. 计数排序(Counting Sort)

5. 归并排序(Merge Sort)

6. 快速排序(Quick Sort)

7. 桶排序(Bucket Sort)

8. 基数排序(Radix Sort)

9. 堆排序(Heap Sort)

10. 希尔排序(Shell Sort)

十三、各大排序算法的决策指南

1.数据规模

2.数据特性

3.稳定性需求

4.空间限制

5.特殊场景:


料青山略输我峥嵘,判江河亦低我磅礴

                                                        —— 25.6.6

一、选择排序回顾

① 遍历数组:从索引 0 到 n-1n 为数组长度)。

② 每轮确定最小值:假设当前索引 i 为最小值索引 min_index。从 i+1 到 n-1 遍历,若找到更小元素,则更新 min_index

③ 交换元素:若 min_index ≠ i,则交换 arr[i] 与 arr[min_index]

'''
① 遍历数组:从索引 0 到 n-1(n 为数组长度)。

② 每轮确定最小值:假设当前索引 i 为最小值索引 min_index。从 i+1 到 n-1 遍历,若找到更小元素,则更新 min_index。

③ 交换元素:若 min_index ≠ i,则交换 arr[i] 与 arr[min_index]。
'''

def selectionSort(arr: List[int]):
    n = len(arr)
    for i in range(n):
        min_index = i
        for j in range(i+1, n):
            if arr[j] < arr[min_index]:
                min_index = j
        if min_index != i:
            arr[i], arr[min_index] = arr[min_index], arr[i]
    return arr

二、冒泡排序回顾

① 初始化:设数组长度为 n

② 外层循环:遍历 i 从 0 到 n-1(共 n 轮)。

③ 内层循环:对于每轮 i,遍历 j 从 0 到 n-i-2

④ 比较与交换:若 arr[j] > arr[j+1],则交换两者。

⑤ 结束条件:重复步骤 2-4,直到所有轮次完成。

'''
① 初始化:设数组长度为 n。

② 外层循环:遍历 i 从 0 到 n-1(共 n 轮)。

③ 内层循环:对于每轮 i,遍历 j 从 0 到 n-i-1。

④ 比较与交换:若 arr[j] > arr[j+1],则交换两者。

⑤ 结束条件:重复步骤 2-4,直到所有轮次完成。
'''
def bubbleSort(arr: List[int]):
    n = len(arr)
    for i in range(n):
        for j in range(n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

三、插入排序回顾

① 遍历未排序元素:从索引 1 到 n-1

② 保存当前元素:将 arr[i] 存入 current

③ 元素后移:从已排序部分的末尾(索引 j = i-1)向前扫描,将比 current 大的元素后移。直到找到第一个不大于 current 的位置或扫描完所有元素。

④ 插入元素:将 current 放入 j+1 位置。

'''
① 遍历未排序元素:从索引 1 到 n-1。

② 保存当前元素:将 arr[i] 存入 current。

③ 元素后移:从已排序部分的末尾(索引 j = i-1)向前扫描,将比 current 大的元素后移。直到找到第一个不大于 current 的位置或扫描完所有元素。

④ 插入元素:将 current 放入 j+1 位置。
'''
def insertSort(arr: List[int]):
    n = len(arr)
    for i in range(n):
        current = arr[i]
        j = i - 1
        while current < arr[j] and j >0:
            arr[j+1] = arr[j]
            j -= 1
        arr[j + 1] = current
    return arr

四、计数排序回顾

① 初始化:设数组长度为 n,元素最大值为 r。创建长度为 r+1 的计数数组 count,初始值全为 0。

② 统计元素频率:遍历原数组 arr,对每个元素 x,将 count[x] 加 1。

③ 重构有序数组:初始化索引 index = 0。遍历计数数组 count,索引 v 从 0 到 r,若 count[v] > 0,则将 v 填入原数组 arr[index],并将 index 加 1。count[v] - 1,重复此步骤直到 count[v] 为 0。

④ 结束条件:当计数数组遍历完成时,排序结束。

'''
输入全为非负整数,且所有元素 ≤ r

① 初始化:设数组长度为 n,元素最大值为 r。创建长度为 r+1 的计数数组 count,初始值全为 0。

② 统计元素频率:遍历原数组 arr,对每个元素 x,将 count[x] 加 1。

③ 重构有序数组:初始化索引 index = 0。遍历计数数组 count,索引 v 从 0 到 r,
若 count[v] > 0,则将 v 填入原数组 arr[index],并将 index 加 1。
count[v] - 1,重复此步骤直到 count[v] 为 0。

④ 结束条件:当计数数组遍历完成时,排序结束。
'''

def countingSort(arr: List[int], r: int):
    # count = [0] * len(r + 1)
    count = [0 for i in range(r + 1)]
    for x in arr:
        count[x] += 1
    index = 0
    for v in range(r + 1):
        while count[v] > 0:
            arr[index] = v
            index += 1
            count[v] -= 1
    return arr

五、归并排序回顾

Ⅰ、递归分解列表

① 终止条件:若链表为空或只有一个节点(head is None 或 head.next is None),直接返回头节点。

② 快慢指针找中点:初始化 slow 和 fast 指针,slow 指向头节点,fast 指向头节点的下一个节点。fast 每次移动两步,slow 每次移动一步。当 fast 到达末尾时,slow 恰好指向链表的中间节点。

③ 分割链表:将链表从中点断开,head2 指向 slow.next(后半部分的头节点)。将 slow.next 置为 None,切断前半部分与后半部分的连接。

④ 递归排序子链表:对前半部分(head)和后半部分(head2)分别递归调用 mergesort 函数。

Ⅱ、合并两个有序列表

① 创建虚拟头节点:创建一个值为 -1 的虚拟节点 zero,用于简化边界处理。使用 current 指针指向 zero,用于构建合并后的链表。

② 比较并合并节点:遍历两个子链表 head1 和 head2,比较当前节点的值:若 head1.val <= head2.val,将 head1 接入合并链表,并移动 head1 指针。否则,将 head2 接入合并链表,并移动 head2 指针。每次接入节点后,移动 current 指针到新接入的节点。

③ 处理剩余节点:当其中一个子链表遍历完后,将另一个子链表的剩余部分直接接入合并链表的末尾。

④ 返回合并后的链表:虚拟节点 zero 的下一个节点即为合并后的有序链表的头节点。

'''
Ⅰ、递归分解列表

① 终止条件:若链表为空或只有一个节点(head is None 或 head.next is None),直接返回头节点。

② 快慢指针找中点:初始化 slow 和 fast 指针,slow 指向头节点,fast 指向头节点的下一个节点。fast 每次移动两步,slow 每次移动一步。当 fast 到达末尾时,slow 恰好指向链表的中间节点。

③ 分割链表:将链表从中点断开,head2 指向 slow.next(后半部分的头节点)。将 slow.next 置为 None,切断前半部分与后半部分的连接。

④ 递归排序子链表:对前半部分(head)和后半部分(head2)分别递归调用 mergesort 函数。

Ⅱ、合并两个有序列表

① 创建虚拟头节点:创建一个值为 -1 的虚拟节点 zero,用于简化边界处理。使用 current 指针指向 zero,用于构建合并后的链表。

② 比较并合并节点:遍历两个子链表 head1 和 head2,比较当前节点的值:若 head1.val <= head2.val,将 head1 接入合并链表,并移动 head1 指针。否则,将 head2 接入合并链表,并移动 head2 指针。每次接入节点后,移动 current 指针到新接入的节点。

③ 处理剩余节点:当其中一个子链表遍历完后,将另一个子链表的剩余部分直接接入合并链表的末尾。

④ 返回合并后的链表:虚拟节点 zero 的下一个节点即为合并后的有序链表的头节点。
'''
def mergesort(self, head: ListNode):
    if head is None or head.next is None:
        return head
    slow, fast = head, head.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    head2 = slow.next
    slow.next = None
    return self.merge(self.mergesort(head), self.mergesort(head2))

def merge(self, head1: ListNode, head2: ListNode):
    zero = ListNode(-1)
    current = zero
    while head1 and head2:
        if head1.val <= head2.val:
            current.next = head1
            head1 = head1.next
        else:
            current.next = head2
            head2 = head2.next
        current = current.next
    current.next = head1 if head1 else head2
    return zero.next

六、快速排序回顾

Ⅰ、分区函数 Partition

① 随机选择基准元素:根据左右边界下标随机选择基准元素(选择的是元素并非下标),将基准元素赋值变量进行后续比较

② 交换基准元素:将基准元素移动到最左边,将基准元素存储在变量中,

③ 分区操作:对于基准元素右边的元素,找到第一个小于基准元素的值,移动到最左边;对于基准元素左边的元素,找到第一个大于基准元素的值,移动到最右边

④ 返回基准元素的最终位置:循环执行完毕后,基准元素左边的值都小于它,基准元素右边的值都大于它

Ⅱ、递归快速排序函数

① 定义递归终止条件:当左索引小于右索引时,结束递归

② 分区操作: 执行第一次分区操作,找到基准元素

③ 递归调用分区函数:将基准元素的左边、右边部分分别传入递归函数进行排序

'''
Ⅰ、分区函数 Partition

① 随机选择基准元素:根据左右边界下标随机选择基准元素(选择的是元素并非下标),将基准元素赋值变量进行后续比较

② 交换基准元素:将基准元素移动到最左边,将基准元素存储在变量中,

③ 分区操作:对于基准元素右边的元素,找到第一个小于基准元素的值,移动到最左边;对于基准元素左边的元素,找到第一个大于基准元素的值,移动到最右边

④ 返回基准元素的最终位置:循环执行完毕后,基准元素左边的值都小于它,基准元素右边的值都大于它

Ⅱ、递归排序函数

① 定义递归终止条件:当左索引小于右索引时,结束递归

② 分区操作: 执行第一次分区操作,找到基准元素

③ 递归调用分区函数:将基准元素的左边、右边部分分别传入递归函数进行排序
'''

def Partition(arr, left, right):
    idx = random.randint(left, right)
    arr[left], arr[idx] = arr[idx], arr[left]
    l = left
    r = right
    x = arr[l]
    while l < r:
        while l < r and x < arr[r]:
            r -= 1
        if l < r:
            arr[l], arr[r] = arr[r], arr[l]
            l += 1

        while l < r and x > arr[l]:
            l += 1
        if l < r:
            arr[l], arr[r] = arr[r], arr[l]
            r -= 1
    return l

def quickSort(arr, l, r):
    if l >= r:
        return
    node = self.quickSort(l, r)
    self.quickSort(arr, l, node-1)
    self.quickSort(arr, node+1, r)
    return arr

七、桶排序回顾

① 初始化桶和频率数组: 创建字符长度+1的桶bucket,索引 i 表示频率为 i 的字符列表;长度为max的频率数组count,用于记录每个字符的出现次数

② 统计字符频率:通过 ord(char) 获取字符的ASCII码,作为频率数组的索引

③ 将字符按照频率放入桶中:遍历频率数组,将每个字符以频率作为索引放入数组中

④ 返回桶数组:返回桶数组,其中每个桶包含对应频率的字符列表

def bucketSort(arr, max_val):  # 移除 max_val 表示字符编码最大值(如 256)
    n = len(arr)
    # 初始化桶:索引范围 [0, max_val-1]
    bucket = [[] for _ in range(max_val)]
    
    # 分布:按字符编码放入桶
    for char in arr:
        bucket[ord(char)].append(char)  # 索引 = 字符编码值
    
    # 合并桶(索引升序即字符升序)
    sorted_arr = []
    for b in bucket:
        sorted_arr.extend(b)  # 每个桶内元素已按插入顺序排列
    
    return sorted_arr  # 返回排序后的一维数组

八、基数排序

① 初始化参数和辅助数组:设置最大元素数MAX_N为 50000,最大位数MAX_T为 8,进制BASE为 10;计算并存储基数的各次幂(如 10⁰, 10¹, ..., 10⁷)到数组PowOfBase

② 预处理数组元素:为每个元素加上BASE^(MAX_T-1)(即 10⁷),确保所有元素变为正数;这一步是为了处理可能的负数输入,将其转换为正数进行排序

③ 按位进行多轮排序:从最低有效位(个位)开始,逐位进行处理(共进行MAX_T轮)

        分配阶段:将元素分配到对应的桶(0-9)中对每个元素,计算当前位上的数字(通过整除和取模运算)

        收集阶段:按桶的顺序(0 到 9)依次收集元素;将收集的元素依次放回原数组,覆盖原有的顺序

④ 恢复原始数值:排序完成后,从每个元素中减去BASE^(MAX_T-1)(即 10⁷);恢复元素的原始值,完成排序过程

'''
基数排序

① 初始化参数和辅助数组:设置最大元素数MAX_N为 50000,最大位数MAX_T为 8,进制BASE为 10;计算并存储基数的各次幂(如 10⁰, 10¹, ..., 10⁷)到数组PowOfBase中

② 预处理数组元素:为每个元素加上BASE^(MAX_T-1)(即 10⁷),确保所有元素变为正数;这一步是为了处理可能的负数输入,将其转换为正数进行排序

③ 按位进行多轮排序:从最低有效位(个位)开始,逐位进行处理(共进行MAX_T轮)

        分配阶段:将元素分配到对应的桶(0-9)中对每个元素,计算当前位上的数字(通过整除和取模运算)

        收集阶段:按桶的顺序(0 到 9)依次收集元素;将收集的元素依次放回原数组,覆盖原有的顺序

④ 恢复原始数值:排序完成后,从每个元素中减去BASE^(MAX_T-1)(即 10⁷);恢复元素的原始值,完成排序过程
'''
    MAX_N = 50000    # 最多的元素数
    MAX_T = 8        # 元素的最大位数
    BASE = 10        # 定义数字的进制

    def RedixSort(self, arr):
        n = len(arr)
        PowOfBase = [1 for i in range(self.MAX_T)]    # 代表BASE的多少次方
        for i in range(1, self.MAX_T):
            PowOfBase[i] = PowOfBase[i - 1] * self.BASE

        for i in range(n):
            arr[i] += PowOfBase[self.MAX_T - 1]

        pos = 0 
        while pos < self.MAX_T:
            RedixBucket = [ [] for i in range(self.BASE)]

            for i in range(n):
                rdx = arr[i] // PowOfBase[pos] % self.BASE
                RedixBucket[rdx].append( arr[i] )
            
            top = 0
            for i in range(self.BASE):
                for rb in RedixBucket[i]:
                    arr[top] = rb
                    top += 1
            
            pos += 1

        for i in range(n):
            arr[i] -= PowOfBase[self.MAX_T - 1]
        return arr

九、堆排序

 堆排序

辅助函数(节点关系):

leftSon(idx)返回节点idx的左子节点索引(2*idx + 1)。

rightSon(idx)返回节点idx的右子节点索引(2*idx + 2)。

parent(idx)返回节点idx的父节点索引((idx-1)//2)。

better(a, b)比较函数,默认返回a > b,用于定义最大堆的比较规则。

Heapify 函数:

Ⅰ、计算子节点索引

leftSon(curr) 和 rightSon(curr) 分别计算当前节点的左右子节点索引。

optId 初始化为当前节点索引,用于记录最大值的位置。

Ⅱ、找出最大值索引

比较当前节点与左右子节点的值:如果左子节点存在且值更大,则更新 optId 为左子节点索引。同理,对右子节点进行相同比较。better 函数默认实现为 a > b,确保构建最大堆。

Ⅲ、交换并递归调整

如果最大值不在当前节点(curr != optId),则交换当前节点与最大值节点。

递归调用 Heapify 处理被交换的子节点,确保其所有子树仍满足堆性质。

sortArray 函数

构建最大堆:从最后一个非叶子节点(n//2 -1)开始,向上逐个调用Heapify,确保整个数组成为最大堆。

循环过程:将堆顶元素(最大值)与当前未排序部分的最后一个元素交换;减少堆的大小(size = i),将最大值排除在后续调整范围外;调用 Heapify(0) 重新调整剩余元素为最大堆。

效果:每次循环将当前最大值移至数组末尾,最终形成升序排列。

def leftSon(idx):
    return 2 * idx + 1

def rightSon(idx):
    return 2 * idx + 2

def parent(idx):
    return (idx - 1) // 2

def better(a, b):
    return a > b 

class Solution:
    def Heapify(self, heap, size, curr):
        leftSonId = leftSon(curr)
        rightSonId = rightSon(curr)
        optId = curr
        if leftSonId < size and better(heap[leftSonId], heap[optId]):
            optId = leftSonId
        if rightSonId < size and better(heap[rightSonId], heap[optId]):
            optId = rightSonId

        if curr != optId:
            heap[curr], heap[optId] = heap[optId], heap[curr]
            self.Heapify(heap, size, optId)

    def sortArray(self, nums: List[int]) -> List[int]:
        n = len(nums)
        for i in range(n // 2, -1, -1):
            self.Heapify(nums, n, i)

        for i in range(n-1, -1, -1):
            nums[0], nums[i] = nums[i], nums[0]
            self.Heapify(nums, i, 0)

        return nums

十、希尔排序

 ① 希尔排序

Ⅰ、初始化间隔(增量)

        首先计算初始间隔 gap,通常取数组长度的一半(gap = n // 2)。这个间隔决定了子序列的划分方式,后续会逐步缩小。

Ⅱ、分组与插入排序

        外层循环(间隔控制):当 gap > 0 时,执行循环体。每次循环结束后将间隔缩小一半(gap = gap // 2)。

        中层循环(遍历每个子序列):从 gap 开始遍历到数组末尾,每个元素 arr[i] 属于不同的子序列。

        内层循环(子序列插入排序):保存当前元素 arr[i] 到临时变量 temp。在当前子序列中(间隔为 gap),从后往前比较元素。如果前一个元素 arr[j - gap] 大于 temp,则将其向后移动 gap 个位置。重复上述比较和移动操作,直到找到正确的插入位置,然后将 temp 插入该位置。

Ⅲ、缩小间隔

        每次完成当前间隔的所有子序列排序后,将间隔缩小一半(例如,从 gap = 4 到 gap = 2,再到 gap = 1)。当间隔最终缩小到 1 时,整个数组被视为一个子序列,此时执行的就是标准的插入排序,但由于前面的分组排序已经使数组接近有序,插入排序的效率会更高。

Ⅳ、终止条件

        当间隔 gap 减小到 0 时,排序完成,返回排序后的数组。

② 排序数组 

        调用希尔排序函数,传入列表(数组)nums

class Solution:
    def shell_sort(self, arr):
        n = len(arr)
        gap = n // 2  # 初始间隔
        
        while gap > 0:
            # 对每个间隔分组进行插入排序
            for i in range(gap, n):
                # 保存当前元素,作为待插入的值
                temp = arr[i]
                j = i
                # 在间隔为gap的分组内,从后往前找到插入位置
                while j >= gap and arr[j - gap] > temp:
                    arr[j] = arr[j - gap]  # 元素后移
                    j -= gap
                arr[j] = temp  # 插入正确位置
            gap = gap // 2  # 缩小间隔
        return arr

    def sortArray(self, nums: List[int]) -> List[int]:
        return self.shell_sort(nums)

 十一、十大排序算法对比

算法时间复杂度空间复杂度稳定性数据规模数据特性典型应用场景
选择排序O(n²)O(1)不稳定小规模任意顺序教学示例、简单场景(如嵌入式设备)
冒泡排序O(n²)O(1)稳定小规模近乎有序轻量级数据处理、教学演示
插入排序O(n²)O(1)稳定小规模近乎有序或少量元素链表排序、实时数据插入场景
计数排序O(n+k)O(n+k)稳定中等规模数值范围小(k 已知)整数排序(如高考分数统计)
归并排序O(n log n)O(n)稳定大规模任意顺序,需稳定性外排序、分布式系统(如 Hadoop)
快速排序O(n log n)O(log n)不稳定大规模任意顺序通用排序(如编程语言内置 sort 函数)
桶排序O(n + k)O(n+k)稳定大规模均匀分布、浮点数据海量数据排序(如日志分析)
基数排序O(d(n + k))O(n + k)稳定大规模多关键字、固定长度数据字符串排序(如姓名按拼音排序)
堆排序O(n log n)O(1)不稳定大规模任意顺序,需原地排序内存受限场景(如嵌入式系统)
希尔排序O(n log²n) - O(n²)O(1)不稳定中等规模中等逆序度数据库索引排序、文件系统排序

十二、各算法详解与应用场景 

1. 选择排序(Selection Sort)

  • 核心思想:每次从未排序部分选择最小值,与未排序部分的第一个元素交换。
  • 优点:实现简单,原地排序。
  • 缺点:时间复杂度高,不适用于大规模数据。
  • 应用场景
    • 教学场景或简单程序(如玩具项目)。
    • 内存严格受限且数据量极小(如几百个元素)。

2. 冒泡排序(Bubble Sort)

  • 核心思想:相邻元素两两比较,逆序时交换,将最大值逐步 “冒泡” 到末尾。
  • 优点:稳定排序,实现简单。
  • 缺点:平均效率低,仅适用于小规模数据。
  • 优化:引入标志位提前终止(如鸡尾酒排序)。
  • 应用场景
    • 教育演示(理解排序逻辑)。
    • 小规模、近乎有序的数据(如已排序数据的微小更新)。

3. 插入排序(Insertion Sort)

  • 核心思想:将元素逐个插入已排序部分的合适位置,类似打牌时整理手牌。
  • 优点:稳定排序,对近乎有序的数据效率高(O (n) 时间)。
  • 缺点:大规模逆序数据下效率低。
  • 变种:希尔排序(分组插入优化)。
  • 应用场景
    • 链表排序(无需随机访问,插入操作高效)。
    • 实时数据处理(如传感器数据逐个插入排序)。

4. 计数排序(Counting Sort)

  • 核心思想:统计每个值的出现次数,按顺序重建数组。
  • 前提:数据范围有限(k 已知),通常为非负整数。
  • 优点:线性时间复杂度,稳定排序。
  • 缺点:空间复杂度高(需额外数组存储计数)。
  • 应用场景
    • 整数排序(如 100 以内的学生成绩排序)。
    • 数据压缩前的预处理(如统计字符频率)。

5. 归并排序(Merge Sort)

  • 核心思想:分治策略,递归分割数组,合并时排序。
  • 优点:稳定排序,时间复杂度稳定为 O (n log n)。
  • 缺点:需要 O (n) 额外空间(无法原地排序)。
  • 变种:自然归并排序(利用已排序子序列)。
  • 应用场景
    • 外排序(处理无法一次性加载到内存的数据)。
    • 需要稳定性的场景(如排序含有相同键的记录)。

6. 快速排序(Quick Sort)

  • 核心思想:分治策略,选择枢轴元素,将数组划分为两部分递归排序。
  • 优点:平均时间复杂度低,原地排序(空间复杂度 O (log n))。
  • 缺点:最坏情况 O (n²)(可通过随机枢轴或三数取中法优化)。
  • 应用场景
    • 通用排序(如 Python 的sorted()、C++ 的std::sort)。
    • 大规模数据排序(如数据库查询结果排序)。

7. 桶排序(Bucket Sort)

  • 核心思想:将数据分配到有限数量的桶中,每个桶内单独排序,再合并结果。
  • 前提:数据分布均匀,桶的数量合理。
  • 优点:线性时间复杂度(若桶内排序为 O (1))。
  • 缺点:对非均匀分布数据效率低。
  • 应用场景
    • 浮点数据排序(如 [0,1) 区间的随机数)。
    • 海量数据并行处理(如 MapReduce 中的排序阶段)。

8. 基数排序(Radix Sort)

  • 核心思想:按数字的每一位(如个位、十位)依次排序,从最低位到最高位。
  • 前提:数据有固定长度(如字符串、固定位数的整数)。
  • 优点:稳定排序,适用于多关键字排序。
  • 缺点:依赖数据进制(通常为 10 进制或 256 进制)。
  • 应用场景
    • 字符串排序(如按字典序排序姓名)。
    • 整数排序(如身份证号码、邮政编码)。

9. 堆排序(Heap Sort)

  • 核心思想:利用最大堆或最小堆,每次将堆顶元素与末尾交换,调整堆结构。
  • 优点:原地排序(O (1) 空间),时间复杂度稳定为 O (n log n)。
  • 缺点:不稳定排序,缓存性能较差(非连续访问数组)。
  • 应用场景
    • 内存受限环境(如嵌入式设备、实时系统)。
    • 需要在线处理数据(如动态维护 Top-K 元素)。

10. 希尔排序(Shell Sort)

  • 核心思想:分组插入排序,增量序列逐步缩小至 1,最终用插入排序收尾。
  • 优点:比插入排序更快,适用于中等规模数据。
  • 缺点:时间复杂度依赖增量序列,稳定性差。
  • 优化:使用 Sedgewick 序列或 Hibbard 序列提升效率。
  • 应用场景
    • 中等规模数组(如几千到几万元素)。
    • 数据库索引排序(如 B + 树节点内部排序)。

十三、各大排序算法的决策指南

1.数据规模

小规模(n<1000):选择插入排序、冒泡排序或选择排序(实现简单)。

大规模(n≥1000):优先快速排序、归并排序或堆排序。

2.数据特性

近乎有序:插入排序或冒泡排序(优化后效率高)。

整数 / 字符串:计数排序、基数排序(利用数据特性降维复杂度)。

分布均匀:桶排序(线性时间复杂度)。

3.稳定性需求

需要稳定排序:归并排序、基数排序、冒泡排序。

无需稳定排序:快速排序、堆排序、希尔排序。

4.空间限制

内存紧张:堆排序(O (1) 空间)、希尔排序(O (1) 空间)。

空间充足:归并排序(O (n) 空间)、计数排序(O (n+k) 空间)。

5.特殊场景

实时数据:插入排序(动态插入)。

并行处理:桶排序(可分布式实现)。

外存数据:归并排序(分块处理)。

十大排序算法各有优劣,实际应用中需根据数据规模、特性、稳定性要求及环境限制综合选择。例如:

        日常编程首选快速排序(高效、通用)。

        嵌入式系统优先堆排序(省内存)。

        教学或简单场景使用插入 / 冒泡排序(易理解)。

        大数据场景考虑桶排序、基数排序或分布式归并排序。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在 Windows 11 中永久更改默认浏览器:阻止 Edge 占据主导地位

在 Windows 11 中更改默认浏览器对于新手或技术不太熟练的用户来说可能会令人沮丧。 为什么要在 Windows 11 中更改默认浏览器? 这是一个重要的问题:你为什么要从 Microsoft Edge 切换过来? 生态系统集成:如果你已经在广泛使用 Google 服务,Chrome 可以提供无缝集成。同…

量子比特实现方式

经典计算机是通过电子电路运转起来的。使用硅制半导体制成的名为晶体管的小元件发挥了开关的作用&#xff0c;将其与金属布线组合起来即可实现逻辑门&#xff0c;再将逻辑门集成起来就能制造出经典计算机。量子计算机的制造过程则要复杂许多&#xff0c;因为量子计算机既需要量…

智慧水务发展迅猛:从物联网架构到AIoT系统的跨越式升级

AI大模型引领智慧水务迈入新纪元 2025年5月25日&#xff0c;水利部自主研发的“水利标准AI大模型”正式发布&#xff0c;它标志着水务行业智能化进程的重大突破。该模型集成1800余项水利标准、500余项法规及海量科研数据&#xff0c;支持立项、编制、审查等全流程智能管理&…

Java高级 | 【实验五】Spring boot+mybatis操作数据库

隶书文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…

在MATLAB中使用自定义的ROS2消息

简明结论&#xff1a; 无论ROS2节点和MATLAB运行在哪&#xff0c;MATLAB本机都必须拥有自定义消息源码并本地用ros2genmsg生成&#xff0c;才能在Simulink里订阅这些消息。只要你想让MATLAB或Simulink能识别自定义消息&#xff0c;必须把消息包源码(.msg等)拷到本机指定目录&a…

【MATLAB去噪算法】基于ICEEMDAN联合小波阈值去噪算法

ICEEMDAN联合小波阈值去噪算法相关文献 &#xff08;注&#xff1a;目前相关论文较少&#xff0c;应用该套代码可发直接一些水刊&#xff09; 一、CEEMDAN的局限性 模式残留噪声问题&#xff1a;原始CEEMDAN在计算每个IMF时直接对噪声扰动的信号进行模态分解并平均。 后果&a…

XXTEA,XTEA与TEA

TEA、XTEA和XXTEA都是分组加密算法&#xff0c;它们在设计、安全性、性能等方面存在显著区别。以下是它们的主要区别&#xff1a; 密钥长度 TEA&#xff1a;使用128位密钥。 XTEA&#xff1a;通常使用128位或256位密钥。 XXTEA&#xff1a;密钥长度更灵活&#xff0c;可以使用任…

机器人玩转之---嵌入式开发板基础知识到实战选型指南(包含ORIN、RDK X5、Raspberry pi、RK系列等)

1. 基础知识讲解 1.1 什么是嵌入式开发板&#xff1f; 嵌入式开发板是一种专门设计用于嵌入式系统开发的硬件平台&#xff0c;它集成了微处理器、内存、存储、输入输出接口等核心组件于单块印刷电路板上。与传统的PC不同&#xff0c;嵌入式开发板具有体积小、功耗低、成本适中…

腾讯云国际版和国内版账户通用吗?一样吗?为什么?

在当今全球化的数字化时代&#xff0c;云计算服务成为众多企业和个人拓展业务、存储数据的重要选择。腾讯云作为国内领先的云服务提供商&#xff0c;其国际版和国内版备受关注。那么&#xff0c;腾讯云国际版和国内版账户是否通用&#xff1f;它们究竟一样吗&#xff1f;背后又…

OrCAD X Capture CIS设计小诀窍系列第二季--03.如何在Capture中输出带有目录和元器件信息的PDF

背景介绍&#xff1a;我们在进行原理图设计时&#xff0c;经常需要输出PDF来查看或评审&#xff0c;但通过”Print”功能导出的PDF较为简单&#xff0c;只能查看设计视图&#xff1b;而通过使用Ghostscript软件可以输出带有目录和元器件信息的PDF&#xff0c;让设计师可以直接在…

汽车的安全性能测试:试验台铁地板的重要性

汽车的安全性能测试是非常重要的&#xff0c;其中试验台铁地板的设计和材料选择起着至关重要的作用。试验台铁地板是指在进行汽车碰撞、侧翻等试验时&#xff0c;用于支撑汽车底部和提供稳定支撑的重要部件。 在进行汽车碰撞试验时&#xff0c;试验台铁地板的设计和材料需要具…

实践指南:从零开始搭建RAG驱动的智能问答系统

LLM 赋能的最强大的应用之一是复杂的问答 (Q&A) 聊天机器人。这些是可以回答关于特定来源信息问题的应用程序。这些应用程序使用一种称为检索增强生成的技术&#xff0c;或 RAG。本文将展示如何基于 LangChain 构建一个简单的基于非结构化数据文本数据源的问答应用程序。 温…

边缘计算服务器

边缘计算服务器的核心要点解析&#xff0c;综合技术架构、应用场景与部署方案&#xff1a; 一、核心定义与技术特性‌ 本质定位‌ 部署在网络边缘侧的专用计算设备&#xff08;如工厂车间、智慧路灯等&#xff09;&#xff0c;直接处理终端设备&#xff08;传感器、摄像头等…

第R9周:阿尔茨海默病诊断(优化特征选择版)

文章目录 1. 导入数据2. 数据处理2.1 患病占比2.2 相关性分析2.3 年龄与患病探究 3. 特征选择4. 构建数据集4.1 数据集划分与标准化4.2 构建加载 5. 构建模型6. 模型训练6.1 构建训练函数6.2 构建测试函数6.3 设置超参数 7. 模型训练8. 模型评估8.1 结果图 8.2 混淆矩阵9. 总结…

电动螺丝刀-多实体拆图建模案例

多实体建模要注意下面两点&#xff1a; 多实体建模的合并结果一定要谨慎在实际工作中多实体建模是一个非常好的思路&#xff0c;先做产品的整体设计&#xff0c;再将个体零件导出去做局部细节设计 电动螺丝刀模型动图展示 爆炸视图动图展示 案例素材点击此处获取 建模步骤 1. …

当丰收季遇上超导磁测量:粮食产业的科技新征程

麦浪藏光阴&#xff0c;心田种丰年&#xff01;又到了一年中最令人心潮澎湃的粮食丰收季。金色的麦浪随风翻滚&#xff0c;沉甸甸的稻穗谦逊地低垂着&#xff0c;处处洋溢着丰收的喜悦。粮食产业&#xff0c;无疑是国家发展的根基与命脉&#xff0c;是民生稳定的压舱石。在现代…

电子电气架构 --- 什么是功能架构?

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

AudioRelay 0.27.5 手机充当电脑音响

—————【下 载 地 址】——————— 【​本章下载一】&#xff1a;https://pan.xunlei.com/s/VOS4MvfPxrnfS2Zu_YS4egykA1?pwdi2we# 【​本章下载二】&#xff1a;https://pan.xunlei.com/s/VOS4MvfPxrnfS2Zu_YS4egykA1?pwdi2we# 【百款黑科技】&#xff1a;https://uc…

NVIDIA Dynamo:数据中心规模的分布式推理服务框架深度解析

NVIDIA Dynamo&#xff1a;数据中心规模的分布式推理服务框架深度解析 摘要 NVIDIA Dynamo是一个革命性的高吞吐量、低延迟推理框架&#xff0c;专为在多节点分布式环境中服务生成式AI和推理模型而设计。本文将深入分析Dynamo的架构设计、核心特性、代码实现以及实际应用示例&…

第十三节:第四部分:集合框架:HashMap、LinkedHashMap、TreeMap

Map集合体系 HashMap集合的底层原理 HashMap集合底层是基于哈希表实现的 LinkedHashMap集合的底层原理 TreeMap集合的底层原理 代码&#xff1a; Student类 package com.itheima.day26_Map_impl;import java.util.Objects;public class Student implements Comparable<Stu…