欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

十大经典排序算法解析(Python实现)

程序员文章站 2022-07-14 19:22:18
...

十大经典排序算法解析

序算法是《数据结构与算法》中最基本的算法之一。

排序算法可以分为内部排序和外部排序。

内部排序是数据记录在内存中进行排序。

而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。

用一张图概括:
十大经典排序算法解析(Python实现)
时间复杂度与空间复杂度

关于时间复杂度:

平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。

线性对数阶 (O(nlog2n)) 排序 快速排序、堆排序和归并排序;

O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。 希尔排序

线性阶 (O(n)) 排序 基数排序,此外还有桶、箱排序。

关于稳定性:

稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。

不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。

1. 冒泡排序

1.1 算法步骤

比较相邻的元素。如果第一个比第二个大,就交换他们两个。

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

针对所有的元素重复以上的步骤,除了最后一个。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

1.2参考代码

def bubble_sort3(arr):
    for j in range(len(arr)-1, 0, -1):
        count = 0
        for i in range(0, j):
            if arr[i] > arr[i + 1]:
                arr[i], arr[i + 1] = arr[i + 1], arr[i]
                count += 1
        if count == 0:
            return


bubble_sort3(arr)
print(arr)  # [1, 3, 4, 7, 8, 34, 67]

2. 选择排序

2.1 算法步骤

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

重复第二步,直到所有元素均排序完毕。

2.2参考代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : xiaoke


def select_sort(alist):
    """选择排序"""
    n = len(alist)
    # 外层循环控制循环次数
    for j in range(0, n - 1):
        # 假设找到的最小元素下标为j
        min_index = j
        # 寻找最小元素的过程
        for i in range(j + 1, n):
            # 假设最小下标的值,大于循环中一个元素,那么就改变最小值的下标
            if alist[min_index] > alist[i]:
                min_index = i
        # 为了容错处理,因为循环一开始就假设把最小值的下标j赋值给变量min_index
        if j != min_index:
            # 在不停的循环中,不停的交换两个不一样大小的值
            alist[min_index], alist[j] = alist[j], alist[min_index]


if __name__ == '__main__':
    alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print("原列表为:%s" % alist)
    select_sort(alist)
    print("新列表为:%s" % alist)

# 结果如下:
# 原列表为:[54, 26, 93, 17, 77, 31, 44, 55, 20]
# 新列表为:[17, 20, 26, 31, 44, 54, 55, 77, 93]

3. 插入排序

3.1 算法步骤

将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

3.2代码参考

lst = [4,5,6,3,2,1]

def insertSort(arr):
    for i in range(1,len(arr)):
        j = i-1
        key = arr[i]
        while j >= 0:
            if arr[j] > key:
                arr[j+1] = arr[j]
                arr[j] = key
            j -= 1
    return arr
print(insertSort(lst))

4. 希尔排序

4.1 算法步骤

选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;

按增量序列个数 k,对序列进行 k 趟排序;

每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

4.2代码参考

def shell_sort(alist):
    """希尔排序"""
    n = len(alist)
    gap = n // 2
    while gap >= 1:
        for j in range(gap, n):
            i = j
            while (i - gap) >= 0:
                if alist[i] < alist[i - gap]:
                    alist[i], alist[i - gap] = alist[i - gap], alist[i]
                    i -= gap
                else:
                    break
        gap //= 2


if __name__ == '__main__':
    alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print("原列表为:%s" % alist)
    shell_sort(alist)
    print("新列表为:%s" % alist)


# 结果如下:
# 原列表为:[54, 26, 93, 17, 77, 31, 44, 55, 20]
# 新列表为:[17, 20, 26, 31, 44, 54, 55, 77, 93]

5. 归并排序

5.1 算法步骤

申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

设定两个指针,最初位置分别为两个已经排序序列的起始位置;

比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

重复步骤 3 直到某一指针达到序列尾;

将另一序列剩下的所有元素直接复制到合并序列尾。

5.2代码参考

import random

def ConfiationAlgorithm(str):
    if len(str) <= 1: #子序列
        return str
    mid = (len(str) / 2)
    left = ConfiationAlgorithm(str[:mid])#递归的切片操作
    right = ConfiationAlgorithm(str[mid:len(str)]) 
    result = []
    #i,j = 0,0

    while len(left) > 0 and len(right) > 0:
        if (left[0] <= right[0]):
            #result.append(left[0])
            result.append(left.pop(0))
            #i+= 1
        else:
            #result.append(right[0])
            result.append(right.pop(0))
            #j+= 1

    if (len(left) > 0):
        result.extend(ConfiationAlgorithm(left))
    else:
        result.extend(ConfiationAlgorithm(right))
    return result   
if __name__ == '__main__':
    a = [20,30,64,16,8,0,99,24,75,100,69]
    print ConfiationAlgorithm(a)
    b = [random.randint(1,1000) for i in range(10)]
    print ConfiationAlgorithm(b)

