常见几种排序理解及Python实现

冒泡排序

**思路:**每趟两两比较,确定最后一个数为最大值,依次,确定倒数第二个为第二大值……,直到所有数轮完,则发现从后到前已经有序(大–小),
从前往后看,彷如水泡从水底往水面升起过程(水泡越来越大)而由来
例子:[3,2,1]
1、第一趟的第一次:第1个位置(下标为0)会和第2个位置(下标为1)比较,也就是3会和2比,由于3比2大,故交换,变成[2,3,1]
2、第一趟第二次:第2个位置(下标为1)会和第3个位置(下标为2)比较,也就是3会和1比,由于3比1大,故交换,变成[2,1,3]
3、第二趟第一次:第1个位置(下标为0)会和第2个位置(下标为1)比较,也就是2会和1比,由于2比1大,故交换,变成[1,2,3],排序完成
**时间复杂度:*O(nn),共计需要比较n-1趟,每趟两两比较直至最后也是n-1次,故所需要比较次数为(n-1)+(n-2)+……+2+1=((n-1)+1)(n-1)/2=n(n-1)/2

def bubbleSort(lst):
    """
    冒泡排序:每趟两两比较,比较完,可以确定最后一个位置为最大值
    """
    for i in range(len(lst)-1):#排序的趟数
        #两两比较的次数
        for j in range(len(lst)-i-1):
            if lst[j]>lst[j+1]:
                lst[j],lst[j+1] = lst[j+1],lst[j]

插入排序

**思路:**抽数放到前面已经有序部分做比较,这里需要优先和前面有序的最后一个数做比较,因为如果该数大于或等于其最后一个数,则直接放最后面,时间复杂度为O(1)。依次,如果比第二大的数大,则可以放倒数第2,利用前面有序部分有序性,能在不同程度减少比较次数;但是最坏的时间复杂度还是O(n^2),即最倒霉时该数比前面有序都要小,则要每次比较N遍,共计N个数需要放置。
**例子:**如[1,2,3,……,9],抽到的数为10,则10和9比较后,可以直接放到最后,比较次数为1,同理如果不幸运抽到0,则比较需要比较9次
**时间复杂度:**O(n*n)

def insertSort(lst):
    """插入排序:每次抽出一个数字,插入到有序的数组中,从后往前依次和插入的数组逐个比较"""
    start = time.time()
    for i in range(1,len(lst)):

        temp = lst[i]#这里需要先存储每次比较的值,否则后面交换后会更新第i位置数值
        for j in range(i-1,-1,-1):#只有从后往前比较,才能最大发挥效率(因为如果相对有序,则会停止比较)
            if temp>=lst[j]:#当插入值比原先有序数组最后一个值大,则无需交换,已经是有序
                break
            else:#由于每次从后往前比较(相邻比较),当每次插入值比当前比较值小,则交换每次相邻的值
                lst[j+1], lst[j] = lst[j], temp
    t = time.time() - start
    return len(lst), t

选择排序

**思路:**每轮通过逐个比较,确定第一个位置为最小值,从第一个位置往后所有再进行比较,确定第二个值为第二小,依次确定全部
这里和冒泡排序的展示有点像,只是冒泡为相邻两个值互相比较,这里为每轮选定一个值,和所有值都比一遍后,确定最小值再更换第一个,相对更新值频次更低;
**例子:**如[3,2,1],
1、第一轮所有都比较一遍,发现1是最小的,故1和第一个位置值也就是3交换,变成[1,2,3]
2、第二轮在[2,3]里面比较哪个才是最小的,发现比较完,2已经是最小,故不需要交换,排序完成
**时间复杂度:**O(n*n)

#选择排序
def selectSort(lst):
    """
    选择排序:找到最小值后记录其下标,然后直接和第一个值换
    """
    for i in range(len(lst)-1):#排序的趟数
        min = i
        #每一趟比较的次数
        for j in range(i+1,len(lst)):
            if lst[min]>lst[j]:
                min = j
        if min!=i:
            lst[min],lst[i] = lst[i],lst[min]

快排

**思路:**每轮通过基准值,分拆成3块,比基准值小的部分,基准值,比基准值大的部分,再依次对基准值小的部分、大的部分,做类似的分拆,直到剩1个值或为空时返回(这里之所以必须要小于等于1,是因为如果以2个值为基准时,这两个是可能乱序,只剩1个或0个,则返回必然是有序的)
例子:[3,1,2,5,6,7]
1、借助递归思路,最外层递归实现为return [1,2] + [3] + [5,6,7]
2、再往里一层left_lst=[1,2]则是由[] + [1] + [2]
3、同层right_lst=[5,6,7]则是由[] + [5] + [6,7],这里的[6,7]则是由继续再往里一层递归返回
[6,7]=[] + [6] + [7]
**时间复杂度:**O(n*n),平均时间复杂度为O(nlogn)

