python 排序算法集合

1、冒泡排序

核心算法
  - 排序算法,一般都实现为就地排序,输出为升序
  - 扩大有序区,减小无序区。
  - 每一趟比较中,将无序区中所有元素一次进行两两比较,升序排序将大的数调整至两数中的右侧
  - 每一趟比较完成,都会把这一趟的最大数推到当前无序区的最右侧

代码实现:

    nums = [9,8,1,2,3,4,5,6,7]
    print(nums)
    print('-----------------')
    length = len(nums)       #读取列表的长度
    #i  排序的趟数
    count = 0
    swap_count = 0           #两个count只是为了进行计数

    for i in range(length-1):          #实际是9个数,排序的趟数是8,所以-1;再因为range后不包,所以这里实际上-2
        for j in range(length-1 -i):   #无序区中排序的次数,正好是i,8次,8-8正好可得j索引为0,然后依次进行排序
            count += 1
            if nums[j] > nums[j+1]:    #j len-2;j+1 len-1 ;如果nums索引的值 > nums索引位置+1的值,则执行以下操作
                temp = nums[j]         #引入了一个中间变量temp来存储nums当前索引的值
                nums[j] = nums[j+1]
                nums[j+1] = temp       #如果nums索引的值>nums索引位置+1的值,那么到这一步,大小数值,交换完成。
                swap_count += 1
        print(nums)                    #在j循环之外,打印每一趟完成的当前排序情况
    print('------------------')
    print(nums)       #最终排序完成的情况
    print(count,swap_count)

    打印的结果是:
    [1, 2, 3, 4, 5, 6, 7, 8, 9]  #完成了排序
    36 15  #排序的次数是36次,交换的次数是15次,但是发现没有,其实只要在9和8完成排序之后,nums列表的顺序已经是期望的升序排序,但是该程序依旧一次次进行不必要的排序,效率低下;因此,可以对以上代码进行优化,如下:
    nums = [9,8,1,2,3,4,5,6,7]
    print(nums)
    print('-----------------')
    length = len(nums)
    count = 0
    swap_count = 0

    for i in range(length-1):
        flag = False   ###打标记,假设这一趟没有交换过
        for j in range(length-1 - i):
            count += 1
            if nums[j] > nums[j+1]:
                temp = nums[j]
                nums[j] = nums[j+1]
                nums[j+1] = temp
                swap_count += 1
                flag = True   ###当执行到这一步,flag置为True
        print(nums)
        if not flag:
            break            ###当flag置为True,而不是False时,表明已经没有交换,则break
    print('--------------++++-')
    print(nums)
    print(count,swap_count)



总结:
冒泡法需要数据一趟趟比较
可以设定一个标记判断此轮是否有数据交换发生,若没有,可结束排序,如果发生交换,继续下一轮排序
最差的排序情况是,初始顺序与目标顺序完全相反,遍历次数为n(n-1)/2
最好的排序情况是,初始顺序与目标顺序完全相同,遍历次数为n-1
时间复杂度是O(n^2)

2、简单选择排序

核心算法:
结果可为升序或降序排列,默认升序排列
扩大有序区,减小无序区。
以降序为例;相邻元素依次两两比较,获得每一次比较后的最大值,并记住此值的索引
每一趟都从无序区中选择出最大值,然后交换到当前无序区的最左端

m_list=[
    [1,9,8,5,6,7,4,3,2],
    [1,2,3,4,5,6,7,8,9],
    [9,8,7,6,5,4,3,2,1]
]
nums = m_list[0]
length = len(nums)
print(nums)
count_iter=0
count_swap=0

for i in range(length-1):
    maxindex = i
    for j in range(i+1,length):
        count_iter += 1
        if nums[maxindex]<nums[j]:
            maxindex=j    
    if maxindex != i:
        nums[maxindex],nums[i]=nums[i],nums[maxindex]
        count_swap += 1
print(nums)
print(count_iter,count_swap)

--->
[1, 9, 8, 5, 6, 7, 4, 3, 2]
[9, 8, 7, 6, 5, 4, 3, 2, 1]
36 7

当然还可以使用二元选择排序的方法:
同时选择出每一趟的最大值和最小值,并分别固定到两端的有序区
可以减少迭代的趟数

