IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 超全的排序算法总结——Python -> 正文阅读

[数据结构与算法]超全的排序算法总结——Python

排序算法总结

算法时间复杂度空间复杂度评价稳定性
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)较差稳定
鸡尾酒排序 O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)比冒泡好,但没有多好稳定
选择排序 O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)较差不稳定
插入排序 O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)较差稳定
希尔排序 O ( n 1.5 ) O(n^{1.5}) O(n1.5) O ( 1 ) O(1) O(1)较优,适用大部分场景不稳定
归并排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n ) O(n) O(n)较优,但不适合太多数据稳定
快速排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn)较优,但太多数据会栈溢出不稳定
快速排序(非递归版) O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn)较优,不会出现栈溢出,但直接使用库里面的栈不如系统中创建的栈帧速度快不稳定
堆排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn)较优,适用于大部分场景不稳定
计数排序 O ( n + k ) O(n + k) O(n+k) O ( k ) O(k) O(k)较优,经过本人测试,在给定范围中排序,基数排序非常快的稳定
基数排序 O ( n ? k ) O(n?k) O(n?k) O ( n + k ) O(n + k) O(n+k)较优, 适用于大部分场景稳定
桶排序 O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k)中等,因为需要用户自己指定桶的个数且桶的个数直接影响排序性能稳定

一些准备工作

import numpy as np
import time

# 交换元素
def swap(array, i, j):
    array[i], array[j] = array[j], array[i]

# 创建 0~1000 的 n 个随机数
def create_random(n):
    return np.random.randint(0, 1000, n)

# 测试array是否是升序
def is_order(array, correct_array):
    length = len(array)
    for i in range(length):
        if array[i] != correct_array[i]:
            return False
    return True

# 计算排序所花费的时间
def test_fun(array,sort_fun,order_fun):
    # 排序出正确的答案
    correct_array = np.sort(array)
    # 排序结束
    before_time = time.time()
    sort_fun(array)
    after_time = time.time()
    require_time = after_time - before_time
    # 排序结束
    
    # 判断结果是否正确
    order_ret = order_fun(array, correct_array)
    # 输出结果
    time_ret = ' {0} takes {1} seconds to sorting {2} int32 numbers!!!'.format(str(sort_fun).split(' ')[1], 
                                                     require_time, len(array))
    return 'Success!!!' + time_ret if order_ret else 'Defeat!!!' + time_ret

1、冒泡排序

def Bubble_Sort(array):
    length = len(array)
    if length <= 1:
        return
    for i in range(length):
        flag = False
        for j in range(length - i - 1):
            if array[j] > array[j + 1]:
                swap(array, j, j + 1)
                flag = True
        if not flag:
            break
print(test_fun(create_random(10000), Bubble_Sort, is_order))
Success!!! Bubble_Sort takes 25.275135040283203 seconds to sorting 10000 int32 numbers!!!

2、鸡尾酒排序

def CockTail_Sort(array):
    length = len(array)
    i, j = 0, length
    while i < j:
        # 将大泡冒到最后面
        for index in range(i + 1, j):
            if array[index - 1] > array[index]:
                swap(array, index - 1, index)
        j -= 1  # 更改排序区间
        # 将小泡冒到最前面
        for index in range(j - 1, i, -1):
            if array[index - 1] > array[index]:
                swap(array, index - 1, index)
        i += 1
print(test_fun(create_random(10000), CockTail_Sort, is_order))
Success!!! CockTail_Sort takes 34.09252619743347 seconds to sorting 10000 int32 numbers!!!

3、选择排序

def Select_Sort(array):
    length = len(array)
    if length <= 1:
        return
    for i in range(length):
        min_index = i
        for j in range(i, length):
            if array[j] < array[min_index]:
                min_index = j
        swap(array, i, min_index)
print(test_fun(create_random(10000), Select_Sort, is_order))
Success!!! Select_Sort takes 12.891336917877197 seconds to sorting 10000 int32 numbers!!!

4、插入排序

def Insert_Sort(array, start=0, gap=1):
    length = len(array)
    if length <= 1:
        return

    for i in range(start + gap, length, gap):
        index = i
        tmp = array[index]
        while index > start and array[index - gap] > tmp:
            array[index] = array[index - gap]
            index -= gap
        array[index] = tmp
