十大排序算法(Python实现)

十大排序算法(Python实现)

十种常见排序算法可以分为两大类

非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。执行计算需要消耗的时间长短。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。执行计算需要的内存空间
参考:https://blog.csdn.net/weixin_41571493/article/details/81875088

  1. 交换排序
    1.1 冒泡排序(Bubble Sort)
    1.2 快速排序(Quick Sort)
  2. 插入排序
    2.1 简单插入排序(Insert Sort)
    2.2 希尔排序(Shell Sort)
    3.选择排序
    3.1 简单选择排序(Select Sort)
    3.2 堆排序(Heap Sort)
  3. 归并排序
    4.1 二路归并排序(Two-way Merge Sort)
  4. 线性时间非比较类排序
    5.1 计数排序(Counting Sort)
    5.2 桶排序(Bucket Sort)
    5.3 基数排序(Radix Sort)
    在这里插入图片描述

1. 交换排序

1.1 冒泡排序(Bubble Sort)
基本思想:比较相邻的元素。如果第一个比第二个大,就交换它们两个;小的放前面, 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数。
复杂度:
冒泡排序对n个数据操作n-1轮,每轮找出一个最大(小)值。
操作只对相邻两个数比较与交换,每轮会将一个最值交换到数据列首(尾),像冒泡一样。
每轮操作O(n)次,共O(n)轮,时间复杂度O(n^2)。
额外空间开销出在交换数据时那一个过渡空间,空间复杂度O(1)
Code:在这里插入图片描述

def BubbleSort(lst):
    n=len(lst)
    if n<=1:
        return lst
    for i in range (0,n):
        for j in range(0,n-i-1):
            if lst[j]>lst[j+1]:
                (lst[j],lst[j+1])=(lst[j+1],lst[j])
    return lst
x=input("请输入待排序数列:\n")
y=x.split()
arr=[]
for i in  y:
    arr.append(int(i))
arr=BubbleSort(arr)
print(arr)
print("数列按序排列如下:")
for i in arr:
print(i,end=' ')

1.2 快速排序(Quick Sort)

基本思想:从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
复杂度:
基准值若能把数据分为平均的两块,划分次数O(logn),每次划分遍历比较一遍O(n),时间复杂度O(nlogn)。
额外空间开销出在暂存基准值,O(logn)次划分需要O(logn)个,空间复杂度O(logn)
Code:
在这里插入图片描述

def quick_sort(data):    
    """快速排序"""    
    if len(data) >= 2:  # 递归入口及出口        
        mid = data[0]  # 选取基准值,也可以选取第一个或最后一个元素        
        left, right = [], []  # 定义基准值左右两侧的列表        
        data.remove(mid)  # 从原始数组中移除基准值        
        for num in data:            
            if num >= mid:                
                right.append(num)            
            else:                
                left.append(num)        
        return quick_sort(left) + [mid] + quick_sort(right)    
    else:        
        return data
 示例:
array = [2,3,5,7,1,4,6,15,5,2,7,9,10,15,9,17,12]
print(quick_sort(array))
输出为[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]

2. 插入排序

2.1 简单插入排序(Insert Sort)
基本思想:• 从第一个元素开始,该元素可以认为已经被排序;
• 取出下一个元素,在已经排序的元素序列中从后向前扫描;
• 如果该元素(已排序)大于新元素,将该元素移到下一位置;
复杂度:简单插入排序操作n-1轮,每轮将一个未排序树插入排好序列。
开始时默认第一个数有序,将剩余n-1个数逐个插入。插入操作具体包括:比较确定插入位置,数据移位腾出合适空位
每轮操作O(n)次,共O(n)轮,时间复杂度O(n^2)。
额外空间开销出在数据移位时那一个过渡空间,空间复杂度O(1)
Code
在这里插入图片描述

def insert_sort(data):
	l=len(data)
	for i in range(1,n):
		j=i
		while j>0:
			if data[j]<data[j-1]:
				data[j],data[j-1]=data[j-1],data[j]
				j-=1
			else:
				break
	return data
data=[2,4,12,8,34,26,78,1]
p=insert_sort(data)
print(p)
				
def insertsort(data):
	L=len(data)
	for i in range(1,L):
		for j in range(i):
			if data[i]<data[j]:
				data.insert(j,data[i])
				data.pop(i+1)
				break
	return data
array=[2,4,12,8,34,26,78,1]
p=insertsort(array)
print(p)

2.2 希尔排序(Shell Sort):https://blog.csdn.net/weixin_42587961/article/details/81072911
基本思想:将数组列在一个表中并对列分别进行插入排序,重复这过程,不过每次用更长的列(步长更长了,列数更少了)来进行。最后整个表就只有一列了
复杂度:希尔排序对序列划分O(n)次,每次简单插入排序O(logn),时间复杂度O(nlogn)
额外空间开销出在插入过程数据移动需要的一个暂存,空间复杂度O(1)
Code:
在这里插入图片描述

