首页 5G技术

常用排序算法全景解析:原理、代码与避坑指南

分类:5G技术
字数: (2476)
阅读: (2985)
内容摘要:常用排序算法全景解析:原理、代码与避坑指南,

在后端开发中,常见排序算法是基础也是关键。数据排序在各种业务场景中都扮演着重要角色,比如用户列表排序、商品价格排序、搜索结果排序等。选择合适的排序算法,直接影响到系统的性能和用户体验。本文将深入探讨几种常见的排序算法,并结合实际案例进行分析,让你在实际开发中能够游刃有余。

排序算法概览

我们将讨论以下几种常见的排序算法:

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 快速排序
  • 归并排序
  • 堆排序
  • 计数排序
  • 桶排序
  • 基数排序

对于每种算法,我们将介绍其原理、时间复杂度、空间复杂度,并给出示例代码。

冒泡排序

冒泡排序是最简单的排序算法之一。它重复地遍历要排序的列表,比较相邻的元素,如果它们的顺序错误就交换它们。重复这个过程直到没有需要交换的元素。

常用排序算法全景解析:原理、代码与避坑指南
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1] :
                arr[j], arr[j+1] = arr[j+1], arr[j]
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)
  • 稳定性: 稳定

选择排序

选择排序的工作原理是每次从待排序的数据元素中选出最小(或最大)的一个元素存放在序列的起始位置,然后再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到全部待排序的数据元素排完。

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]
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)
  • 稳定性: 不稳定

插入排序

插入排序的工作方式是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

def insertion_sort(arr):
    n = len(arr)
    for i in range(1, n):
        key = arr[i]
        j = i-1
        while j >= 0 and key < arr[j] :
                arr[j + 1] = arr[j]
                j -= 1
        arr[j + 1] = key
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)
  • 稳定性: 稳定

快速排序

快速排序是一种分而治之的算法。它选择一个元素作为“基准”(pivot),并将列表分成两个子列表:小于基准的元素和大于基准的元素。然后递归地对子列表进行排序。

常用排序算法全景解析:原理、代码与避坑指南
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)
  • 时间复杂度: 平均 O(n log n),最坏 O(n^2)
  • 空间复杂度: O(log n)
  • 稳定性: 不稳定

实战避坑: 快速排序在处理已经基本有序的数据时,性能会退化到 O(n^2)。一种常见的优化方式是随机选择基准值,或者使用三数取中法选择基准值,尽可能避免选择到最大或最小的元素。

归并排序

归并排序也是一种分而治之的算法。它将列表递归地分成更小的子列表,直到每个子列表只包含一个元素。然后将子列表按排序后的顺序合并。

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr)//2
        L = arr[:mid]
        R = arr[mid:]
        merge_sort(L)
        merge_sort(R)
        i = j = k = 0
        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i += 1
            else:
                arr[k] = R[j]
                j += 1
            k += 1
        while i < len(L):
            arr[k] = L[i]
            i += 1
            k += 1
        while j < len(R):
            arr[k] = R[j]
            j += 1
            k += 1
  • 时间复杂度: O(n log n)
  • 空间复杂度: O(n)
  • 稳定性: 稳定

应用场景: 归并排序非常适合处理大规模数据,例如外部排序。在分布式系统中,可以利用 MapReduce 框架并行地进行归并排序,提高排序效率。可以配合 Nginx 使用,例如对日志文件进行排序,从而方便后续的数据分析。

常用排序算法全景解析:原理、代码与避坑指南

堆排序

堆排序利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

def heapify(arr, n, i):
    largest = i  # Initialize largest as root
    l = 2 * i + 1     # left = 2*i + 1
    r = 2 * i + 2     # right = 2*i + 2

    if l < n and arr[i] < arr[l]:
        largest = l

    if r < n and arr[largest] < arr[r]:
        largest = r

    if largest != i:
        arr[i],arr[largest] = arr[largest],arr[i]  # swap

        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]   # swap
        heapify(arr, i, 0)
  • 时间复杂度: O(n log n)
  • 空间复杂度: O(1)
  • 稳定性: 不稳定

计数排序

计数排序是一种非基于比较的排序算法,它适用于一定范围内的整数排序。其核心思想是统计每个整数出现的次数,然后根据统计结果将整数重新排列。

def counting_sort(arr):
    max_element = int(max(arr))

    count_array = [0] * (max_element + 1)

    for element in arr:
        count_array[element] += 1

    sorted_array = []
    for i in range(len(count_array)):
        sorted_array.extend([i] * count_array[i])

    return sorted_array
  • 时间复杂度: O(n+k),其中k是整数的范围
  • 空间复杂度: O(k)
  • 稳定性: 稳定

限制: 计数排序只适用于整数排序,且整数的范围不宜过大,否则会占用大量内存。

常用排序算法全景解析:原理、代码与避坑指南

桶排序

桶排序是将数组分到有限数量的桶子里。每个桶子再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序)。

def bucket_sort(arr):
    bucket = []

    for i in range(len(arr)):
        bucket.append([])

    for j in arr:
        index_b = int(10 * j)
        bucket[index_b].append(j)

    for i in range(len(arr)):
        bucket[i] = sorted(bucket[i])

    k = 0
    for i in range(len(arr)):
        for j in range(len(bucket[i])):
            arr[k] = bucket[i][j]
            k += 1
    return arr
  • 时间复杂度: O(n+k),其中k是桶的数量
  • 空间复杂度: O(n+k)
  • 稳定性: 稳定

基数排序

基数排序是一种非比较型的整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。基数排序可以采用LSD(Least Significant Digit)或MSD(Most Significant Digit)两种方法进行排序。

def radix_sort(arr):
    max_value = max(arr)
    radix = 1
    while max_value // radix > 0:
        arr = counting_sort_for_radix(arr, radix)
        radix *= 10
    return arr

def counting_sort_for_radix(arr, radix):
    size = len(arr)
    output = [0] * size
    count = [0] * 10
    for i in range(size):
        index = arr[i] // radix
        count[index % 10] += 1
    for i in range(1, 10):
        count[i] += count[i - 1]
    i = size - 1
    while i >= 0:
        index = arr[i] // radix
        output[count[index % 10] - 1] = arr[i]
        count[index % 10] -= 1
        i -= 1
    return output
  • 时间复杂度: O(nk),其中n是数组长度,k是最大值的位数
  • 空间复杂度: O(n+k)
  • 稳定性: 稳定

总结

选择合适的常见排序算法取决于具体的应用场景。如果数据量较小,可以选择简单的冒泡排序或插入排序。如果对性能要求较高,则可以选择快速排序或归并排序。对于特定类型的数据,例如整数,可以选择计数排序或基数排序。理解各种排序算法的原理和特性,才能在实际开发中做出正确的选择,提高系统的性能和效率。

常用排序算法全景解析:原理、代码与避坑指南

转载请注明出处: 程序员老猫

本文的链接地址: http://m.acea1.store/blog/176155.SHTML

本文最后 发布于2026-04-02 19:46:16,已经过了25天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 蓝天白云 5 天前
    计数排序虽然快,但是对内存消耗确实是个问题,感谢提醒!
  • 海王本王 3 天前
    计数排序虽然快,但是对内存消耗确实是个问题,感谢提醒!
  • 月光族 5 天前
    计数排序虽然快,但是对内存消耗确实是个问题,感谢提醒!
  • 奶茶续命 1 天前
    感谢分享!之前一直对快速排序的基准值选择很困惑,看了你的避坑经验,豁然开朗。