基本排序算法的python代码

本文参考b站视频: 清华大学博士讲解Python数据结构与算法(完整版)全套100节

地址:https://www.bilibili.com/video/BV1uA411N7c5

冒泡排序

# 冒泡排序,升序
def bubble_sort(li):
    flag = False  # 一次冒泡没有交换说明有序,直接输出
    for i in range(len(li)-1):
        for j in range(0,len(li)-1-i):  # 有序区放最后
            if li[j] > li[j+1]:         # 每次将最大的数字放在最后
                li[j], li[j+1] = li[j+1], li[j]
                flag = True
        if not flag:
            return li
    return li

选择排序

# 选择排序,升序
def select_sort(li):
    for i in range(0,len(li)):
        max_index = 0  # 每次都需要单独设为无序区的序号,不然如果交换后的序号成为了最大,就会在下一次跳出无序区
        for j in range(0,len(li)-i):  # 有序区在最后
            if li[j] > li[max_index]:  # 无序区选择最大
                max_index = j
        li[j],li[max_index] = li[max_index],li[j]
    return li

插入排序

# 插入排序,升序
def insert_sort(li):
    for i in range(0,len(li)-1):  # 有序部分放在最前面
        j = i
        while li[j+1] < li[j] and j >= 0:
            li[j],li[j+1] = li[j+1],li[j]
            j -= 1
    return li

快速排序

# 快速排序辅助函数,排一次(相当于二分一次)数据交换情况
def partition(li,left,right):
    tmp = li[left]
    start = left
    while left < right:
        while tmp <= li[right] and left < right:
            right -= 1
        while tmp >= li[left] and left < right:
            left += 1
        li[left],li[right] = li[right],li[left]  # 直接交换左边大于tmp和右边小于tmp的数据,因为反正最后都只剩一个空位
    li[left],li[start] = li[start],li[left]
    return left  # right也可以,需要记录下分割的下标


# 快速排序,升序,递归
def quick_sort(li,left,right):
    if left < right:  
        mid = partition(li,left,right)
        quick_sort(li,left,mid-1)  # 用li作为输入的列表,是因为li是全局变量,所以每次修改li都会改变
        quick_sort(li,mid+1,right)  # 每次迭代用left和right表示下一次分割的下标位置
    return li

堆排序

# 调整堆顶元素,其他部分都是最大堆,需要输入调整的第一个下标和最后一个下标
# 堆顶元素不一定是最大元素,堆其他部分满足最大堆
def sift(li,first,last):
    left = 2 * first + 1
    right = 2 * first + 2
    while right <= last:
        if li[first] >= li[right] and li[first] >= li[left]:  # 父节点最大,则直接退出
            break
        else:
            if li[left] > li[right]:  # 左节点最大
                li[first],li[left] = li[left],li[first]
                first = left
            else:  # 右节点最大
                li[first], li[right] = li[right], li[first]
                first = right
        left = 2 * first + 1  # 下一层
        right = 2 * first + 2


# 堆排序,升序,最大堆
def heap_sort(li):
    # 建立堆,从下到上(最底层到顶层)
    for i in range((len(li)-2)//2,-1,-1):
        sift(li,i,len(li)-1)
    # 排序,将最后一个元素替换到顶元素,顶元素已经排好序了
    for i in range(len(li)-1,0,-1):
        li[0],li[i] = li[i],li[0]
        sift(li,0,i-1)
    return li

归并排序

# 一个列表,前部分顺序排好,后部分顺序排好,将两部分归并
def merge(li,first,mid,last):
    tmp_i = first
    tmp_j = last
    tmp_list = []  # first,mid,last都是下标
    first2 = mid+1
    while first <= mid and first2 <= last:
        if li[first] < li[first2]:
            tmp_list.append(li[first])
            first += 1
        else:
            tmp_list.append(li[first2])
            first2 += 1
    if first <= mid:  # 判断+循环,可以直接用while
        for i in range(first,mid+1):
            tmp_list.append(li[first])
            first += 1
    elif first2 <= last:
        for i in range(first2,last+1):
            tmp_list.append(li[first2])
            first2 += 1
    li[tmp_i:tmp_j+1] = tmp_list  # 将合并得到的列表送入li中,不然最后的结果没有变化了
    return tmp_list


# 归并排序,升序
def merge_sort(li,first,last):  # 因为需要递归(像树一样),所以写入另外两个参数
    if first < last:  # 用递归都没有写过循环
        # 分割
        mid = (first + last) // 2
        merge_sort(li,first,mid)
        merge_sort(li,mid+1,last)
        # 排序
        merge(li,first,mid,last)
    return li

计数排序

# 计数排序,升序,从0开始的整数,需要先算出最大值,无需比较
def count_sort(li):
    max_num = max(li)
    count_list = [0 for i in range(max_num+1)]  # 需要额外开一个空间
    for i in li:
        count_list[i] += 1
    li = []
    for index,val in enumerate(count_list):  # index为排序数的大小,val为数量
        for i in range(val):
            li.append(index)
    return li

希尔排序

# 希尔排序,升序
# 为什么最开始分组少,最后分组多啊?
# 分组多是为了一次前进一大步,相比于插入排序
def shell_sort(li):
    gap = len(li)//2
    while gap >= 1:  # 为啥需要增量为1的排序啊,那不就是冒泡了吗?这个时候改动的数据很少,已经趋于排好了
        for i in range(gap,len(li)):  # 用冒泡排序作为间隔gap的排序,也可以插入排或选择排
            for j in range(len(li)-1,i-1,-1):
                if li[j-gap] > li[j]:
                    li[j],li[j-1] = li[j-1],li[j]
        gap = gap//2  # 每次gap减小,趋于有序
    return li

桶排序

# 桶排序,升序
def bucket_sort(li):
    bucket_num = 10  # 桶的数量
    max_num = max(li)
    buckets = [[] for _ in range(bucket_num)]  # 10个桶
    bucket_len = max_num//bucket_num+1  # 每个桶的长度,可能不一样,但没有影响;+1是因为取整会漏掉一些数
    for i in li:
        bucket_ind = i//bucket_len
        buckets[bucket_ind].append(i)
    li = []
    for i in buckets:  # 对每一个桶排序
        for j in range(0,len(i)):  # 这里是选择排序,也可以直接调用其他排序方法
            for loop in range(j,len(i)):
                if i[j] > i[loop]:  # 取无序区的最大值,交换,但这样不稳定
                    i[j],i[loop] = i[loop],i[j]
        li.extend(i)
    return li

基数排序

# 基数排序,升序,无需比较
def radix_sort(li):
    max_num = max(li)
    bucket_num = 10  # 0~9一共10个数字
    it = 1
    while max_num >= 1:
        buckets = [[] for _ in range(bucket_num)]  # 每一轮都需要重新定义buckets
        for i in li:  # 所有元素进入桶
            bucket_ind = i%(10**it)//(10**(it-1))  # 取对应位置上的数,比如个位,十位,百位,越先取的优先级越低
            buckets[bucket_ind].append(i)
        li = []
        for i in buckets:  # 将数全部取出放入li中
            li.extend(i)
        it += 1
        max_num //= 10  # 标志,当max_num为0时全部位数已经用过了
    return li

你可能感兴趣的:(排序算法,算法,数据结构)