常见排序算法的 Python 实现

2019/07/27 Python 算法 逃离CSDN

常见排序算法的 Python 实现

选择排序

"""
选择排序,顾名思义,扫描全表,选最小值
外循环走size-1次,每一次确定当前一个最小的数
内循环走size-(已经确定的最小数),理解为当前位置之前的数字都已有序,从当前位置出发到结尾扫描确定当前最小的数字
时间复杂度:平均O(n**2),最坏O(n**2),最好O(n**2)
空间复杂度:O(1)
稳定性:不稳定
"""

def select_sort(sort_list):
    for i in range(size-1):
        min = i
        for j in range(i, size):
            if sort_list[j] < sort_list[min]:
                min = j
        sort_list[min], sort_list[i] = sort_list[i], sort_list[min]
    print("选择排序结果是:",sort_list)

冒泡排序

"""
冒泡排序,逐个比较,将最大的数排到最后方
外循环走size-1次,每次确定一个最大的数
内循环走size-(当前已确定的数),理解为从头开始,两两比较,a(n)>a(n+1),则交换
时间复杂度:平均O(n**2),最坏O(n**2),最好O(n)
空间复杂度:O(1)
稳定性:稳定
"""
def bub_sort(sort_list):
    for i in range(size-1):
        for j in range(1, size):
            if sort_list[j] < sort_list[j-1]:
                sort_list[j], sort_list[j-1] = sort_list[j-1], sort_list[j]
    print("冒泡排序结果是:",sort_list)

插入排序

"""
插入排序,类似于体育课排队列
外循环走size-1次,每次确定一个较小的数,一次内循环结束,当前位置的左侧是相对大小确定的
内循环走0次或者当前已确定数的次数,理解为当前数与之前的第一个数对比,若小于则交换,继而继续比较,所以最少0次,最多当前已确定数次
时间复杂度:平均O(n**2),最坏O(n**2),最好O(n)
空间复杂度:O(1)
稳定性:稳定
"""
def insert_sort(sort_list):
    for i in range(1, size):
        j = i
        while j > 0 and sort_list[j]<sort_list[j-1]:
            sort_list[j], sort_list[j-1] = sort_list[j-1], sort_list[j]
            j -= 1
    print("插入排序结果是:",sort_list)

希尔排序

"""
希尔排序(该处指配合插入排序的希尔排序),由插入排序的定义可以看出来,当前的数想要确定位置必须与之前的数字逐个比较,
而希尔排序改成h个比较,这样做的好处是,针对数据量大的数组,排序的过程更轻松(构建h个不同的子数组,每个子数组逻辑相邻(相差距离为h))
外循环的运算次数为(size = size//3循环,直到size等于1,每循环一次,运算次数加一
如size = 150,150//3=50(1次),50//3=16(2次),16//3=5(3次),5//3=1(4次))
内循环为选择插入排序,次数由当前的外循环变量决定
时间复杂度:平均O(n**1.3),其他情况不好分析
空间复杂度:O(1)
稳定性:不稳定
"""
def shell_sort(sort_list):
    h = 1
    while h < size//3:
        h = h*3+1

    while h >=1:
        for i in range(h, size):
            j = i
            while j > h and sort_list[j]<sort_list[j-h]:
                sort_list[j], sort_list[j-h] = sort_list[j-h], sort_list[j]
                j -= h
        h = h//3
    print("希尔排序结果是:",sort_list)

归并排序

"""
归并排序,分治算法思想,将一个大问题分解成若干个小问题,若问题类似可用递归完成
常见两种归并算法,自顶向下和自底向上
自顶向下的算法用递归的方法,先解决左边的排序,再解决右边的排序
自底向上的算法用拆解合并的思想,先拆成size/2个小数组进行归并排序,继而将结果拆成size/4个数组归并排序,当size/(2**n)<1时完成排序
时间复杂度:平均O(nlog2n),最坏O(nlog2n),最好O(nlog2n)
空间复杂度:O(n)(需要一个临时数组来保存)
稳定性:稳定
"""
class merge(object):
    #原地归并抽象方法,方便复用,传入数组,左值,中值,右值
    def merge_sort(self, sort_list, lo, mid, hi):       
        i = lo
        j = mid+1
        aux = copy.deepcopy(sort_list)      

        for k in range(lo, hi+1):
            if i > mid:
                sort_list[k] = aux[j]
                j += 1
            elif j > hi:
                sort_list[k] = aux[i]
                i += 1
            elif aux[j] <= aux[i]:
                sort_list[k] = aux[j]
                j += 1
            else:
                sort_list[k] = aux[i]
                i += 1

    def sort(self, sort_list):
        self.sort1(sort_list, 0, size-1)

    #自顶向下的归并排序
    def sort1(self, sort_list, lo, hi):
        if hi <= lo:            
            return sort_list

        mid = lo + (hi-lo)//2
        self.sort1(sort_list, lo, mid)
        self.sort1(sort_list, mid+1, hi)
        self.merge_sort(sort_list, lo, mid, hi)

    def sort2(self, sort_list):
        sz = 1
        while sz < size:
            lo = 0
            while lo < size-sz:
                self.merge_sort(sort_list, lo, lo+sz-1, min(lo+sz+sz-1, size-1))
                lo += sz+sz
            sz = sz+sz
        print(sort_list)

