十大排序算法(下)--Python

一、计数排序

1、概述

是通过统计元素的数量来实现排序的,通常用于整数数组

2、原理

  • 遍历数组,找出待排序的数组中最大和最小的值
  • 统计数组中每个元素的出现次数,存到数组A中,元素大小为下标
  • 对数组做前缀和操作(每次加上前面一项)
  • 再填充目标数组,按照下标顺序和出现次数来

3、代码

# 计数排序
def Counting_Sort(nums):
    Max_Num = max(nums)
    Counter = [0] * (Max_Num + 1)  # 记录每个元素出现的次数
    for i in nums:
        Counter[i] += 1
    for j in range(Max_Num):       # 计算前缀和
        Counter[j + 1] += Counter[j]
    Lenth = len(nums)
    res = [0] * Lenth
    for q in range(Lenth - 1, -1, -1): # 倒叙遍历num,将元素填入结果数组res
        num = nums[q]
        res[Counter[num] - 1] = num # 将值放到对应的索引处
        Counter[num] -= 1
    for p in range(Lenth): # 使用res数组覆盖原数组
        nums[p] = res[p]


if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    Counting_Sort(tmp)
    print(tmp)
    
结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

4、特征

时间复杂度:O(n + m)

空间复杂度:O(n + m)

稳定排序

适用于数据量大但是数据范围小的情况

二、 堆排序

1、概述

输入数组创建大顶堆,此时最大的元素再堆顶,再不断的进行出堆操作,依次记录数据,即可得到从小到大排列的序列。

2、原理

  • 将初始待排列的序列构建成大顶堆,创建出初始的无序区

  • 将堆顶的元素和最后一个元素交换,就会得到一个新的无序区,和新的有序区,并且此时无序区域内的元素都小于有序区的元素

  • 交换完的新堆顶可能会违反堆的性质,所以需要对无序区重新进行构建,构建完成后再重复上述动作,直到排序完成

3、代码

# 堆排序
def Max_Heapify(nums, Lenth, i):  # 堆的长度为Lenth,从节点i开始,建堆操作
    while True:
        Left = 2 * i + 1
        Right = 2 * i + 2
        tmp = i
        if Left < Lenth and nums[Left] > nums[tmp]:
            tmp = Left
        if Right < Lenth and nums[Right] > nums[tmp]:
            tmp = Right
        # 如果节点i最大或者索引l,r越界,则说明不用继续堆化直接跳出即可
        if tmp == i:
            break
        # 交换节点
        nums[i], nums[tmp] = nums[tmp], nums[i]
        # 继续向下堆化
        i = tmp