def shell_sort(alist):
    n = len(alist)
    # 初始步长 
    gap = n // 2
    while gap > 0:
        # 按步长进行插入排序
        for i in range(gap,n):
            j = i
            # 原地插入排序,之前的插入排序另辟空间存储的
            while j>=gap and alist[j-gap] > alist[j]:
                alist[j-gap], alist[j] = alist[j], alist[j-gap]
                j -= gap
        # 得到新的步长
        gap = gap //2

alist = [54,26,93,17,77,31,44,55,20]
shell_sort(alist)
print(alist)	

3.选择排序

3.1 简单选择排序(Select Sort)
基本思想 :同样对数据操作n-1轮,每轮找出一个最大(小)值。
复杂度:每轮操作O(n)次,共O(n)轮,时间复杂度O(n^2)。
额外空间开销出在交换数据时那一个过渡空间,空间复杂度O(1)。
Code :https://blog.csdn.net/lerry13579/article/details/82051883
在这里插入图片描述

def SelectSort(sorted_list):
    l = len(input_list)
    if l == 0:
        return []
    for i in range(l):
        # 默认第i个元素是每次的最小值的索引
        min_index = i
        # 找到后面元素最小的索引
        for j in range(i + 1, l):
            if sorted_list[min_index] > sorted_list[j]:
                min_index = j
        # 将找到的最小元素放入前面已经有序序列的末尾
        temp = sorted_list[i]
        sorted_list[i] = sorted_list[min_index]
        sorted_list[min_index] = temp
        print("%dth" % (i + 1))
        print(sorted_list)
    return sorted_list
if __name__ == '__main__':
    input_list = [50, 123, 543, 187, 49, 30, 0, 2, 11, 100]
    print("input_list:",input_list)
    sorted_list = SelectSort(input_list)
    print("sorted_list:",input_list)

3.2 堆排序(Heap Sort):https://www.cnblogs.com/shiqi17/p/9694938.html
基本思想 :将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。可称为有序区,然后将剩余n-1个元素重新构造成一个堆,估且称为堆区(未排序)。这样会得到n个元素的次小值。重复执行,有序区从:1—>n,堆区:n–>0,便能得到一个有序序列了
复杂度:对O(n)级别个非叶子节点进行堆调整操作O(logn),时间复杂度O(nlogn);之后每一次堆调整操作确定一个数的次序,时间复杂度O(nlogn)。合起来时间复杂度O(nlogn)
额外空间开销出在调整堆过程,根节点下移交换时一个暂存空间,空间复杂度O(1)
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),是不稳定排序
Code
在这里插入图片描述

#调整父节点 与孩子大小, 制作大顶堆
def adjust_heap(data, par_node, high):
    new_par_node = par_node
    j = 2*par_node +1   #取根节点的左孩子, 如果只有一个孩子 high就是左孩子,如果有两个孩子 high 就是右孩子
    while j <= high: #如果 j = high 说明没有右孩子,high就是左孩子
        if j < high and data[j] < data[j+1]: #如果这儿不判断 j < high 可能超出索引
            # 一个根节点下,如果有两个孩子,将 j  指向值大的那个孩子
            j += 1
        if data[j] > data[new_par_node]: #如果子节点值大于父节点,就互相交换
            data[new_par_node], data[j] = data[j], data[new_par_node]
            new_par_node = j #将当前节点,作为父节点,查找他的子树
            j = j * 2 + 1
        else:
            # 因为调整是从上到下,所以下面的所有子树肯定是排序好了的,
            #如果调整的父节点依然比下面最大的子节点大,就直接打断循环,堆已经调整好了的
            break
#索引计算: 0 -->1 --->....
#父节点 i   左子节点:偶数:2i +1  右子节点:基数:2i +2  注意:当用长度表示最后一个叶子节点时 记得 -1
#从第一个非叶子节点(即最后一个父节点)开始,即 list_.length//2 -1(len(list_)//2 - 1)
#开始循环到 root 索引为:0 的第一个根节点, 将所有的根-叶子 调整好,成为一个 大顶堆
def heap_sort(lst):
    """
    根据列表长度,找到最后一个非叶子节点,开始循化到 root 根节点,制作 大顶堆
    :param lst: 将列表传入
    :return:
    """
    length = len(lst)
    last = length -1  #最后一个元素的 索引
    last_par_node = length//2 -1
    while last_par_node >= 0:
        adjust_heap(lst, last_par_node, length-1)
        last_par_node -= 1  #每调整好一个节点,从后往前移动一个节点
    while last > 0:
        lst[0], lst[last] = lst[last],lst[0]
        # 调整堆少让 adjust 处理最后已经排好序的数,就不处理了
        adjust_heap(lst, 0, last-1)
        last -= 1
    return lst #将列表返回
