python【排序】冒泡、选择、插入、归并、快速排序

冒泡排序

第一遍扫描的时候,
比较第一个位置第二个位置,因为54>26,所以进行交换;
比较第二个位置第三个位置,因为54<93,所以不发生交换;
比较第三个位置第四个位置,因为93>17,所以进行交换;
以此类推,在第一遍扫描结束的时候,可以把93放在最后
在这里插入图片描述

写法一

def bubbleSort(alist):
    for passnum in range(len(alist) - 1, 0, -1):
        for i in range(passnum):
            if alist[i] > alist[i + 1]:
                alist[i], alist[i + 1] = alist[i + 1], alist[i]

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

由于alist的长度是9,所以passnum的取值范围是{8,7,6,5,4,3,2,1}
第一遍排序的时候,passnum=8i的取值范围是0~7,而if语句中判断的是alist[i]>alist[i+1], 所以是在a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]之间依次两两进行比较,最后会把最大的数排在a[8]的位置;
第二遍排序的时候,passnum=7i的取值范围是0~6,而if语句中判断的是alist[i]>alist[i+1], 所以是在a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]之间依次两两进行比较,最后会把最大的数排在a[7]的位置;
在遍历passnum的过程中,每次都会把余下列表中最大的数排在后面,最终实现排序操作。

写法二

对于上面遍历passnum的for循环,也可以写成while循环的形式

# 等同于上面的冒泡排序
def bubbleSort(alist):
    passnum = len(alist) - 1
    while passnum > 0:
        for i in range(passnum):
            if alist[i] > alist[i + 1]:
                alist[i], alist[i + 1] = alist[i + 1], alist[i]
        passnum -= 1

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

短冒泡排序

由于冒泡排序总是需要对元素之间两两进行比较,并决定是否交换,那么当列表本来就是有序时,仍然这样比较一遍,是很浪费时间资源的,于是存在一种短冒泡排序的方法。增加一个标记,如果在一遍排序中,并没有发生交换,说明列表已经有序,后面的操作就不需要了。

# 与上面的冒泡排序不同的是,增加了一个exchanges,来标记是否进行交换
def shortBubbleSort(alist):
    exchanges = True
    passnum = len(alist) - 1
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):
            if alist[i] > alist[i + 1]:
                exchanges = True
                alist[i], alist[i + 1] = alist[i + 1], alist[i]
        passnum = passnum - 1

可以看到,和冒泡排序的写法二差不多,区别在于增加了exchanges这个标记项,以及while循环的判断语句。

  1. 首先标记exchanges=True
  2. 进入while循环,再设置exchanges=False
  3. 如果列表已经有序,那么未发生元素交换,exchanges依然是False,不满足while的循环条件,所以退出循环。
  4. 如果列表不是有序的,那么exchanges=True,可以进入下一轮的while循环

参考:
Short Bubble Sort Algorithm ( youtube )
https://runestone.academy/runestone/static/pythonds/SortSearch/TheBubbleSort.html

选择排序

选择排序在冒泡排序的基础上进行了改进,每一遍扫描只需要进行一次交换。选择排序在每一次扫描的时候,都找出最大值,并将其放在合适的位置上。
第一遍扫描(alist[:]),93最大,和最后的20进行交换;
第二遍扫描(alist[:-1]),77最大,和倒数第二个位置的55交换;
第三遍扫描(alist[:-2]),55最大,和倒数第三个位置的44交换;
以此类推
在这里插入图片描述

写法一

def selectionSort(alist):
    for fillslot in range(len(alist)-1,0,-1):
        positionOfMax = 0
        # 找到最大值
        for location in range(1, fillslot + 1): 
            if alist[location] > alist[positionOfMax]:
                positionOfMax = location
        # 将最大值与最后一个值进行交换
        alist[fillslot], alist[positionOfMax] = alist[positionOfMax], alist[fillslot]
        
    
alist = [54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)

写法二

同样,也可以改成while循环。

def selectionSort(alist):
    fillslot = len(alist) - 1
    while fillslot > 0:
        positionOfMax = 0
        # 找到最大值
        for location in range(1, fillslot+1): 
            if alist[location] > alist[positionOfMax]:
                positionOfMax = location
        # 将最大值与最后一个值进行交换
        alist[fillslot], alist[positionOfMax] = alist[positionOfMax], alist[fillslot]
        fillslot -= 1
    
alist = [54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)

参考:
https://runestone.academy/runestone/static/pythonds/SortSearch/TheSelectionSort.html

插入排序

从第二个元素开始,如果比前一个元素大,就保持不动;如果比前一个元素小,就插入到它前面合适的位置。
以下面这个图为例,
在第五遍扫描的时候,要决定31的位置,在代码中先用currentvalue保存31,
因为93比31大,所以把93往后移,
又因为77比31大,所以77也往后移,
54也比31大,所以54往后移。
25比31小,所以31插入到25后面
在这里插入图片描述