6. 快速排序

6.1 算法步骤

从数列中挑出一个元素,称为 “基准”(pivot);

重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

6.2代码参考

def quick_sort(array, left, right):
    if left >= right:
        return
    low = left
    high = right
    key = array[low]
    while left < right:
        while left < right and array[right] > key:
            right -= 1
        array[left] = array[right]
        while left < right and array[left] <= key:
            left += 1
        array[right] = array[left]
    array[right] = key
    quick_sort(array, low, left - 1)
    quick_sort(array, left + 1, high)

7. 堆排序

7.1 算法步骤

创建一个堆 H[0……n-1];

把堆首(最大值)和堆尾互换;

把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;

重复步骤 2,直到堆的尺寸为 1。

7.2代码参考

def HeapSort(input_list):
	
	#调整parent结点为大根堆
	def HeapAdjust(input_list,parent,length):
		
		temp = input_list[parent]
		child = 2*parent+1
		
		while child < length:
			if child+1 <length and input_list[child] < input_list[child+1]:
				child +=1
			
			if temp > input_list[child]:
				break
			input_list[parent] = input_list[child]
			parent = child
			child = 2*child+1
		input_list[parent] = temp
	
	if input_list == []:
		return []
	sorted_list = input_list
	length = len(sorted_list)
	#最后一个结点的下标为length//2-1
	#建立初始大根堆
	for i in range(0,length // 2 )[::-1]:
		HeapAdjust(sorted_list,i,length)
	
	for j in range(1,length)[::-1]:
		#把堆顶元素即第一大的元素与最后一个元素互换位置
		temp = sorted_list[j]
		sorted_list[j] = sorted_list[0]
		sorted_list[0] = temp
		#换完位置之后将剩余的元素重新调整成大根堆
		HeapAdjust(sorted_list,0,j)
		print('%dth' % (length - j))
		print(sorted_list)
	return sorted_list
	
		
if __name__ == '__main__':
	input_list = [50,123,543,187,49,30,0,2,11,100]
	print("input_list:")
	print(input_list)
	sorted_list = HeapSort(input_list)
	print("sorted_list:")
	print(input_list)

8. 计数排序

8.1 算法步骤

花O(n)的时间扫描一下整个序列 A,获取最小值 min 和最大值 max

开辟一块新的空间创建新的数组 B,长度为 ( max - min + 1)

数组 B 中 index 的元素记录的值是 A 中某元素出现的次数

最后输出目标整数序列,具体的逻辑是遍历数组 B,输出相应元素以及对应的个数

8.2代码参考

def countsort(lista):
	leng=len(lista);
	c=[];
	res=[];
	for i in range(0,100):
		c.append(0);
	for i in range(0,leng):
		c[lista[i]]=c[lista[i]]+1;
		res.append(0);
	for i in range(0,100):
		c[i]=c[i-1]+c[i];        #c中此时存放的是小于或者等于i的数字的个数
	for i in range(leng-1,-1,-1):
		res[c[lista[i]]-1]=lista[i];
		c[lista[i]]=c[lista[i]]-1;	
	return res;
lista=[5,4,2,5,1,7];        #计数排序测试代码
lista=countsort(lista);
print lista;	

9. 桶排序

9.1 算法步骤

设置固定数量的空桶。

把数据放到对应的桶中。

对每个不为空的桶中数据进行排序。

拼接不为空的桶中数据,得到结果

9.2代码参考

class node:
	def __init__(self,k):
		self.key=k;
		self.next=None;
 
def bucketsort(lista):
	h=[];
	for i in range(0,10):
		h.append(node(0));
	for i in range(0,len(lista)):
		tmp=node(lista[i]);
		map=lista[i]/10;
		p=h[map];
		if p.key is 0:
			h[map].next=tmp;
			h[map].key=h[map].key+1;
		else:
			while(p.next !=None and p.next.key<=tmp.key):
				p=p.next;
			tmp.next=p.next;
			p.next=tmp;
			h[map].key=h[map].key+1;
	k=0;
	for i in range(0,10):
		q=h[i].next;
		while(q != None ):
			lista[k]=q.key;
			k=k+1;
			q=q.next;
	return lista;
lista=[1,4,23,45,97,22,10,4];     #桶排序测试代码
bucketsort(lista);
print lista;

10. 基数排序

10.1 算法步骤

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零

从最低位开始,依次进行一次排序

从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列

10.2参考代码

def radix_sort(list, d=3): # 默认三位数,如果是四位数,则d=4,以此类推
    for i in range(d):  # d轮排序
        s = [[] for k in range(10)]  # 因每一位数字都是0~9,建10个桶
        for j in list:
            s[int(j / (10 ** i)) % 10].append(j)  
        re = [a for b in s for a in b]
    return re