list_ = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
heap_sort(list_)
print(list_)

4. 归并排序

4.1 二路归并排序(Two-way Merge Sort)
基本思想 :把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
复杂度:归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(nlogn)的时间复杂度。代价是需要额外的内存空间。
Code :
在这里插入图片描述

def MERGE(left, right):
    i = 0
    j = 0
    res = []
    while (i < len(left) and j < len(right)):
        if left[i] < right[j]:
            res.append(left[i])
            i += 1
        else:
            res.append(right[j])
            j += 1
    res += left[i:] + right[j:]
    return res
def MERGE_SORT(A):
    print('list:',A)
    if (len(A) <= 1):
        return A
    mid = len(A) // 2
    print('mid:',mid)
    left = MERGE_SORT(A[:mid])
    right = MERGE_SORT(A[mid:])
    return MERGE(left, right)

if __name__ == '__main__':
    A = [5, 4, 6, 9, 12, 35, 34, 32, 11, 23, 21, 14, 3, 8]
    print(MERGE_SORT(A))

5. 线性时间非比较类排序

5.1 计数排序(Counting Sort)
基本思想 :1找出待排序的数组中最大和最小的元素;
2统计数组中每个值为i的元素出现的次数,存入数组C的第i项,计数累加;
3填充目标数组:将每个元素i放在新数组的第arr(i)项。
复杂度:计数数组的大小取决于待排数据取值范围,所以对数据有一定要求,否则空间开销无法承受。
计数排序只需遍历一次数据,在计数数组中记录,输出计数数组中有记录的下标,时间复杂度为O(n+k)。
额外空间开销即指计数数组,实际上按数据值分为k类(大小取决于数据取值),空间复杂度O(k)。

Code :
在这里插入图片描述

def CountSort(lst):
    n = len(lst)
    num = max(lst)
    count = [0] * (num + 1)
    for i in range(0, n):
        count[lst[i]] += 1
    arr = []
    for i in range(0, num + 1):
        for j in range(0, count[i]):
            arr.append(i)
    return arr

t=CountSort([1,5,2,1,9,4,6,5])
print(t)

5.2 桶排序(Bucket Sort)
基本思想 :
• 设置一个定量的数组当作空桶;
• 遍历输入数据,并且把数据一个一个放到对应的桶里去;
• 对每个不是空的桶进行排序;
• 从不是空的桶里把排好序的数据拼接起来。
复杂度:假设n个数据,划分为k个桶,桶内采用快速排序,时间复杂度为O(n)+O(k * n/klog(n/k))=O(n)+O(n(log(n)-log(k))),
显然,k越大,时间复杂度越接近O(n),当然空间复杂度O(n+k)会越大,这是空间与时间的平衡。
Code
5.3 基数排序(Radix Sort)
基本思想 :将整数按位数切割成不同的数字,然后按每个位数分别比较。
步骤:
1-将整数按位数切割成不同的数字,然后按每个位数分别比较。
从低位开始将待排序的数按照这一位的值放到相应的编号为0~9的桶中。
2-等到低位排完得到一个子序列,再将这个序列按照次低位的大小进入相应的桶中,一直排到最高位为止,数组排序完成。
1获取列表中最大数值的位数d=len(str(max(list)))
2外循环产生d个空列表,每个列表有10个空列表值,
3-依次进行从个位数、十位数、百位数等的操作,按照位数将列表中的数值放入相应的空列表中,内循环过后,依次拿出空列表的值赋给原先的列表
复杂度:基数排序基于分别排序,分别收集,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要O(n)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2n) ,当然d要远远小于n,因此基本上还是线性级别的。
基数排序的空间复杂度为O(n+k),其中k为桶的数量。一般来说n>>k,因此额外空间需要大概n个左右。
Code :
在这里插入图片描述

def RadixSort(Lst):
    d = len(str(max(Lst))) #列表中的最大元素的位数
    for i in range(d): #0-9一共10个桶
        BucketLst = [[] for k in range(10)]
		print(BucketLst)
        for j in range(len(Lst)):
		   BucketLst[Lst[j]//(10**i)%10].append(Lst[j]) #关键1-取位数  10**i
		Lst = [number for B in BucketLst for number in B] #关键2-依次拿出桶中的元素,给原列表Lst, 
									# 而不是给缓存 temp = [number for B in BucketLst for number in B]
    return Lst
if __name__ == "__main__":
    Lst1 = [1,44,5,222,1,34,89,6,2,7]
    print(Lst1)
print(RadixSort(Lst1))

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值