print(test_fun(create_random(10000), Insert_Sort, is_order))
Success!!! Insert_Sort takes 8.749930381774902 seconds to sorting 10000 int32 numbers!!!

5、希尔排序

def Shell_Sort(array):
    length = len(array)
    if length <= 1:
        return
    
    gap = length // 2  # python // 表示对最后的结果取整
    
    while(gap >= 1):
        for i in range(gap):
            Insert_Sort(array, i, gap)  # 对插入排序算法复用
        gap //= 2
print(test_fun(create_random(1000000), Shell_Sort, is_order))
Success!!! Shell_Sort takes 17.48081064224243 seconds to sorting 1000000 int32 numbers!!!

6、堆排序

建堆的时间复杂度计算(设最后形成的完全二叉树有 h h h层):

T = 1 ? 2 h ? 1 + 2 ? 2 h ? 2 + 3 ? 2 h ? 3 + . . . + ( h ? 1 ) ? 2 1 + h ? 2 0 T=1*2^{h-1}+2*2^{h-2}+3*2^{h-3}+...+(h-1)*2^1+h*2^0 T=1?2h?1+2?2h?2+3?2h?3+...+(h?1)?21+h?20

h = l o g 2 ( n ) h=log_2(n) h=log2?(n)带入上式得建堆的时间复杂度为 O ( n ) O(n) O(n)

排序的时间复杂度计算:
每一个节点最差要下沉 O ( l o g n ) O(logn) O(logn)次,故总时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
故堆排序时间复杂度为 O ( n ) + O ( n l o g n ) = O ( n l o g n ) O(n)+O(nlogn)=O(nlogn) O(n)+O(nlogn)=O(nlogn)

# parent = (child - 1) / 2
# left_child = 2 * parent + 1
# right_child = 2 * parent + 2
# 因为为了升序 故构建大根堆

# 通过 adjust_up 构建堆
def adjust_up(array):
    length = len(array)
    
    for i in range(1, length):
        index = i
        parent = int((index - 1) / 2)
        
        while array[index] > array[parent] and parent >= 0:
            swap(array, index, parent)
            index = parent
            parent = int((index - 1) / 2)
# 通过 adjust_down 将数据融进堆 
def adjust_down(array, heap_len):  # heap_len 堆的长度
    index = 0
    max_i = 2 * index + 1
    
    if max_i >= heap_len:
        return
    
    if max_i + 1 < heap_len and array[max_i] < array[max_i + 1]:  # 使得 max_i 指向两个子节点的最大值
         max_i += 1
            
    while array[index] < array[max_i]:
        swap(array, index, max_i)
        index = max_i
        max_i = 2 * index + 1
        
        if max_i >= heap_len:
            return
        
        if max_i + 1 < heap_len and array[max_i] < array[max_i + 1]:  # 使得 max_i 指向两个子节点的最大值
            max_i += 1
def Heap_Sort(array):
    length = len(array)
    
    if length <= 1:
        return
    # 将 array 构建成 heap
    adjust_up(array)
    heap_len = length
    
    while heap_len > 1:
        swap(array, 0, heap_len - 1)
        heap_len -= 1
        adjust_down(array, heap_len)
print(test_fun(create_random(1000000), Heap_Sort, is_order))
Success!!! Heap_Sort takes 27.513195753097534 seconds to sorting 1000000 int32 numbers!!!

7、快速排序

快速排序时间复杂度计算:

在分析快排的时间复杂度时,可以将函数的调用看做成一个二叉树,最优的情况是是一个完全二叉树,层数为 l o g n logn logn,最差的情况是二叉树只有一个边,这种情况下二叉树的层数变成了 n n n,这种情况只会在数组完全倒序的情况下发生,因此在进行快排之前要进行一次洗牌以打乱数组,故对于快排而言,数组越乱,性能越高。对于每一层,都会将数组的所有元素遍历一遍,因此每一层的时间复杂度为 O ( n ) O(n) O(n)。因此快排的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

快速排序空间复杂度计算:

快排用到了递归,因此主要的空间复杂度即为函数栈帧的建立,根据上面不难得出,最优情况下会创建 n l o g n nlogn nlogn个栈帧,最差情况下会创建 n 2 n^2 n2个栈帧,因此排序前对数组洗牌非常关键。故快排的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