总结:
简单选择排序需要数据一趟趟比较,并在每一趟中发现极值
没有办法知道当前这一趟是否已经达到排序要求,但是可以知道极值是否在目标索引位置上
遍历次数1...n-1之和n(n-1)/2
时间复杂度O(n^2)
减少了交换次数,提高了效率,性能略好于冒泡法

3、简单插入排序

核心算法:
    结果可为升序或降序排列,默认升序排列;以升序为例
    扩大有序区、减小无序区;图中绿色部分就是增大的有序区,黑色就是减小的无序区
    增加一个哨兵位,图中最左端红色数字,其中放置每一趟待比较数值
    将哨兵位数值与有序区数值从右往左依次比较,找到哨兵位数值合适的插入位置

算法实现:
增加哨兵位
    为了方便,采用列表头部索引0位置插入哨兵位
    每一次从有序区最右端的下一个数,即无序区最左端的数放到哨兵位
比较与挪动
    从有序区最右端开始,从右至左依次与哨兵位比较
    比较数比哨兵大,则右移一位,换下一个左边的比较数
    直到找不到大于哨兵的比较数,这时把哨兵插入到这个数右侧的空位即可

m_list=[1,9,8,5,6]
nums=[0] + m_list
print(nums[1:])
length=len(nums)
count_move=0

for i in range(2,length):    #测试的值从nums的索引2开始向后直到最后一个元素
    nums[0] = nums[i]        #索引0位哨兵、索引1位假设的有序区,都跳过
    j = i - 1         #i左边的那个数就是有序区末尾
    if nums[j] > nums[0]:    #如果最右侧数大于哨兵才需要挪动和插入
        while nums[j] > nums[0]:
            nums[j+1] = nums[j]  #右移,不是交换
            j -= 1    #继续向左
            count_move += 1
        nums[j+1] = nums[0]  #循环中多减了一次

print(nums[1:])
print(count_move)
--->
[1, 9, 8, 5, 6]
[1, 5, 6, 8, 9]
5

总结:
最好情况,正好是升序排列,比较迭代n-1次
最差情况,正好是降序排列,比较迭代n(n-1)/2次,数据移动非常多
使用两层嵌套循环,时间复杂度O(n^2)
是稳定排序算法:
    如果待排序序列R中两元素相等,即Ri等于Rj,且i<j,那么排序后这个先后顺序不变,这种排序算法就称为稳定排序。
一般使用在小数据规模比较
优化:如果比较操作耗时大,可以使用二分查找提高效率,即二分查找插入排序

4、堆排序

import math

origin = [None,30,20,80,40,50,10,60,70,90] #None为补位,设计列表索引从1开始使用数据
total = len(origin) - 1  #初始待排序个数,即n

# print_tree函数可以不实现,只是为了打印观察方便
def print_tree(array, unit_width=2):
    length = len(array)
    depth = math.ceil(math.log2(length))
    start = 1
    spaces = ' ' * unit_width
    for i in range(depth-1, -1 ,-1):
        pre = 2 ** i -1
        print(pre * spaces, end='')
        end = start + start
        line = array[start:end]
        interval = (2 * pre + 1) * spaces
        print(interval.join(map(lambda x:'{:{}}'.format(x, unit_width),line)))
        start = end

def heap_adjust(i,n,array:list):
    '''
    调整当前结点(核心算法);调整的结点的起点在n//2,保证所有调整的结点都有孩子结点
    :param i: 当前结点的下标
    :param n: 待比较个数
    :param array: 待排序数据
    '''
    while 2 * i <= n:   #一定有左孩子结点
        lchild_index = 2 * i   #2i为左孩子,2i+1为右孩子
        max_child_index = lchild_index  #假设孩子结点中左孩子目前最大
        if lchild_index + 1 <= n and array[lchild_index + 1] > array[lchild_index]: #一定有右孩子结点,并且大于左孩子结点
            max_child_index = lchild_index + 1    #则右孩子结点最大

        if array[max_child_index] > array[i]:    #和自己子树的根结点比较
            array[max_child_index],array[i] = array[i],array[max_child_index]  #最大子结点和它的根结点进行数据交换
            i = max_child_index  #子树的根结点就换成最大子结点
        else:   #否则,说明目前子树根结点是最大的,不用调整,直接结束
            break
        #这一步作完后,可以打印出如下
        #               30
        #       20              80
        #   90      50      10      60
        # 70  40

