Python组蓝桥杯备赛超详细知识点总结笔记_排序算法篇

Python 组蓝桥杯备赛笔记(排序算法篇)

一、排序算法概述

排序算法是将一组数据按指定顺序(升序 / 降序)重新排列的基础算法,便于后续查找、统计等操作。根据时间复杂度、空间复杂度和稳定性的差异,可分为简单排序(O (n²))高效排序(O (nlogn))特殊排序(基于分桶思想) 三类。二、冒泡排序(Bubble Sort)

算法名称核心思想时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性适用场景关键特点
冒泡排序通过相邻元素比较交换,将大元素逐步 “冒泡” 到数组末尾O(n²)O(n²)O (n)(优化后)O(1)稳定小规模数据、教学演示、接近有序的数据实现最简单,交换次数多,优化后可提前终止
选择排序每轮从待排序部分选最小值,与待排序部分首位交换,逐步构建有序序列O(n²)O(n²)O(n²)O(1)不稳定小规模数据、硬件交换成本高的场景(如元素体积大)交换次数少(最多 n-1 次),但比较次数固定,不稳定
插入排序类比 “抓扑克牌”,将未排序元素插入到已排序部分的正确位置O(n²)O(n²)O(n)O(1)稳定小规模数据、接近有序的数据(如日志增量排序、Excel 局部排序)局部调整效率高,对有序数据友好,稳定
希尔排序按 “增量” 分组,对每组插入排序,逐步缩小增量至 1,最终完成排序O(n^1.3)O(n²)O(n)O(1)不稳定中等规模数据(n < 10000)、对空间敏感的场景插入排序的升级,突破 O (n²) 复杂度,无需额外空间
快速排序选基准值,将数组分为 “小于 / 等于 / 大于基准值” 三部分,递归排序子数组O(n log n)O (n²)(基准不当)O(n log n)O(log n)不稳定大规模数据(n ≥ 1000)、通用排序场景(数据库、文件排序)平均效率最高,实现灵活,可通过优化基准值避免最坏情况
归并排序递归拆分数组为两个子数组,分别排序后合并为有序数组(分治思想)O(n log n)O(n log n)O(n log n)O(n)稳定需稳定排序的大规模数据(电商订单排序)、外排序(数据存磁盘)复杂度稳定,支持外排序,但空间开销大
堆排序构建大顶堆,反复提取堆顶(最大值)并调整堆,直至所有元素有序O(n log n)O(n log n)O(n log n)O (1)(迭代)不稳定内存受限、需原地排序的大规模数据(如嵌入式系统)利用堆的极值特性,空间效率高,但元素访问跳跃性大,缓存友好性差
计数排序统计每个值的出现次数,通过前缀和计算位置,构建有序数组(非比较型)O(n + k)O(n + k)O(n + k)O(n + k)稳定小范围整数排序(如成绩 0-100、年龄 1-120)、作为基数排序子过程线性时间排序,仅适用于整数,数据范围 k 需接近 n
基数排序按数位(低位→高位或高位→低位)排序,每轮用计数排序作为子过程(非比较型)O(d×(n + k))O(d×(n + k))O(d×(n + k))O(n + k)稳定大整数排序(如身份证号)、字符串排序、数据范围大但数位少的场景依赖数位拆分,可处理大范围数据,稳定性取决于子排序算法
桶排序将数据分配到若干 “桶” 中,桶内单独排序(用其他算法),最后合并所有桶(分治型)O (n)(理想)O (n²)(极端分布)O (n)(理想)O(n + k)稳定数据均匀分布的场景(如用户年龄、商品价格区间固定)、分布式排序效率依赖数据分布,均匀分布时接近线性时间,需合理设置桶数量

二、冒泡排序

冒泡排序是一种基础的交换排序算法,核心思想是通过相邻元素的比较与交换,将较大的元素逐步 “冒泡” 到数组末尾,较小元素自然向前移动。

def bubble_sort(a,n):	# n 为数组长度,a 为待排序数组
    # 外层循环:控制轮次,共 n-1 轮(i 从 1 到 n-1)
    for i in range(1, n):
        # 内层循环:控制比较范围,第 i 轮比较前 n-i 个元素(j 从 0 到 n-i-1)
        for j in range(n - i):
            # 相邻元素比较,前大后小则交换
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]

1. visualgo演示动画

请添加图片描述

2. 优化

基础冒泡排序在数组已有序时仍会执行 n-1 轮循环,可添加 “是否交换” 的标志位优化,减少无效轮次:

def bubble_sort(a,n):
    for i in range(1, n):
        swapped = False  # 标志位:记录本轮是否发生交换
        for j in range(n - i):
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]
                swapped = True  # 发生交换,置为 True
        if not swapped:  # 本轮无交换,说明数组已有序,提前退出
            break
  • 优化效果:数组已有序时,仅需 1 轮循环(O(n)),大幅提升效率。

三、选择排序(Selection Sort)

选择排序是一种简单直观的排序算法,核心思想是每一轮从待排序元素中找出最小(或最大)的元素,将其与待排序部分的首位元素交换位置,从而逐步构建有序序列。

def selection_sort(a,n):
    # 外层循环:控制轮次(0 到 n-2,共 n-1 轮)
    for i in range(n - 1):
        # 初始化最小值为当前轮次起始元素
        min_value = a[i]
        min_idx = i
        # 内层循环:从 i 到 n-1 查找最小值
        for j in range(i, n):
            if a[j] < min_value:
                min_value = a[j]
                min_idx = j
        # 交换最小值与当前轮次起始元素
        a[i], a[min_idx] = a[min_idx], a[i]

visualgo演示动画

请添加图片描述

四、插入排序(Insertion Sort)

插入排序的核心思想是将数组分为 “已排序” 和 “未排序” 两部分,每次从 “未排序” 部分取一个元素,插入到 “已排序” 部分的正确位置,类似整理扑克牌的过程。

def insertion_sort(a,n):
    # 外层循环:控制未排序部分的元素(i 从 1 到 n-1,默认第一个元素为已排序部分)
    for i in range(1, n):
        # 记录当前待插入的元素
        current = a[i]
        # 内层循环:在已排序部分寻找插入位置(j 从 i-1 向前遍历)
        j = i - 1
        # 当已排序部分元素大于待插入元素时,将其向后移动
        while j >= 0 and a[j] > current:
            a[j + 1] = a[j]
            j -= 1
        # 将待插入元素放到正确位置
        a[j + 1] = current

visualgo演示动画

请添加图片描述

五、希尔排序(Shell Sort)

希尔排序是插入排序的改进版,核心思想是通过设定间隔(增量)将数组分割为多个子数组,对每个子数组进行插入排序,逐步缩小间隔直至为 1,最终完成整体排序。通过预排序减少数据的无序程度,提高最终插入排序的效率。

