简单排序算法Python(只写了代码,仅供自己参考)

# -*- coding: utf-8 -*-
# 参考各位大佬的写法整理了一下,便于自己对简单排序算法的理解
# 算法不仅仅是算法,更是一种思想
import threading
import time


def bubble_sort(input_list):
    """冒泡排序"""
    length = len(input_list)
    if length < 2:
        return input_list
    for i in range(length):
        for j in range(length - 1 - i):
            if input_list[j + 1] < input_list[j]:
                input_list[j + 1], input_list[j] = input_list[j], input_list[j + 1]
    return input_list


def select_sort(input_list):
    """选择排序"""
    length = len(input_list)
    if length < 2:
        return input_list
    for i in range(length):
        minIndex = i  # 初始默认minIndex为未排序列中最小元素对应的索引
        for j in range(i + 1, length):
            if input_list[j] < input_list[minIndex]:  # 如果找到更小元素,进行交换
                input_list[j], input_list[minIndex] = input_list[minIndex], input_list[j]
    return input_list


def quick_sort1(input_list, left, right):
    """快速排序"""

    # 直接在原列表上进行操作
    def partition(input_list, left, right):
        # 主要目的是将比基准数小的放到基准数左边,比基准数大的放到基准数右边
        key = left  # 划分参考数索引,默认为第一个数为基准数,可优化
        while left < right:
            # 如果列表后边的数,比基准数大或相等,则前移一位直到有比基准数小的数出现
            while left < right and input_list[right] >= input_list[key]:
                right -= 1
            # 如果列表前边的数,比基准数小或相等,则后移一位直到有比基准数大的数出现
            while left < right and input_list[left] <= input_list[key]:
                left += 1
            # 此时已找到一个比基准大的数,和一个比基准小的数,将他们互换位置
            (input_list[left], input_list[right]) = (input_list[right], input_list[left])
        # 当从两边分别逼近,直到两个位置相等时结束,将对应位置的数同基准数进行互换
        (input_list[left], input_list[key]) = (input_list[key], input_list[left])
        # 返回目前基准数所在位置的索引
        return left

    if left >= right:  # 如果已经不能分区,开始结束递归调用(递归调用结束的判定)
        return
    # 从基准开始分区
    mid = partition(input_list, left, right)
    # 递归调用
    quick_sort1(input_list, left, mid - 1)
    quick_sort1(input_list, mid + 1, right)
    return input_list