def max_heap(n, array:list):    #构建大顶堆
    for i in range(n//2, 0, -1):  #range右不包,所有到0
        heap_adjust(i, n, array)
    return array
    #max_heap(total,origin),已经构成了大顶堆
    #               90
    #       70              80
    #   40      50      10      60
    # 20  30

def heap_sort(n, array):
    while n > 1:  #n的个数要大于1,保证至少有两个待排序数
        array[1], array[n] = array[n], array[1] #堆顶和最后一个结点交换
        n -= 1    #排序的n的个数每次减少一个
        if n == 2 and array[2] >= array[1]:  #优化;当n只有2个了并且子结点大于它的根结点则结束
            break
        heap_adjust(1, n, array)  #调整之后,获得新的大顶堆
    return array

max_heap(total,origin)
print_tree(heap_sort(total,origin))
print(origin[1:])

#最后执行如下,完成排序
#               10
#       20              30
#   40      50      60      70
# 80  90
# [10, 20, 30, 40, 50, 60, 70, 80, 90]

--------
总结:
    是利用堆性质的一种选择排序,在堆顶选出最大值或最小值
时间复杂度:
    堆排序的时间复杂度为O(nlogn)
    由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为O(nlogn)
空间复杂度:
    只是使用了一个交换的空间,空间复杂度就是O(1)
是不稳定的排序算法

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这里给出一个示例,假设有一个包含学生姓名、课程和成绩的列表,我们可以使用 Python 的内置排序函数 `sorted()` 来按照不同的条件进行排序。 ```python students = [ {"name": "Alice", "course": "Math", "score": 87}, {"name": "Bob", "course": "English", "score": 92}, {"name": "Charlie", "course": "History", "score": 78}, {"name": "David", "course": "Math", "score": 90}, {"name": "Eva", "course": "English", "score": 85}, {"name": "Frank", "course": "History", "score": 81}, ] # 按照课程升序排列 sorted_students = sorted(students, key=lambda x: x["course"]) print(sorted_students) # 按照成绩降序排列 sorted_students = sorted(students, key=lambda x: x["score"], reverse=True) print(sorted_students) # 按照课程和成绩升序排列 sorted_students = sorted(students, key=lambda x: (x["course"], x["score"])) print(sorted_students) ``` 输出结果分别为: ``` [{'name': 'Alice', 'course': 'Math', 'score': 87}, {'name': 'Bob', 'course': 'English', 'score': 92}, {'name': 'Eva', 'course': 'English', 'score': 85}, {'name': 'Charlie', 'course': 'History', 'score': 78}, {'name': 'Frank', 'course': 'History', 'score': 81}, {'name': 'David', 'course': 'Math', 'score': 90}] [{'name': 'Bob', 'course': 'English', 'score': 92}, {'name': 'David', 'course': 'Math', 'score': 90}, {'name': 'Alice', 'course': 'Math', 'score': 87}, {'name': 'Eva', 'course': 'English', 'score': 85}, {'name': 'Frank', 'course': 'History', 'score': 81}, {'name': 'Charlie', 'course': 'History', 'score': 78}] [{'name': 'Bob', 'course': 'English', 'score': 92}, {'name': 'Eva', 'course': 'English', 'score': 85}, {'name': 'Alice', 'course': 'Math', 'score': 87}, {'name': 'David', 'course': 'Math', 'score': 90}, {'name': 'Charlie', 'course': 'History', 'score': 78}, {'name': 'Frank', 'course': 'History', 'score': 81}] ``` 以上代码中,`key` 参数指定了排序的依据,可以使用 lambda 函数来指定排序的规则。在第一个例子中,我们按照课程名称进行升序排列;在第二个例子中,我们按照成绩进行降序排列;在第三个例子中,我们先按照课程名称进行升序排列,如果课程名称相同,则按照成绩进行升序排列

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值