蓝桥备战——排序算法大全

1.冒泡排序(Bubble Sort)

算法思路
依次比较相邻元素,若顺序错误则交换,每一轮将最大的元素“冒泡”到末尾。重复直到所有元素有序。

代码实现:

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        # 提前退出标志位(若某轮未交换,说明已有序)
        swapped = False
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = True
        if not swapped:
            break
    return arr

注意:

  • 时间复杂度:平均和最坏 O(n²),最好 O(n)(已有序时)。

  • 适用场景:教学用途,小规模数据或基本有序数据。

 2. 选择排序(Selection Sort)

算法思路
每一轮找到未排序部分的最小值,将其与未排序部分的第一个元素交换位置。

代码实现:

def selection_sort(arr):
    n = len(arr)
    for i in range(n):
        min_idx = i
        for j in range(i+1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        arr[i], arr[min_idx] = arr[min_idx], arr[i]
    return arr

注释

  • 时间复杂度:始终 O(n²)(无论数据是否有序)。

  • 适用场景:教学或简单场景,不推荐实际应用。

 3. 插入排序(Insertion Sort)

算法思路
将未排序元素逐个插入到已排序部分的正确位置,类似整理扑克牌。

代码实现

def insertion_sort(arr):
    for i in range(1, len(arr)):
        key = arr[i]  # 当前待插入元素
        j = i-1
        # 将比 key 大的元素后移
        while j >=0 and key < arr[j]:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = key
    return arr

注释

  • 时间复杂度:平均和最坏 O(n²),最好 O(n)(已有序时)。

  • 适用场景:小规模数据或基本有序数据,实际应用中的优化(如TimSort的组成部分)。

4. 快速排序(Quick Sort)

 算法思路
分治法策略。选择一个基准元素,将数组分为“小于基准”和“大于基准”两部分,递归排序子数组。

代码实现

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr)//2]  # 选择中间元素作为基准
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + middle + quick_sort(right)

# 更高效的原地分区版本(Hoare分区法)
def partition(arr, low, high):
    pivot = arr[high]
    i = low - 1
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i+1

def quick_sort_inplace(arr, low=0, high=None):
    if high is None:
        high = len(arr) - 1
    if low < high:
        pi = partition(arr, low, high)
        quick_sort_inplace(arr, low, pi-1)
        quick_sort_inplace(arr, pi+1, high)
    return arr

注释

  • 时间复杂度:平均 O(n log n),最坏 O(n²)(当基准选择极不平衡时)。

  • 适用场景:大规模数据,实际应用广泛(需优化基准选择)。

5. 归并排序(Merge Sort) 

算法思路
分治法策略。将数组分为两半,递归排序后合并两个有序子数组。

代码实现

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    merged = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            merged.append(left[i])
            i += 1
        else:
            merged.append(right[j])
            j += 1
    merged.extend(left[i:])
    merged.extend(right[j:])
    return merged

注释

  • 时间复杂度:始终 O(n log n),稳定但需要额外空间。

  • 适用场景:需要稳定排序或链表排序。

6. 堆排序(Heap Sort)

算法思路
将数组构建为大顶堆,每次取出堆顶元素(最大值),调整剩余堆结构。

代码实现

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)