def __Quick_Sort(array, left, right):
    base_num = array[left]
    i = left
    j = right

    while i < j:
        while i < j and array[j] >= base_num:
            j -= 1

        while i < j and array[i] <= base_num:
            i += 1

        if j != i:
            swap(array, j, i)
    
    swap(array, left, i)
    return i
    
def _Quick_Sort(array, left, right):
    if left >= right:  # 递归的出口
        return None

    mid = __Quick_Sort(array, left, right)
    
    _Quick_Sort(array, left, mid)
    _Quick_Sort(array, mid + 1, right)

def Quick_Sort(array):
    length = len(array)
    if length <= 1:
        return
    np.random.shuffle(array) # 重新打乱牌序
    _Quick_Sort(array, 0, length - 1)
print(test_fun(create_random(1000000), Quick_Sort, is_order)) 
Success!!! Quick_Sort takes 78.20122027397156 seconds to sorting 1000000 int32 numbers!!!

8、快速排序(非递归)

from queue import Queue  # 导入 python 内置 Queue 模块
def Quick_Sort_Non_Recurse(array):
    length = len(array)
    if length <= 1:
        return
    que = Queue()
    que.put(0), que.put(length - 1)
    while not que.empty():  # empty() 队列为空返回 True
        left ,right = que.get(), que.get()
        mid = __Quick_Sort(array, left, right)
        if right - left + 1 > 2:  # 只有两个以上的元素排序后需要进队列 
            if left < mid:
                que.put(left), que.put(mid)  # 左边的一对区间
            if mid + 1 < right:
                que.put(mid + 1), que.put(right)  # 右边的一对区间
print(test_fun(create_random(1000000), Quick_Sort_Non_Recurse, is_order))
Success!!! Quick_Sort_Non_Recurse takes 96.13525390625 seconds to sorting 1000000 int32 numbers!!!

9、归并排序

def Merge(array, tmp_array, low, mid, high):
    index = low
    p1, p2 = low, mid + 1
    end_p1, end_p2 = mid, high

    while(p1 <= end_p1 and p2 <= end_p2):
        if array[p1] <= array[p2]:
            tmp_array[index] = array[p1]
            p1 += 1
        else:
            tmp_array[index] = array[p2]
            p2 += 1
        index += 1
    while p1 <= end_p1:
        tmp_array[index] = array[p1]
        index += 1
        p1 += 1
    while p2 <= end_p2:
        tmp_array[index] = array[p2]
        index += 1
        p2 += 1      
    # 拷贝到原数组
    for i in range(low, high + 1):
        array[i] = tmp_array[i]
        
def _Merge_Sort(array, tmp_array, low, high):
    # 递归的出口
    if low == high:
        return
    mid = int((low + high) / 2)
    _Merge_Sort(array, tmp_array, low, mid)
    _Merge_Sort(array, tmp_array, mid + 1, high)
    # 将分开的数组合并
    Merge(array, tmp_array, low, mid, high)
    
    
def Merge_Sort(array):
    length = len(array)
    if length <= 1:
        return
    tmp_array = np.zeros(length, dtype=int)
    _Merge_Sort(array, tmp_array, 0, length - 1)
print(test_fun(create_random(1000000), Merge_Sort, is_order))
Success!!! Merge_Sort takes 14.850624799728394 seconds to sorting 1000000 int32 numbers!!!

10、基数排序

创建一个获取最大数位数的函数,这个函数的返回值决定排序的次数

排序的循环体:
创建 0~9 共十个 bucket 用于存放比较当前位时,元素的个数,但应注意每一次排序前要清空
遍历一遍原数组,将当前位元素的个数分配给各个 bucket
更新 bucket ,统计在当前第 n 个 bucket 以及前面的所有 bucket 共有多少元素,以便后续获取当前排序元素在 tmp 数组中的位置
获取当前排序的元素的对应位的数值k,然后 bucket[k]-1 即为当前元素在tmp数组中的位置,同时 bucket[k] -= 1
最后,将 tmp 数组 copy 到原数组

# 统计array中最大数的位数
def MaxBit(array):
    length = len(array)
    maxNum = array[0]
    for i in range(length):
        if array[i] > maxNum:
            maxNum = array[i]
    d = 1
    while maxNum >= 10:
        maxNum /= 10
        d += 1
    
    return d