def quickSorted(lst):

    #base
    if len(lst) <= 1:
        return lst

    pivot = lst[0]#基准值

    left_lst = quickSorted([item for item in lst[1:] if item<pivot])
    right_lst = quickSorted([item for item in lst[1:] if item>=pivot])
    return left_lst + [pivot] + right_lst

归并排序

**思路:**归并也就是分治思路,先分后治(合起来),故具体实现上,需要一个分的函数实现(这里用二分法),一个合的函数实现(双指针比较)
例子:[3,1,2,5,6,7]
1、最外层递归:mid = 3,分别递归左半部分[3,1,2]和右半部分[5,6,7],返回左右部分的合并结果,也就是[1,2,3,5,6,7]
2、左半部分往里一层递归mid=1,分别递归其的左半部分[3],其右半部分[1,2],返回其左右部分的合并结果,也就是[1,2,3],同理[5,6,7]
3、针对左半部分里面的右半部分的[1,2],继续拆分到[1],[2],再合起来为[1,2],这里要注意的是必须要拆分到长度小于等于1,这也是为什么[1,2]还要继续拆,才能保证每个返回的值为有序(单个值返回必然是有序)
**时间复杂度:**O(n*logn)

def merge(a, b):
    """对拆分部分进行合并"""
    #[1,3,8] [2,5,4]
    lst = []#存储后面合并数字
    #定义左右指针
    left = 0#指向a
    right = 0#指向b
    #对两边比较
    while right<len(b) and left<len(a):
        #从b数组比较开始
        while right<len(b) and b[right]<=a[left]:
            lst.append(b[right])
            right += 1
        #判断是right是否已经到了b数组的尾端,是则直接把a剩下的都加到数组,结束程序
        if right == len(b):
            for i in range(left,len(a)):
                lst.append(a[i])
            break
        #从a数组比较开始
        while left<len(a) and a[left]<b[right]:
            lst.append(a[left])
            left += 1
        #判断是left是否已经到了a数组的尾端,是则直接把b剩下的都加到数组,结束程序
        if left == len(a):
            for i in range(right,len(b)):
                lst.append(b[i])
            break
    return lst

def mergeSorted(lst):
    """对数组进行拆分,并调用合并函数"""
    #当拆分到小于等于1个时,直接返回
    if len(lst) <= 1:
        return lst
    mid = len(lst)//2
    a = mergeSorted(lst[:mid])
    b = mergeSorted(lst[mid:])

    return merge(a,b)

计数排序

**思路:**针对于范围有限字符列举,直接存储每个字符出现的次数,然后再按照次数从小到达遍历每个字符即可;典型的空间换时间
例子:[3,2,1,1,1,2,5,5,8]
1、求出最大值为8,最小值为1,则空间长度为8-1=7,则可以建立长度为7的容器(这里取列表,专门匹配每个字母出现的次数),一开始初始化为[0]7,表示每个字符记录次数都为0
2、遍历整个数组,如当3出现一次,则3对应的次数所在位置+1,全部遍历完,得到每个字符出现的次数,这里的记录数组为[3,2,1,0,2,0,0,1]–>[1,2,3,4,5,6,7,8]对应的次数
3、根据每个字符出现次数,从头到尾打印字符即可[1,1,1,2,2,3,5,5,8],为0的则不打印
**时间复杂度:**O(n
n)

def countSort(lst):
    """计数排序:当数字较小,如1000间隔内,效率相当快"""
    start = time.time()
    if len(lst) == 0:
        return []
    #找到最大最小值,确定范围
    lst_max = lst[0]
    lst_min = lst[0]
    for i in range(1, len(lst)):
        if lst[i] > lst_max:
            lst_max = lst[i]
        elif lst[i] < lst_min:
            lst_min = lst[i]
    nums = lst_max - lst_min + 1 #计数的范围
    counts = [0] * nums
    #对lst进行循环,判断每个数属于范围内,出现一次则+1
    for i in range(len(lst)):
        counts[lst[i]-lst_min] = counts[lst[i]-lst_min] + 1#只要数字出现一次,就加1
    #重新赋值items,根据每个数字出现的次数
    pos = 0
    for i in range(nums):
        for j in range(counts[i]):#两个for的实际是循环了lst里面的所有数,故O(n)
            lst[pos] = lst_min + i
            pos += 1

    t = time.time() - start
    return len(lst), t
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值