快速排序

"""
快速排序,是常规条件下最快的排序算法,使用分治算法思想,利用递归完成
首先先改变数组内部顺序(消除输入依赖),然后通过切分函数找出一个值(二分切分中,该值越接近正确顺序的中值越好)
以该值为mid,递归调用自身,分而治之
重点在于切分函数,二分切分函数的思想是,以某子数组第一个数a为基准,
从左往右扫描找出一个大于a的数,再从右往左扫描找出一个小于a的数,两者交换
最后将a放到正确的位置,返回切分的数的索引
时间复杂度:平均O(nlog2n),最坏O(n**2),最好O(nlog2n)
空间复杂度:O(log2n)(需要一个临时数组来保存)
稳定性:不稳定
"""
class quick(object):
    #消除输入依赖
    def sort(self, sort_list):
        random.sample(sort_list, size)
        self.sort1(sort_list, 0, size-1)

    #递归主函数体,从切分函数得到切分索引,左右递归,递归结束不用归并
    def sort1(self, sort_list, lo, hi):
        if hi <= lo:
            return sort_list

        j = self.partition(sort_list, lo, hi)
        self.sort1(sort_list, lo, j-1)
        self.sort1(sort_list, j+1, hi)

    #切分函数,左右指针,轮流扫描,交换位置,最后将切分元素放到正确的位置,返回切分索引
    def partition(self, sort_list, lo, hi):
        i = lo
        j = hi+1
        v = sort_list[lo]

        while True:
            i = i + 1
            while sort_list[i]<v:
                if i==hi:
                    break
                i += 1
            j = j - 1
            while v < sort_list[j]:
                if j==lo:
                    break
                j -= 1

            if i >= j:
                break

            sort_list[i], sort_list[j] = sort_list[j], sort_list[i]

        sort_list[lo], sort_list[j] = sort_list[j], sort_list[lo]
        return j

基数排序

"""
基数排序,不进行比较的整数排序算法,基数指的是整数的进制(默认为10),
根据位数要做几次不同的桶排序,位数的计算为int(math.ceil(math.log(max(sort_list)+1, radix)))
每次循环完成当前位数(个位、十位、百位)的大小排序,理解过程可见http://bubkoo.com/2014/01/15/sort-algorithm/radix-sort/
一共有十个桶,分别对应0-10,每个桶有若干数据,则桶可以用二维数组完成,记为a[index1][index2],
对每一个sort_list里的数,index1 = sort_list_num%(radix**i)//(radix**(i-1))
时间复杂度:平均O(k*n),最坏O(k*n),最好O(k*n),k为最大数字的位数
空间复杂度:O(n)
稳定性:稳定
"""
def radix_sort(sort_list, radix=10):
    """sort_list为整数列表, radix为基数"""
    K = int(math.ceil(math.log(max(sort_list)+1, radix)))   
    for i in range(1, K+1):
        bucket = [[] for i in range(radix)]
        for val in sort_list:
            bucket[val%(radix**i)//(radix**(i-1))].append(val)
        del sort_list[:]
        for each in bucket:
            sort_list.extend(each)
            print(sort_list)

测试

import copy
import random
import math

sort_list = [20,1,24,54,11,26,87,45,32,544,25,87,47,48,58,1024]
global size
size = len(sort_list)

#select_sort(sort_list)
#bub_sort(sort_list)
#insert_sort(sort_list)
#shell_sort(sort_list)
#自顶向下归并排序测试
# a = merge()
# a.sort(sort_list)
# print(sort_list)
#自底向上归并排序测试
# a = merge()
# a.sort2(sort_list)
# print(sort_list)
#快速排序测试
# print(sort_list)
# a = quick()
# a.sort(sort_list)
# print(sort_list)
#基数排序测试
#radix_sort(sort_list)

Search

    Table of Contents