def shell_sort(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 //= 2
    return arr

Algorithm Visualizer演示动画

请添加图片描述

六、快速排序(Quick Sort)

快速排序是一种分治思想的排序算法,核心步骤为:选择一个 “基准值”,将数组分为 “小于基准值”“等于基准值”“大于基准值” 三部分,然后递归对左右两部分进行排序。通过分治策略大幅减少比较和交换次数,是实践中高效的排序算法之一。

def quick_sort(arr, left, right):
    if left >= right:
        return  # 子数组长度为1或0时无需排序
    # 选择基准值(此处选最左元素)
    pivot = arr[left]
    i, j = left, right
    # 分区操作:将小于基准值的放左边,大于的放右边
    while i < j:
        # 从右向左找第一个小于基准值的元素
        while i < j and arr[j] >= pivot:
            j -= 1
        arr[i] = arr[j]
        # 从左向右找第一个大于基准值的元素
        while i < j and arr[i] <= pivot:
            i += 1
        arr[j] = arr[i]
    # 将基准值放到最终位置
    arr[i] = pivot
    # 递归排序左子数组和右子数组
    quick_sort(arr, left, i - 1)
    quick_sort(arr, i + 1, right)

visualgo演示动画

请添加图片描述

七、归并排序(Merge Sort)

归并排序是一种基于分治思想的排序算法,核心思想是将数组递归拆分为两个子数组,分别排序后再合并为一个有序数组。通过 “分 - 治 - 合” 的策略,将复杂问题分解为简单子问题,最终实现整体排序。

# 1. 归并排序核心函数(递归实现)
def merge_sort(arr):
    # 基线条件:数组长度小于等于1时无需排序
    if len(arr) <= 1:
        return arr
    # 分:将数组拆分为左右两个子数组
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    # 合:将两个有序子数组合并为一个有序数组
    return merge(left, right)

# 2. 合并两个有序数组的函数
def merge(left, right):
    result = []
    i = j = 0
    # 比较两个子数组的元素,按从小到大顺序放入结果数组
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    # 将剩余元素添加到结果数组(其中一个子数组已遍历完)
    result.extend(left[i:])
    result.extend(right[j:])
    return result

# 3. 读取输入并排序
n = int(input())
a = list(map(int, input().split()))
a = merge_sort(a)
print(' '.join(map(str, a)))

visualgo演示动画

请添加图片描述

八、桶排序(Bucket Sort)

桶排序是一种分布式排序算法,核心思想是将待排序数据分到若干个 “桶” 中,对每个桶内数据单独排序(可使用其他排序算法),最后将所有桶的结果合并。适用于数据分布均匀的场景,通过分桶减少单个桶内的数据量,从而提高排序效率。

#设置桶的默认数量为5
def bucket_sort_with_auto_size(arr, num_buckets = 5):
    if not arr or num_buckets <= 0:
        return arr.copy()
    # 1. 计算数据范围
    min_val = min(arr)
    max_val = max(arr)
    data_range = max_val - min_val
    # 2. 自动计算桶大小(确保每个桶的范围合理)
    # 处理所有元素都相同的特殊情况
    if data_range == 0:
        bucket_size = 1
    else:
        # 桶大小 = 数据范围 / 桶数量,向上取整确保覆盖所有数据
        bucket_size = (data_range + num_buckets - 1) // num_buckets
    # 3. 初始化桶
    buckets = [[] for _ in range(num_buckets)]
    # 4. 将元素分配到对应的桶中
    for num in arr:
        # 计算桶索引
        if data_range == 0:
            bucket_index = 0
        else:
            bucket_index = min((num - min_val) // bucket_size, num_buckets - 1)
        buckets[bucket_index].append(num)
    # 5. 桶内排序(使用插入排序)
    for bucket in buckets:
        for i in range(1, len(bucket)):
            current = bucket[i]
            j = i - 1
            while j >= 0 and bucket[j] > current:
                bucket[j + 1] = bucket[j]
                j -= 1
            bucket[j + 1] = current
    # 6. 合并所有桶的结果
    sorted_arr = []
    for bucket in buckets:
        sorted_arr.extend(bucket)
    
    return sorted_arr

Algorithm Visualizer演示动画

请添加图片描述

九、堆排序(Heap Sort)

堆排序是基于堆数据结构的排序算法,核心思想是先将数组构建为大顶堆(或小顶堆),然后反复提取堆顶元素(最大值或最小值)并调整堆结构,直至所有元素排序完成。利用堆的性质高效获取极值,实现整体排序。

def heap_sort(arr):
    n = len(arr)
    
    # 1. 构建大顶堆(从最后一个非叶子节点向上调整)
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    
    # 2. 逐步提取堆顶元素并调整堆
    for i in range(n - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]  # 交换堆顶(最大值)与当前末尾元素
        heapify(arr, i, 0)  # 调整剩余元素为大顶堆
    return arr

# 2. 堆调整函数(维护大顶堆性质)
def heapify(arr, n, i):
    largest = i  # 初始化最大值为根节点
    left = 2 * i + 1  # 左子节点索引
    right = 2 * i + 2  # 右子节点索引
    
    # 比较左子节点与当前最大值
    if left < n and arr[left] > arr[largest]:
        largest = left
    # 比较右子节点与当前最大值
    if right < n and arr[right] > arr[largest]:
        largest = right
    
    # 若最大值不是根节点,则交换并递归调整子树
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

# 3. 读取输入并排序
n = int(input())
a = list(map(int, input().split()))
a = heap_sort(a)
print(' '.join(map(str, a)))

hufeifei演示动画

为什么给链接不放gif?😠

  • 听我解释:
    找了一圈,发现还是这个演示动画最详细易懂,但是视频过长转gif内存太大了,导不进来。
    加速的话感觉太快看不懂,有想剪辑一下添加字幕什么的,但是不知道怎么剪,压缩成GIF字幕很糊也看不清,最后只能给链接让读者自行跳转查看了😭

胡飞飞堆排序算法可视化

十、计数排序(Counting Sort)

计数排序是一种非比较型排序算法,核心思想是通过统计待排序元素中每个值出现的次数,然后根据次数计算每个值在结果数组中的位置,最终构建有序数组。适用于数据范围较小且为整数的场景,无需元素间比较即可完成排序。

def counting_sort(arr):
    if not arr:
        return arr    
    # 1. 确定数据范围(最大值和最小值)
    min_val = min(arr)
    max_val = max(arr)
    range_size = max_val - min_val + 1  # 数据范围大小
    
    # 2. 初始化计数数组并统计每个元素的出现次数
    count = [0] * range_size
    for num in arr:
        count[num - min_val] += 1  # 映射到计数数组索引(处理负数)
    
    # 3. 计算计数数组的前缀和(确定每个元素的结束位置)
    for i in range(1, len(count)):
        count[i] += count[i - 1]
    
    # 4. 反向遍历原数组,构建结果数组(保证稳定性)
    result = [0] * len(arr)
    for num in reversed(arr):
        # 计算当前元素在结果数组中的位置
        index = count[num - min_val] - 1
        result[index] = num
        count[num - min_val] -= 1  # 更新计数,确保重复元素位置正确
    return result

Algorithm Visualizer演示动画

请添加图片描述

十、基数排序(Radix Sort)

基数排序是一种非比较型排序算法,核心思想是按照低位到高位(或高位到低位)的顺序,对每个数位进行排序(通常使用计数排序作为子过程),逐步将所有数位排序完成,最终实现整体有序。利用数位的递进关系,通过多次子排序实现整体排序。

# 1. 基数排序核心函数(低位优先)
def radix_sort(arr):
    if not arr:
        return arr
    
    # 1. 确定最大数的位数(决定排序轮次)
    max_num = max(arr)
    digit_count = len(str(max_num))  # 数位数量
    
    # 2. 对每个数位进行计数排序(从低位到高位)
    for digit in range(digit_count):
        divisor = 10 ** digit  # 用于提取当前数位(1, 10, 100, ...)
        arr = counting_sort_by_digit(arr, divisor)
    return arr

# 2. 按指定数位进行计数排序(子过程)
def counting_sort_by_digit(arr, divisor):
    n = len(arr)
    result = [0] * n
    count = [0] * 10  # 0-9 共10个数字
    
    # 1. 统计当前数位的数字出现次数
    for num in arr:
        digit = (num // divisor) % 10
        count[digit] += 1
    
    # 2. 计算前缀和(确定每个数字的结束位置)
    for i in range(1, 10):
        count[i] += count[i - 1]
    
    # 3. 反向遍历原数组,构建结果数组(保证稳定性)
    for num in reversed(arr):
        digit = (num // divisor) % 10
        index = count[digit] - 1
        result[index] = num
        count[digit] -= 1
    return result

# 3. 读取输入并排序
n = int(input())
a = list(map(int, input().split()))
a = radix_sort(a)
print(' '.join(map(str, a)))

visualgo演示动画

请添加图片描述

十一、排序算法选型建议表

选型维度推荐算法推荐理由不推荐算法规避原因
小规模数据(n < 1000)插入排序、选择排序1. 插入排序:对接近有序数据友好,稳定且实现简单,局部调整效率高;2. 选择排序:交换次数仅 n-1 次,适合硬件交换成本高的场景(如元素体积大)快速排序、归并排序1. 快速排序:递归开销占比高,小规模数据下优势不明显;2. 归并排序:空间开销 O (n),性价比低
中大规模数据(n ≥ 1000)快速排序、归并排序1. 快速排序:平均时间复杂度 O (n log n),工业界常用,平均效率最高;2. 归并排序:复杂度稳定无退化,支持稳定排序和外排序(数据存磁盘)冒泡排序、选择排序时间复杂度 O (n²),数据量增大时效率骤降,排序耗时呈平方级增长
需稳定排序插入排序、归并排序、计数排序、基数排序、桶排序1. 插入 / 归并排序:基于比较且稳定,适配多数数据类型;2. 计数 / 基数 / 桶排序:非比较型稳定排序,线性时间效率,适合特定场景(如整数、均匀分布数据)选择排序、快速排序、堆排序交换 / 分区 / 堆调整过程中,会改变相等元素的相对位置,无法保证稳定性
内存受限(低空间开销)希尔排序、堆排序(迭代)、快速排序1. 希尔排序:原地排序,空间复杂度 O (1),无额外内存开销;2. 堆排序(迭代):空间 O (1),仅需常数级变量;3. 快速排序:平均空间 O (log n),递归栈深度可控归并排序、计数排序、桶排序1. 归并排序:需 O (n) 空间存储合并结果;2. 计数 / 桶排序:需 O (n+k) 空间存储计数数组 / 桶
数据范围小(k ≈ n)计数排序线性时间复杂度 O (n+k),直接统计元素出现次数,无需比较,效率远高于比较型排序堆排序、快速排序虽为 O (n log n) 复杂度,但需额外比较和逻辑处理,效率低于计数排序
数据均匀分布桶排序理想情况下时间复杂度 O (n),按分布分桶后桶内元素少,排序开销低,可并行处理选择排序、希尔排序1. 选择排序:比较次数固定 O (n²),不依赖数据分布;2. 希尔排序:效率受增量序列影响,均匀分布下无明显优势
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值