def insertionSort(alist):
    for index in range(1, len(alist)):
        currentvalue = alist[index] # 保存当前位置上的值
        position = index # 记录位置
        while position > 0 and alist[position - 1] > currentvalue:
            # 如果前一个值比当前值大,就把前一个值移到当前位置上
            alist[position] = alist[position - 1]
            position = position - 1
        alist[position] = currentvalue

归并排序

代码来自:
python归并排序–递归实现

def mergesort(seq):
    """归并排序"""
    if len(seq) <= 1:
        return seq
    mid = len(seq) // 2  # 将列表分成更小的两个列表
    # 分别对左右两个列表进行处理,分别返回两个排序好的列表
    left = mergesort(seq[:mid])
    right = mergesort(seq[mid:])
    # 对排序好的两个列表合并,产生一个新的排序好的列表
    return merge(left, right)

def merge(left, right):
    """合并两个已排序好的列表,产生一个新的已排序好的列表"""
    result = []  # 新的已排序好的列表
    i = 0  # 左半部分的下标
    j = 0  # 右半部分的下标
    # 对两个列表中的元素 两两对比。
    # 将最小的元素,放到result中,并对当前列表下标加1
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    # 下面两条语句中,只有一条会执行
    result += left[i:] # 若第一个表未检测完,添加到result后面
    result += right[j:] # 若第二个表未检测完,添加到result后面
    return result

seq = [5,3,0,6,1,4]
print('排序前:',seq)
result = mergesort(seq)
print('排序后:',result)

排序前: [5, 3, 0, 6, 1, 4]
排序后: [0, 1, 3, 4, 5, 6]

代码的执行过程:
在这里插入图片描述
注意:
merge函数中,判断语句的条件只能是<=

if left[i] <= right[j]

这样能保证,归并排序是一个稳定的排序。也就是当出现两个相同的数时,它们的相对位置不因排序而改变。

快速排序

# 严蔚敏版
def quick_sort(array, l, r):
    if l < r:
        q = partition(array, l, r)
        quick_sort(array, l, q - 1)
        quick_sort(array, q + 1, r)

def partition(array, l, r):
    # 一趟排序过程
    key = array[l]
    while l < r:
        while l < r and array[r] >= key:  # 移动右指针,直到找到比key小的元素
            r -= 1
        array[l] = array[r]
        while l < r and array[l] <= key:  # 移动左指针,直到找到比key大的元素
            l += 1
        array[r] = array[l]

    array[l] = key
    return l
array = [54,26,93,17,77,31,44,55,20]
quick_sort(array, 0, len(array)-1)
print(array)

下面以array为例,具体解析代码过程.
在这里插入图片描述
这里是一遍扫描,以54作为基准值key,把比它小的都放到左边,比它大的都放到右边。最终得到54在整个数组中的位置,并把这个位置返回给q
然后以q位置为分隔线,进行左半部分的递归和右半部分的递归。
在这里插入图片描述
接下去是对44的左半部分31,26进行扫描,把31作为基准值key,会把26移到31的左边,然后,54左半部分就排完序了。54的右半部分也同理。最终会得到一个整体有序的结果。

堆排序

代码来自利用Python实现堆排序

def MAX_Heapify(heap,HeapSize,root):#在堆中做结构调整使得父节点的值大于子节点
    left = 2*root + 1
    right = left + 1
    larger = root
    if left < HeapSize and heap[larger] < heap[left]:
        larger = left #找到root和left之间的最大值x
    if right < HeapSize and heap[larger] < heap[right]:
        larger = right  # 把right和刚才的最大值x比较
    if larger != root: # 如果最大值不是root,就要进行调整
        #如果做了堆调整则larger的值等于左节点或者右节点的,这个时候做交换操作
        heap[larger],heap[root] = heap[root],heap[larger]
        MAX_Heapify(heap, HeapSize, larger) # 重新调整,退出的条件是,root>=left且root>=right

def Build_MAX_Heap(heap):#构造一个堆,将堆中所有数据重新排序
    HeapSize = len(heap)#将堆的长度单独拿出来
    for i in range((HeapSize -2)//2,-1,-1):#从后往前出数
        MAX_Heapify(heap,HeapSize,i) # 重新调整

def HeapSort(heap):
    #将根节点取出与最后一位做对调,对前面len-1个节点继续进行对调整过程。
    Build_MAX_Heap(heap) # 构建一个大根堆
    for i in range(len(heap)-1,-1,-1):  
        # i的取值范围是7-0,每次0都是最大的,然后分别和7->0等7个元素进行交换
        heap[0],heap[i] = heap[i],heap[0]
        MAX_Heapify(heap, i, 0) # 重新调整,这里的i是size,依次是7、6、5直到0,表示要重新调整的节点数
    return heap

a=[53,17,78,9,45,65,87,32]
HeapSort(a)
print(a)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值