def heap_sort(arr):
    n = len(arr)
    # 构建大顶堆
    for i in range(n//2-1, -1, -1):
        heapify(arr, n, i)
    # 逐个提取元素
    for i in range(n-1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr, i, 0)
    return arr

注释

  • 时间复杂度:始终 O(n log n),原地排序但不稳定。

  • 适用场景:需要原地排序且不要求稳定性的场景。

7. 希尔排序(Shell Sort) 

算法思路
希尔排序是插入排序的优化版本,通过将数组分成多个子序列(按固定间隔 gap)进行插入排序,逐步缩小间隔直到 gap=1,最终对整个数组进行一次插入排序。通过这种方式减少元素的移动次数。

代码实现

def shell_sort(arr):
    n = len(arr)
    gap = n // 2  # 初始间隔设为数组长度的一半(Knuth序列可优化为3h+1)
    while gap > 0:
        for i in range(gap, n):
            temp = arr[i]  # 当前待插入元素
            j = i
            # 对子序列进行插入排序
            while j >= gap and arr[j - gap] > temp:
                arr[j] = arr[j - gap]
                j -= gap
            arr[j] = temp
        gap = gap // 2  # 缩小间隔
    return arr

注释

  • 时间复杂度:取决于间隔序列,平均 O(n log n) ~ O(n^(3/2)),最坏 O(n²)。

  • 空间复杂度:O(1),原地排序。

  • 稳定性:不稳定(子序列插入可能破坏顺序)。

  • 适用场景:中等规模数据,插入排序的优化替代方案。

 8. 计数排序(Counting Sort)

算法思路
非比较排序,适用于整数且范围已知的情况。统计每个元素的出现次数,累加计数数组以确定元素的最终位置,最后反向填充结果数组。

代码实现

def counting_sort(arr):
    if not arr:
        return []
    max_val = max(arr)
    min_val = min(arr)
    range_size = max_val - min_val + 1
    
    # 初始化计数数组和结果数组
    count = [0] * range_size
    output = [0] * len(arr)
    
    # 统计元素出现次数
    for num in arr:
        count[num - min_val] += 1
    
    # 累加计数数组以确定位置
    for i in range(1, range_size):
        count[i] += count[i-1]
    
    # 反向填充结果数组(保证稳定性)
    for num in reversed(arr):
        output[count[num - min_val] - 1] = num
        count[num - min_val] -= 1
    
    return output

注释

  • 时间复杂度:O(n + k),其中 k 是数据范围(max-min)。

  • 空间复杂度:O(n + k),需要额外空间。

  • 稳定性:稳定(反向填充保证相同元素的顺序)。

  • 适用场景:整数数据且范围较小(如年龄、分数等)。

 9. 桶排序(Bucket Sort)

算法思路
将数据分到多个“桶”中,每个桶单独排序(通常用插入排序),最后合并所有桶。假设数据均匀分布,桶的数量通常与输入规模相关。

代码实现

def bucket_sort(arr, bucket_size=5):
    if len(arr) == 0:
        return arr
    
    min_val = min(arr)
    max_val = max(arr)
    
    # 计算桶的数量
    bucket_count = (max_val - min_val) // bucket_size + 1
    buckets = [[] for _ in range(bucket_count)]
    
    # 将元素分配到桶中
    for num in arr:
        idx = (num - min_val) // bucket_size
        buckets[idx].append(num)
    
    # 对每个桶进行排序(这里用插入排序)
    sorted_arr = []
    for bucket in buckets:
        # 插入排序(或其他简单排序)
        for i in range(1, len(bucket)):
            key = bucket[i]
            j = i-1
            while j >=0 and bucket[j] > key:
                bucket[j+1] = bucket[j]
                j -= 1
            bucket[j+1] = key
        sorted_arr.extend(bucket)
    
    return sorted_arr

注释

  • 时间复杂度:平均 O(n + k),最坏 O(n²)(当所有元素集中在一个桶)。

  • 空间复杂度:O(n + k),需要额外桶空间。

  • 稳定性:稳定(取决于桶内排序算法的稳定性)。

  • 适用场景:数据均匀分布的浮点数(如0~1的小数)。

10. 基数排序(Radix Sort) 

算法思路
按位排序,从最低位到最高位依次对每一位使用稳定的排序算法(如计数排序)。适用于整数或定长字符串。

代码实现

def radix_sort(arr):
    max_val = max(arr) if arr else 0
    exp = 1  # 从个位开始
    
    while max_val // exp > 0:
        # 使用计数排序对当前位排序
        counting = [0] * 10  # 0-9的基数
        output = [0] * len(arr)
        
        # 统计当前位的出现次数
        for num in arr:
            digit = (num // exp) % 10
            counting[digit] += 1
        
        # 累加计数数组
        for i in range(1, 10):
            counting[i] += counting[i-1]
        
        # 反向填充保证稳定性
        for num in reversed(arr):
            digit = (num // exp) % 10
            output[counting[digit] - 1] = num
            counting[digit] -= 1
        
        arr = output
        exp *= 10  # 处理更高位
    
    return arr

 

注释

  • 时间复杂度:O(d(n + k)),d 是最大位数,k 是基数范围(如10进制则k=10)。

  • 空间复杂度:O(n + k)。

  • 稳定性:稳定(依赖底层计数排序的稳定性)。

  • 适用场景:整数或定长字符串(如手机号、身份证号)。

 总结对比

排序算法 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 原地排序 稳定性 适用场景
冒泡排序 O(n²) O(n²) O(n) O(1) 稳定 小规模数据、教学示例
选择排序 O(n²) O(n²) O(n²) O(1) 不稳定 教学示例、简单场景
插入排序 O(n²) O(n²) O(n) O(1) 稳定 小规模数据、基本有序数据
快速排序 O(n log n) O(n²) O(n log n) O(log n) 不稳定 大规模数据、实际应用(需优化基准选择)
归并排序 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 log n)~O(n²) O(n²) O(n log n) O(1) 不稳定 中等规模数据、插入排序优化
计数排序 O(n + k) O(n + k) O(n + k) O(n + k) 稳定 整数且范围较小的数据(如年龄、分数)
桶排序 O(n + k) O(n²) O(n + k) O(n + k) 稳定 均匀分布的浮点数
基数排序 O(d(n + k)) O(d(n + k)) O(d(n + k)) O(n + k) 稳定 整数或定长字符串(如身份证号)

小贴士

  1. 时间复杂度

    • k:数据范围(如计数排序中的 max - min)或基数排序的基数(如10进制为10)。

    • d:基数排序中数字的最大位数。

    • 快速排序的最坏时间复杂度发生在基准选择极不平衡时(如已有序数组且基准固定为第一个元素)。

  2. 原地性

    • 原地排序指不需要额外空间(或仅需常数空间),直接在原数组上操作。

    • 归并排序、计数排序、桶排序、基数排序需要额外空间,因此非原地。

  3. 稳定性

    • 稳定排序保证相等元素的相对顺序不变。

    • 快速排序和堆排序的交换逻辑可能破坏稳定性。

  4. 适用场景优先级

    • 小规模数据:冒泡、选择、插入排序。

    • 大规模数据:快速排序、归并排序、堆排序。

    • 特定数据分布:计数、桶、基数排序。

 欢迎关注Alaskaist 收藏点赞助力蓝桥备战

你可能感兴趣的:(蓝桥杯,排序算法,蓝桥大纲,算法,数据结构,排序算法,python,蓝桥杯)