def quick_sort2(input_list):
    """快速排序"""
    # 这个递归的快排会更好理解一些,缺点在于不是在原列表上进行操作
    if len(input_list) >= 2:
        mid = input_list[len(input_list) // 2]  # 基准数可从待排序列任选
        left, right = [], []
        input_list.remove(mid)
        for num in input_list:
            if num >= mid:
                right.append(num)
            else:
                left.append(num)
        return quick_sort2(left) + [mid] + quick_sort2(right)
    else:
        return input_list


def merge_sort(input_list):  # 分治法
    """归并排序"""

    def merge(left, right):  # left和right都是有序的
        merge_result = []
        i = j = 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                merge_result.append(left[i])
                i += 1
            else:
                merge_result.append(right[j])
                j += 1
        if i == len(left):  # 如果left已经追加完毕,将right剩下的元素直接追加到末尾
            merge_result.extend(right[j:])
        else:  # 如果right已经追加完毕,将left剩下的元素直接追加到末尾
            merge_result.extend(left[i:])
        return merge_result

    if len(input_list) < 2:
        return input_list
    middle = len(input_list) // 2
    left = merge_sort(input_list[:middle])  # 递归调用归并排序
    right = merge_sort(input_list[middle:])
    return merge(left, right)


def insert_sort(input_list):
    """插入排序"""
    length = len(input_list)
    if length < 2:
        return input_list
    for i in range(1, length):
        j = i
        target = input_list[i]  # target要进行插入的元素
        while j > 0 and target < input_list[j - 1]:  # 寻找插入位置
            input_list[j] = input_list[j - 1]
            j = j - 1
        input_list[j] = target
    return input_list


def shell_sort(input_list):
    """希尔排序"""

    def shell_insert(input_list, gap):  # gap为组内元素的索引间隔(步长),代码部分参考“插入排序”,很像
        for i in range(gap, length):  # 当gap为1的时候,就是等同于插入排序insert_sort
            j = i
            target = input_list[i]  # 记录要插入的数,因为input_list[i]的值会被覆盖,所以必须提前记录
            while j > 0 and target < input_list[j - gap]:  # 对步长为gap的同组数据进行插入排序
                # 从后往前,直到找到比target小的数的位置
                input_list[j] = input_list[j - gap]  # 向后挪动
                j -= gap  # 未找到之前,继续往前一个
            input_list[j] = target  # 将target插入目标位置

    length = len(input_list)
    if length < 2:
        return input_list
    gap = length // 2
    while gap >= 1:
        shell_insert(input_list, gap)
        gap = gap // 2
    return input_list


def heap_sort(input_list):
    """堆排序"""

    def sift_down(input_list, father, end):  # 让父节点始终比左右子节点(如果右节点存在的话)的值要大
        while True:
            # 交换之后可能造成被交换的孩子节点作为父节点不再满足堆的性质,因此需要重新对交换的孩子节点进行堆初始化
            left_child = 2 * father + 1  # 父节点左孩子节点下标
            if left_child > end:  # 孩子节点不一定还有孩子节点
                break
            # 如果节点的右孩子存在且大于左孩子时,此时,右孩子为孩子节点中的最大,交换时此右节点与父节点交换
            # 否则,父节点只有左孩子或者左孩子比右孩子要小,此时,左孩子是孩子节点中最大的,交换时此左节点与父节点交换
            if left_child + 1 <= end and input_list[left_child + 1] > input_list[left_child]:
                left_child = left_child + 1
            # 若上一个if语句执行,则以下代码是右子节点与父节点交换,否则,为左子节点与父节点交换
            if input_list[left_child] > input_list[father]:
                input_list[left_child], input_list[father] = input_list[father], input_list[left_child]
                father = left_child  # 被调换的子节点重新作为父节点进行调整操作
            else:
                break

    length = len(input_list)
    first = length // 2 - 1  # 最后一个有孩子的节点的下标
    for i in range(first, -1, -1):  # 从最后一个有子节点的节点开始往上调整最大堆
        sift_down(input_list, i, length - 1)
    for head_end in range(length - 1, 0, -1):
        input_list[head_end], input_list[0] = input_list[0], input_list[head_end]  # 头尾对调,同时堆开始变小
        sift_down(input_list, 0, head_end - 1)  # father=0, 表示交换以后从根节点开始往下调整
    return input_list


def count_sort(input_list):
    """计数排序"""
    length = len(input_list)
    if length < 2:
        return input_list
    max_num = max(input_list)
    count = [0] * (max_num + 1)
    for element in input_list:
        count[element] += 1  # 间接使用索引来存储数
    input_list.clear()
    for i in range(max_num + 1):  # 元素[0, 1, 2, ..., max_num]
        # input_list.extend([i] * count[i])  # 下面两条语句可以用这条语句替代
        for j in range(count[i]):  # count[i]表示元素i出现的次数,如果有多次,重复追加
            input_list.append(i)
    return input_list
    # 下面这种代码思想也可以达到相同的目的,只不过中间涉及到元素间的比较,与计数排序的思想稍有差异。
    # count = [0] * length
    # for i in range(length):
    #     p = 0
    #     q = 0
    #     for j in range(length):
    #         # 全表查找比input_list[i]小的元素个数p;q是为了保证相同元素重复出现时仍然能够正常排序
    #         # 这块的元素比较使得它算不上真正意义上的计数排序,不过思想倒是已经很不错。
    #         if input_list[j] < input_list[i]:
    #             p += 1
    #         elif input_list[j] == input_list[i]:
    #             q += 1
    #     # count[p] = input_list[i]
    #     for k in range(p, p + q):  # 如果没有重复元素,最终可得q=1,这里其实就可以写成 count[p] = input_list[i] 了
    #         count[k] = input_list[i]
    # return count


def bucket_sort(input_list):
    """桶排序"""
    big = max(input_list)
    num = big // 10 + 1  # 以10为等差计算桶的数目
    buckets = [[] for i in range(0, num)]  # 初始化桶
    for i in input_list:
        buckets[i // 10].append(i)  # 划分桶

    for i in buckets:  # 桶内排序(桶内快速排序)
        quick_sort1(i, 0, len(i) - 1)  # 对原列表进行修改,所以无需返回新的排序后的列表
        # bucket = quick_sort2(i)  # 返回新的列表,不是在原列表上修改
        # buckets[buckets.index(i)] = bucket  # 根据桶的划分,每个桶是不一样的,所以我这儿用index函数查找每个桶的索引位置
    output_list = []
    for i in buckets:
        output_list.extend(i)
        # for j in i:
        #     output_list.append(j)
    return output_list


def radix_sort(input_list, radix=10):
    """基数排序"""
    # import math
    # K = math.ceil(math.log(max(input_list), radix))  # 用K位数可表示任意整数
    K = 1  # 也可不用 import math 模块, 毕竟简单算法,能不用已有的库就不用
    for element in input_list:
        while True:
            if element >= radix ** K:
                K += 1
            else:
                break
    # K表示最大数的位数,比如最大数为897,则K=3
    bucket = [[] for i in range(radix)]  # 不能用 [[]]*radix (看完下面几条语句你就会明白为什么)
    # bucket = [[]] * radix  # [[], [], [], [], [], [], [], [], [], []]
    # >>> aa = [[]] * 10  # 一改具改
    # >>> bb = [[] for i in range(10)]
    # >>> aa == bb  # 尽管有
    # True
    # >>> aa[0] is aa[1]
    # True
    # >>> bb[0] is bb[1]  # 但是
    # False
    # [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    # [[50], [], [2], [3], [44, 4], [5, 15], [36, 26, 46], [47, 27], [38, 48], [19]]
    # [50, 2, 3, 44, 4, 5, 15, 36, 26, 46, 47, 27, 38, 48, 19]
    # [[2, 3, 4, 5], [15, 19], [26, 27], [36, 38], [44, 46, 47, 48], [50], [], [], [], []]
    # [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
    for i in range(K):  # K次循环
        for val in input_list:
            bucket_index = val % (radix ** (i + 1)) // (radix ** i)  # 把val放到bucket_index对应的这个桶
            # example 4789 % 10**3 // 10**2 == 7
            bucket[bucket_index].append(val)  # 析取整数第K位数字 (从低到高, 个十百千...)
        input_list.clear()
        for each in bucket:
            input_list.extend(each)  # 桶合并
        bucket = [[] for i in range(radix)]
    return input_list


def sleep_sort(input_list):  # 哈哈,调皮一下,顺便学学join和守护线程的用法,如果max(input_list)比较大的话,建议放弃尝试
    """睡眠排序"""
    output_list = []
    thread_list = []

    def do_sleep(i):
        # 统一乘了一个系数,总不能让我真的等i秒
        time.sleep(i / 100)  # 只要sleep时间不会过于短,在毫秒级别以上,应该都是可以分出先后的,再短就悬了
        output_list.append(i)

    for i in input_list:
        t = threading.Thread(target=do_sleep, args=(i,))
        thread_list.append(t)
    for t in thread_list:  # 对每个线程设置守护并开启
        t.setDaemon(True)
        t.start()
    for t in thread_list:  # join所完成的工作就是线程同步,避免主线程提前结束以后直接终止掉了子线程
        t.join()  # 即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后才继续
        # t.join(timeout=0.02)  # 主线程将会累计等待timeout*len(input_list)这么多秒,时间一到,强行终止,返回当前已排序结果
    return output_list


# 示例:(为什么每次调用我都把input_list重新赋值一遍?我乐意?非也!因为大部分排序算法是直接在input_list上进行修改)
if __name__ == '__main__':
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("冒泡排序 :", bubble_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("选择排序 :", select_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("快速排序1:", quick_sort1(input_list, 0, len(input_list) - 1))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("快速排序2:", quick_sort2(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("归并排序 :", merge_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("插入排序 :", insert_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("希尔排序 :", shell_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("  堆排序 :", heap_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("计数排序 :", count_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("  桶排序 :", bucket_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("基数排序 :", radix_sort(input_list))
    input_list = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
    print("睡眠排序 :", sleep_sort(input_list))
"D:\Program Files\Python36\python3.exe" D:/MyProject/Python/workspace/sorting_algorithm.py
冒泡排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
选择排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
快速排序1: [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
快速排序2: [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
归并排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
插入排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
希尔排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
  堆排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
计数排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
  桶排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
基数排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
睡眠排序 : [2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/TomorrowAndTuture/article/details/105512780