# 获取当前数的第k位的数值
def get_k_num(num, k):
    for i in range(k):
        num = int(num / 10)
    return num % 10
# 基数排序
def Radix_Sort(array):
    length = len(array)
    if length <= 1:
        return
    # 创建一个长度为length的数组
    tmp = np.zeros(length, dtype=int)
    # 获取最大数的位数,即排序的次数
    radix = MaxBit(array)
    # 创建10个空桶
    buckets = np.zeros(10, dtype=int)
    for j in range(radix):
        # 清空桶
        buckets -= buckets
        # 统计装入桶的元素个数
        for i in range(length):
            buckets[get_k_num(array[i], j)] += 1
        # 更新bucket
        for i in range(1, 10):
            buckets[i] = buckets[i - 1] + buckets[i]
        # 将排序后的数放到tmp中
        for i in range(length):
            # 要倒着来排序
            index = length - i - 1
            tmp[buckets[get_k_num(array[index], j)] - 1] = array[index]
            buckets[get_k_num(array[index], j)] -= 1

        # 将tmp的元素copy到array中
        for i in range(length):
            array[i] = tmp[i]
print(test_fun(create_random(1000000), Radix_Sort, is_order))
Success!!! Radix_Sort takes 12.643389463424683 seconds to sorting 1000000 int32 numbers!!!

11、计数排序

在遇到范围比较小且数据量非常大的情况下,计数排序性能是不错的

# 寻找最大数
def M_Num(array):
    length = len(array)
    max_num = array[0]
    min_num = array[0]
    for i in range(length):
        if array[i] > max_num:
            max_num = array[i]
        if array[i] < min_num:
            min_num = array[i]
    return min_num, max_num
# 算法本体
def Count_Sort(array):
    length = len(array)
    if length <= 1:
        return
    tmp_array = np.array(array)
    min_num, max_num = M_Num(array)
    buckets = np.zeros(max_num - min_num + 1, dtype=int)
    for i in range(length):
        buckets[tmp_array[i] - min_num] += 1
    for i in range(1, len(buckets)):
        buckets[i] += buckets[i - 1]
    for i in range(length - 1, -1, -1):
        array[buckets[tmp_array[i] - min_num] - 1] = tmp_array[i]
        buckets[tmp_array[i] - min_num] -= 1
print(test_fun(create_random(1000000), Count_Sort, is_order))
Success!!! Count_Sort takes 8.175300121307373 seconds to sorting 1000000 int32 numbers!!!

12、桶排序

def M_Num(array):
    length = len(array)
    max_num = array[0]
    min_num = array[0]
    for i in range(length):
        if array[i] > max_num:
            max_num = array[i]
        if array[i] < min_num:
            min_num = array[i]
    return min_num, max_num

def Bucket_Sort(array, bucket_num=10):
    length = len(array)
    if length <= 1:
        return
    min_num, max_num = M_Num(array)
    bucket_size = int((max_num - min_num) / bucket_num) + 1
    buckets = []
    for i in range(bucket_num):
        buckets.append([])
    for i in range(length):
        bucket_index = int((array[i] - min_num) / bucket_size)
        buckets[bucket_index].append(array[i])

    for i in range(bucket_num):
        Quick_Sort(buckets[i])

    index = 0
    for i in range(bucket_num):
        #bucket_len = len(buckets[i])
        for j in buckets[i]:
            array[index] = j
            index += 1
print(test_fun(create_random(1000000), Bucket_Sort, is_order))
Success!!! Bucket_Sort takes 44.678382873535156 seconds to sorting 1000000 int32 numbers!!!

猴子排序

作者写这个排序纯粹是为了搞笑的,哈哈哈哈

def is_in_order(array):
    length = len(array)
    for i in range(1, length):
        if array[i - 1] > array[i]:
            return False
    return True

def Monkey_Sort(array):
    while not is_in_order(array):
        np.random.shuffle(array)
print(test_fun(create_random(10), Monkey_Sort, is_order))
Success!!! Monkey_Sort takes 21.361676454544067 seconds to sorting 10 int32 numbers!!!
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 21:19:25  更:2022-09-24 21:23:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 18:12:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码