def Heap_Sort(nums):
    Lenth = len(nums)
    # 建立堆
    for i in range(Lenth // 2 - 1, -1, -1):
        Max_Heapify(nums, Lenth, i)
    # 从堆中提取最大的元素,一直循环
    for i in  range(Lenth - 1, 0, -1):
        # 交换根节点和最右叶节点
        nums[0], nums[i] = nums[i], nums[0]
        #重新堆化
        Max_Heapify(nums, i, 0)

if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    Heap_Sort(tmp)
    print(tmp)
    
结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

4、特征

时间复杂度:O(nlogn)

空间复杂度:O(1)

非稳定排序

三、 希尔排序

1、概述

在插入排序上做了改进,先将待排序序列分割成若干子序列分别进行插入序列,等到整个序列基本有序时,再整体做插入排序。

2、原理

  • 选择一个增量序列t1,t2,…,tk,其中ti > tj, tk = 1

  • 按增量序列个数K,对序列进行K趟排序

  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对各个子表直接进行插入排序,仅增量因子为1时,整个序列作为一个表来处理,表长即序列长

3、代码

def Insert_Sort(arr, start, gap):  # 插入排序
    Lenth = len(arr)
    # 外循环是已经排序的区间
    for i in range(start + gap, Lenth, gap):
        Value = arr[i]
        Position = i
        # 内循环将值插入到已经排序区间的正确位置
        while Position >= gap and arr[Position - gap] > Value:
            arr[Position] = arr[Position - gap]
            Position = Position - gap
        arr[Position] = Value

def Shell_Sort(arr):
    count = len(arr) // 2 # 切割子序列的步长
    while count > 0:      # 持续切割直到不能切割为止
        # 遍历每个子序列
        for i in range(count):
            Insert_Sort(arr, i, count)
        count //= 2       # 改变步长,继续切割



if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    tmp_1 = [0.44, 0.45, 0.21, 0.11, 0.32, 0.95, 0.31]
    Shell_Sort(tmp)
    print(tmp)

4、特征

时间复杂度:O(n logn)

空间复杂度:O(1)

不稳定排序

四、 桶排序

1、概述

本质是计数排序的升级版本,假设输入的数据均匀分布,将数据分到有限个桶内,再在每个桶内进行排序,那么如果想要排序更加高效,在额外内存足够的情况下需要尽可能的增大桶的数量。

2、原理

  • 设定一个定量的数组作为空桶

  • 遍历数组,将数据放到对应的桶中

  • 对每个不是空的桶进行排序

  • 从每个非空桶中取出数据进行拼接,得到完整的排序数据

3、代码

# 排序数组为浮点数时
def Bucket_Sort(nums):
    # 初始化tmp = n/2个桶子,平均每个桶分配两个元素
    Lenth = len(nums)
    tmp = Lenth // 2
    bucksts = [[] for _ in range(tmp)]
    # 将数组分配到每个桶中
    for num in nums:
        i = int(num * tmp) # 因为是浮点型,所以需要对区间进行放大
        # 将num添加进桶i
        bucksts[i].append(num)
    # 对各个桶执行排序
    for buckst in bucksts:
        buckst.sort()
    # 遍历桶合并结果
    i = 0
    for buckst in bucksts:
        for num in buckst:
            nums[i] = num
            i += 1


if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    tmp_1 = [0.44, 0.45, 0.21, 0.11, 0.32, 0.95, 0.31]
    Bucket_Sort(tmp_1)
    print(tmp_1)
    
结果:[0.11, 0.21, 0.31, 0.32, 0.44, 0.45, 0.95]

上述代码是针对于浮点数的,所以需要对区间进行放大,当数据是int类型时一般不使用缩放

def Bucket_Sort(nums):
    # 初始化tmp = (max - min)/len + 1个桶子
    Lenth = len(nums)
    tmp = (max(nums) - min(nums)) // Lenth + 1
    bucksts = [[] for _ in range(tmp)]
    # 将数组分配到每个桶中
    for num in nums:
        i = (num - min(nums)) // Lenth 
        # 将num添加进桶i
        bucksts[i].append(num)
    # 对各个桶执行排序
    for buckst in bucksts:
        buckst.sort()
    # 遍历桶合并结果
    i = 0
    for buckst in bucksts:
        for num in buckst:
            nums[i] = num
            i += 1


if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    tmp_1 = [0.44, 0.45, 0.21, 0.11, 0.32, 0.95, 0.31]
    Bucket_Sort(tmp)
    print(tmp)
    
结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

上述两种各自适用于不同场景,在实际使用过程中,需要对区间进行合理话安排,该算法的优势才能最大体现。

4、特征

时间复杂度:O(n + k) # k是指桶数量

空间复杂度:O(n + k)

桶排序是否稳定,取决于内部依赖的排序是否稳定

五、 基数排序

1、概述

类似于计数排序,通过统计个数来时间排序,在此基础上利用数字之间的递进关系,依次对每位数据进行排序,最终得到排序结果。

2、原理

  • 获得数组中最大的数,获取其位数

  • arr为原始数组,从最低位开始取每个位组成arr_tmp数组

  • 对arr_tmp进行计数排序

3、代码

def Digit(nums, exp):# 获取元素nums的位置
    return (nums // exp) % 10

def Digit_Counting_Sort(nums, exp):
    # 十进制范围为0-9,所以创建空间为10的数组
    Counter = [0] * 10
    Lenth = len(nums)
    # 统计0-9每个数字出现的次数
    for i in range(Lenth):
        d = Digit(nums[i], exp)
        Counter[d] += 1
    # 求前缀和,将出现个数转化为数组索引
    for i in range(1, 10):
        Counter[i] += Counter[i - 1]
    res = [0] * Lenth
    # 倒序遍历,根据桶内统计结果,将各元素填入res
    for i in range(Lenth - 1, -1, -1):
        d = Digit(nums[i], exp)
        j = Counter[d] - 1# 获取d在数组中的索引
        res[j] = nums[i] # 将当前元素填入索引
        Counter[d] -= 1  # 索引d数量-1
    # 覆盖原始数组
    for i in range(Lenth):
        nums[i] = res[i]

def Radix_Sort(nums):
    Max_Nums = max(nums)
    # 按照从低到高的顺利遍历
    exp = 1
    while exp <= Max_Nums:
        # 对数组的第k位做计数排序
        Digit_Counting_Sort(nums, exp)
        exp *= 10



if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    tmp_1 = [0.44, 0.45, 0.21, 0.11, 0.32, 0.95, 0.31]
    Radix_Sort(tmp)
    print(tmp)
    
结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

4、特征

时间复杂度:O(nk)

空间复杂度:O(n + d)

是否稳定要取决于其中的计数排序

适用于数字范围较大的情况,但是前提是数据必须可以表示为固定位数的格式,且位数不能过大

六、总结

排序算法平均时间复杂度空间复杂度排序方式是否稳定
计数排序O(n + m)O(n + m)外部排序稳定
堆排序O(nlogn)O(1)内部排序非稳定
希尔排序O(nlogn)O(1)内部排序非稳定
桶排序O(n + k)O(n + k)外部排序取决于内部排序方式
基数排序O(nk)O(n + d)外部排序取决于内部排序方式
  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 中的十大排序算法包括冒泡排序、选择排序、快速排序、归并排序、堆排序、插入排序、希尔排序、计数排序、桶排序和基数排序。 冒泡排序通过比较相邻的元素并交换它们的位置来排序。选择排序通过选择最小的元素并将其放在已排序的序列的末尾来排序。快速排序使用分治法将列表分成较小的子列表,并通过递归地排序子列表来完成排序。归并排序将列表分成较小的子列表,并通过递归地排序子列表,然后将它们合并在一起来完成排序。堆排序使用堆数据结构来排序元素。插入排序通过将未排序的元素插入已排序的序列中来排序。希尔排序是插入排序的一种变体,通过将元素分成较小的子序列并分别进行插入排序来排序。计数排序通过计算每个元素的出现次数,然后按照计数的顺序重构列表来排序。桶排序将元素分配到不同的桶中,然后对每个桶进行排序,最后将桶中的元素合并在一起来完成排序。基数排序根据元素的位数进行排序,从最低位开始依次排序,最终得到有序列表。以上是 Python 中的十大排序算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [用Python实现十大经典排序算法](https://blog.csdn.net/lemonbit/article/details/121738385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值