python 数据结构与算法——排序

排序的分类

基准解释
按比较次数 O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n)
按交换次数
按内存占用需要多少额外空间
按是否递归比如快排是递归算法,选择、插入排序是非递归算法
按稳定性是否保持相同 key 排序前后的相对次序
按适应性是否受到初始顺序的影响,有些算法对预排序的数组有较好的性能
按是否需要内存之外的存储内排序、外排序

排序的复杂度

排序算法额外空间复杂度最好时间间复杂度最坏时间复杂度平均时间复杂度稳定性
冒泡排序 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2)
选择排序 O ( 1 ) O(1) O(1) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2)
插入排序 O ( 1 ) O(1) O(1) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2)
希尔排序 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( n log ⁡ 2 ( n ) ) O(n\log^2(n)) O(nlog2(n))-
归并排序 O ( n ) O(n) O(n) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))
堆排序 O ( 1 ) O(1) O(1) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))
快排 O ( log ⁡ ( n ) ) O(\log(n)) O(log(n)) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n)) O ( n 2 ) O(n^2) O(n2) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))-
树排序 O ( n ) O(n) O(n)- O ( n 2 ) O(n^2) O(n2) O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))-

python 实现

def swap(A, x, y):
    A[x], A[y] = A[y], A[x]

冒泡排序

def BubbleSort(A):
	for i in range(len(A)):
		for k in range(len(A) - 1, i, -1):
			if (A[k] < A[k - 1]):
				swap(A, k, k-1)

改进: 如果第一次遍历,没有发生交换,说明已经排好序了,直接结束,对最好情况的时间复杂度为 O ( n ) O(n) O(n)

def BubbleSort(A):
	swapped = True
	for i in range(len(A)):
		if not swapped:
			return
		for k in range(len(A) - 1, i, -1):
			if (A[k] < A[k - 1]):
				swap(A, k, k-1)
				swapped = True

选择排序

def SelectionSort(A):
    for i in range(len(A)):
        least = i
        for k in range(i + 1, len(A)):
            if A[k] < A[least]:
                least = k
        swap(A, least, i)

插入排序

def InsertionSort(A):
    for i in range(1, len(A)):
        tmp = A[i]
        k = i
        while k > 0 and tmp < A[k - 1]:
            A[k] = A[k - 1]
            k -= 1
        A[k] = tmp

希尔排序

def ShellSort(A):
    sublistcount = len(A) // 2
    while sublistcount > 0:
        for startposition in range(sublistcount):
            gapInsertionSort(A, startposition, sublistcount)
        # print("After increments of size", sublistcount, "The list is", A)
        sublistcount = sublistcount // 2

def gapInsertionSort(A, start, gap):
    for i in range(start + gap, len(A), gap):
        currentvalue = A[i]
        position = i
        while position >= gap and A[position - gap] > currentvalue:
            A[position] = A[position - gap]
            position = position - gap
        A[position] = currentvalue

归并排序

def MergeSort(A):
    if len(A) > 1:
        mid = len(A) // 2
        lefthalf = A[:mid]
        righthalf = A[mid:]
        MergeSort(lefthalf)
        MergeSort(righthalf)
        i = j = k = 0
        while i < len(lefthalf) and j < len(righthalf):
            if lefthalf[i] < righthalf[j]:
                A[k] = lefthalf[i]
                i = i + 1
            else:
                A[k] = righthalf[j]
                j = j + 1
            k = k + 1

        while i < len(lefthalf):
            A[k] = lefthalf[i]
            i = i + 1
            k = k + 1

        while j < len(righthalf):
            A[k] = righthalf[j]
            j = j + 1
            k = k + 1

堆排序

参见 python 数据结构与算法——优先队列和堆

快排

如果不计空间成本,下面是最直观的实现,完美诠释分治的思想

def partition(A):
    pivot, A = A[0], A[1:]
    low = [x for x in seq if x <= pivot]
    high = [x for x in seq if x > pivot]
    return low, pivot, high

def quickSort(A):
    if len(A) <= 1:
        return A
    low, pivot, high = partition(A)
    return quickSort(low) + [pivot] + quickSort(high)

下面是个中规中矩的快排,随机选取分割点

import random

def QuickSort(A, low, high):
    if low < high:
        pivot = Partition(A, low, high)
        QuickSort(A, low, pivot - 1)
        QuickSort(A, pivot + 1, high)

def Partition(A, low, high):
    pivot = low + random.randrange(high - low + 1)
    swap(A, pivot, high)
    for i in range(low, high):
        if A[i] <= A[high]:
            swap(A, i, low)
            low += 1
    swap(A, low, high)
    return low

树排序

使用二叉排序树,参见 二叉树排序算法

线性时间排序算法

计数排序

如果已知数组 A 的最大值小于 k,那么可以在 O ( n + k ) O(n+k) O(n+k) 时间内完成排序,空间复杂度 O ( k ) O(k) O(k)

def counting_sort(A, k):
    C = [0] * k
    for a in A:
        C[a] += 1           
    i = 0
    for a in range(k):
        for c in range(C[a]): 
            A[i] = a
            i += 1
    return A

print(counting_sort( [1, 4, 7, 2, 1, 3, 2, 1, 4, 2, 3, 2, 1], 8 ))

桶排序

桶排序和计数排序类似,如果已知元素的范围, 如: [ m i n , m a x ] [min, max] [min,max],那么

  • 先把这个区间 [ m i n , m a x ] [min, max] [min,max] 分成 K K K 个子区间(桶),此时这些区间的顺序是已知的
  • 遍历数组,把元素这个放进这 K K K 个桶
  • 对每个桶内元素排序
  • 依次读取各个桶内的元素

桶排序的复杂度分析

总元素有 N N N 个,每个桶内的元素平均有 N / K N/K N/K

如果对每个桶采用复杂度为 O ( n 2 ) O(n^2) O(n2) 的排序算法,即 O ( N 2 / K 2 ) O(N^2/K^2) O(N2/K2),那么 K K K 个桶就需要 O ( N 2 / K ) O(N^2/K) O(N2/K) 的时间

最后读取所有元素,需要 O ( N ) O(N) O(N)

所以总的时间代价为: T ( N ) = O ( N 2 / K ) + O ( N ) T(N) = O(N^2/K) + O(N) T(N)=O(N2/K)+O(N)

平平无奇🤨

基数排序

def RadixSort(A):
	RADIX = 10
	maxLength = False
	tmp, placement = -1, 1

	while not maxLength:
		maxLength = True
		buckets = [list() for _ in range(RADIX)]
		for i in A:
			tmp = i // placement
			buckets[tmp % RADIX].append(i)
			if maxLength and tmp > 0:
				maxLength = False

		a = 0
		for b in range(RADIX):
			buck = buckets[b]
			for i in buck:
				A[a] = i
				a += 1

		# 进入下一位
		placement *= RADIX
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值