Python 排序总结

前言: 刚学完排序,快期末考试了,总结一下吧,加深一下印象,如果有不正确的地方请大家不吝赐教, 也可以和大家讨论一下各种算法的优化。
各种排序算法就不仔细的分类了。
注: 算法都是基于生成升序序列。

插入排序(普通插入排序及二分版)

原理:将序列分为有序序列无序序列两部分,初始时有序序列为一(一个元素就是有序的),然后取无序序列第一个值,在有序序列中找出属于他的位置,将该值插入该位置即可,重复若干次。
复杂度:O(n^2)

1、普通插入排序:

def InsertSort( lst ):
    for i in range(1, len(lst)):
        while i > 0 and lst[i] < lst[i-1]: # 循环判断属于lst[i]的位置,并交换
            lst[i], lst[i-1] = lst[i-1], lst[i]
            i -= 1

2、二分查找插入排序:
二分查找的目标位置:寻找刚好小于等于lst[position]的值右边的第一个位置
(与普通插入排序相比,二分查找插入排序并没有很实质性的改进,仅仅引入了二分查找)

def BinarySearch(lst, left, right, position): # 寻找无序序列第一个元素在有序序列中的对应位置
    while left <= right:  # 二分查找目标位置
    # 左右边界相同但仍要进行判断,如有序序列长度为一时
        mid = (left + right)//2
        if lst[mid] <= lst[position]:  # 寻找刚好小于等于lst[position]的值右边的第一个位置
            left = mid + 1
        else:
            right = mid - 1 
    '''mid-1的作用:防止陷入死循环,如有序序列长度为1时,lst[0] = 1, 无序序列第一个元素lst[1] = 0时,
        如果不mid-1, 就会陷入死循环。
    '''
    return left  # left的位置就是目标位置

def BinarySearchInsertSort(lst):  # 二分查找插入排序
    for i in range(1, len(lst)):
        pos = BinarySearch(lst, 0, i-1, i)
        for j in range(i, pos, -1):  # 将无序部分第一个元素移动到有序部分对应位置
            lst[j], lst[j-1] = lst[j-1], lst[j]

冒泡排序(逐步优化)

原理:像冒泡一样从头到尾,一遍又一遍的将逆序的交换顺序,重复若干次。
复杂度:O(n^2)

def BubbleSort(lst):  # 普通冒泡
    for i in range(len(lst)):
        for j in range(0, len(lst)-1):
            if lst[j] > lst[j+1]:
                lst[j], lst[j+1] = lst[j+1], lst[j]

def BubbleSort1(lst):  # 记录有序部分
    for i in range(len(lst)-1, -1, -1):  # 每一轮冒泡都能至少能排好一个位置
        for j in range(0, i):  # 只遍历无序部分
            if lst[j] > lst[j+1]:
                lst[j], lst[j+1] = lst[j+1], lst[j]

def BubbleSort2(lst):  # 增加逆序因子, 判断一趟排序是否存在逆序,若不存在,则序列已有序
    for i in range(len(lst)-1, -1,-1):
        flag = 1  # 逆序因子
        for j in range(0, i):
            if lst[j] > lst[j+1]:
                flag = 0
                lst[j], lst[j+1] = lst[j+1], lst[j]
        if flag:  # 不存在逆序对,跳出循环
            break

选择排序

原理:将整个序列分为有序序列的无序序列,初始时有序序列长度为0,然后通过遍历无序序列,找到最小值,然后与无序序列第一个元素交换,更新有序序列,重复若干次。
复杂度:O(n^2)

def SelectSort(lst):
    for i in range(len(lst)-1):  # i是有序序列的长度,且当有序序列的末端位置为len(lst)-2时,最后一个元素就是有序的,所以遍历只需len(lst)-1次
        minn = float('inf')  # 初始时将最小值设置成无穷大
        pos = i  # 记录最小值的位置
        for j in range(i, len(lst)):  # 遍历无序序列
            if minn > lst[j]:  # 每轮遍历都选择无序序列最小值
                minn = lst[j]
                pos = j
        lst[i], lst[pos] = lst[pos], lst[i]  # 交换位置

快速排序

原理:通过选择一个轴心元素,将序列中的元素小与轴心元素的放在轴心元素左边,大于则放在右边。然后以轴心元素最终位置将序列分成两个部分,重复若干次,直到子序列为一个元素。
复杂度:平均复杂度:O(nlogn) 最坏复杂度:O(n^2)

def QuickSort(lst, left, right):
    if right <= left:  # 完成标志
        return
    i = left
    j = right
    pivot = lst[i]  # 选择基准值
    '''对于基准值的选取,最好选择左边界的元素,因为基准值的位置是由i或j来代表的,
    后面将元素交换在基准值时是将基准值元素位置看为空位置,交换元素时是将元素覆盖基准值位置,
    然后基准值位置变为交换位置的位置,左右重复操作,直到i>=j;
    如果选择中间元素基准值的位置就无法由i或j来代表,算法就不成立了
    如果要选择中间的元素,则需要交换到左边界,即可保证算法的成立
    注:选左边界为基准值时,需从右边界开始判断
    选取右边界为基准值时,需从左边界开始判断
    原因:认为基准值位置为空位置,需从空位值的另一边开始找目标元素,找到后交换位置
    再从空位置的另一侧寻找元素,如果从空位置侧开始,会丢失对空位置的标记,也不符合这种算法的原理'''
    while i < j:  # 交换元素位置,基准值左边都小于基准值,右边都大于基准值
        while i < j and pivot < lst[j]:
            j -= 1
        if i < j:
            lst[i] = lst[j]
            i += 1
        while i < j and lst[i] < pivot:
            i += 1
        if i < j:
            lst[j] = lst[i]
            j -= 1
    lst[i] = pivot
    QuickSort(lst, left, i-1) # 递归进入左右序列
    QuickSort(lst, i+1, right)

归并排序

原理:首先先对无序序列进行不断的二分,直到每个部分只有一个元素,然后对序列进行两两合并,合并时对序列进行排序,这样不断合并最总将获得有序序列。
代码分为两部分,Merge为对序列的合并,MergeSort为主函数,利用递归对序列进行划分。
复杂度:O(nlogn)

def Merge(left, right):  # 对序列进行合并
    l, r = 0, 0
    result = []
    while l < len(left) and r < len(right):  # 逐个比较加入result
        if left[l] < right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += left[l:]  # 将剩下长度的序列加入result
    result += right[r:]
    return result

def MergeSort(lst):
    if len(lst) <= 1:
        return lst
    mid = len(lst)//2
    left = MergeSort(lst[:mid])  # 利用递归和切片对序列进行分割
    right = MergeSort(lst[mid:])
    return Merge(left, right)

其他排序(Shell插入排序……)

Shell插入排序:取分组因子,把序列分成k组,对k组内部进行插入排序,分组因子逐渐变小直到一。
复杂度:平均复杂度:O(nlogn)
最坏复杂度:O(n^2)

'''取分组因子,把序列分成k组,对k组内部进行插入排序,分组因子逐渐变小直到一'''
def ShellSort(lst):
    k = (len(lst))//2  # 不需要(len(lst)+1)//2,这样会多出一个单元素
    while k: # k为分组因子
        for i in range(k+1):  # 对k组分别进行插入排序
            for j in range(i+k, len(lst), k):  # 插入排序
                while j > i and lst[j] < lst[j-k]:
                    lst[j], lst[j-k] = lst[j-k], lst[j]
                    j -= k
        k = k//2
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是丝豆呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值