【数据结构与算法】:十大经典排序算法

news2024/11/23 8:48:35

在这里插入图片描述


文章目录

  • 前言
  • 一、冒泡排序(Bubble Sort)
    • 1.1 冒泡排序原理
    • 1.2 冒泡排序代码
    • 1.3 输出结果
  • 二、选择排序(Selection Sort)
    • 2.1 选择排序原理
    • 2.2 选择排序代码
    • 2.3 输出结果
  • 三、插入排序(Insertion Sort)
    • 3.1 插入排序原理
    • 3.2 插入排序代码
    • 3.3 输出结果
  • 四、希尔排序
    • 4.1 希尔排序原理
    • 4.2 希尔排序代码
    • 4.3 输出结果
  • 五、快速排序(Quick Sort)
    • 5.1 快速排序原理
      • 5.1.1 分治算法
        • 什么是分治算法?
        • 分治法的基本步骤
    • 5.2 快速排序代码
    • 5.3 输出结果
  • 六、归并排序(Merge Sort)
    • 6.1 归并排序原理
    • 6.2 归并排序代码
    • 6.3 输出结果
  • 七、堆排序(Heap Sort)
    • 7.1 堆的概念
    • 7.2 堆排序
    • 7.3 堆排序原理(+)
      • 7.3.1 构建二叉堆
      • 7.3.2 循环删除当前大根堆的堆顶
    • 7.4 堆排序代码
    • 7.5 输出结果
  • 八、计数排序(Counting Sort)
    • 8.1 计数排序原理
    • 8.2 计数排序代码
    • 8.3 输出结果
  • 九、桶排序(Bucket Sort)
    • 9.1 桶排序原理
    • 9.2 桶排序代码
    • 9.3 输出结果
  • 十、 基数排序(Radix Sort)
    • 10.1 基数排序原理
    • 10.2 基数排序实例


前言

    排序算法是计算机科学的核心基石,本文带您领略十大经典排序算法的精髓。文章以思维导图为线索,系统介绍两类主要排序方法:非线性时间比较类排序与线性时间非比较类排序。前者涵盖冒泡、快速、归并等经典算法,通过元素比较实现排序;后者如计数、桶、基数排序,则利用数据特性或额外空间实现高效排序。通过图文示例与详尽代码,可以更直观的理解算法思想与实现细节。
在这里插入图片描述


一、冒泡排序(Bubble Sort)

1.1 冒泡排序原理

相邻元素,比较大小,如果顺序不符合规则,则交换位置。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

1.2 冒泡排序代码

# 冒泡排序
alist = [3,2,5,9,10,1,6,8,2,4]
ln = len(alist)
for i in range(0,ln-1):
    for j in range(0,ln-1-i):
        if alist[j] > alist[j+1]:
            alist[j],alist[j+1] = alist[j+1],alist[j]
        print(alist)
    print(f"第{i+1}轮:",alist)
print("-------完毕-------")
print(alist)

PS:每一轮排序确定一个最终位置

1.3 输出结果

>>> 输出结果:
[2, 3, 5, 9, 10, 1, 6, 8, 2, 4]
[2, 3, 5, 9, 10, 1, 6, 8, 2, 4]
[2, 3, 5, 9, 10, 1, 6, 8, 2, 4]
[2, 3, 5, 9, 10, 1, 6, 8, 2, 4]
[2, 3, 5, 9, 1, 10, 6, 8, 2, 4]
[2, 3, 5, 9, 1, 6, 10, 8, 2, 4]
[2, 3, 5, 9, 1, 6, 8, 10, 2, 4]
[2, 3, 5, 9, 1, 6, 8, 2, 10, 4]
[2, 3, 5, 9, 1, 6, 8, 2, 4, 10]1轮: [2, 3, 5, 9, 1, 6, 8, 2, 4, 10]
[2, 3, 5, 9, 1, 6, 8, 2, 4, 10]
[2, 3, 5, 9, 1, 6, 8, 2, 4, 10]
[2, 3, 5, 9, 1, 6, 8, 2, 4, 10]
[2, 3, 5, 1, 9, 6, 8, 2, 4, 10]
[2, 3, 5, 1, 6, 9, 8, 2, 4, 10]
[2, 3, 5, 1, 6, 8, 9, 2, 4, 10]
[2, 3, 5, 1, 6, 8, 2, 9, 4, 10]
[2, 3, 5, 1, 6, 8, 2, 4, 9, 10]2轮: [2, 3, 5, 1, 6, 8, 2, 4, 9, 10]
[2, 3, 5, 1, 6, 8, 2, 4, 9, 10]
[2, 3, 5, 1, 6, 8, 2, 4, 9, 10]
[2, 3, 1, 5, 6, 8, 2, 4, 9, 10]
[2, 3, 1, 5, 6, 8, 2, 4, 9, 10]
[2, 3, 1, 5, 6, 8, 2, 4, 9, 10]
[2, 3, 1, 5, 6, 2, 8, 4, 9, 10]
[2, 3, 1, 5, 6, 2, 4, 8, 9, 10]3轮: [2, 3, 1, 5, 6, 2, 4, 8, 9, 10]
[2, 3, 1, 5, 6, 2, 4, 8, 9, 10]
[2, 1, 3, 5, 6, 2, 4, 8, 9, 10]
[2, 1, 3, 5, 6, 2, 4, 8, 9, 10]
[2, 1, 3, 5, 6, 2, 4, 8, 9, 10]
[2, 1, 3, 5, 2, 6, 4, 8, 9, 10]
[2, 1, 3, 5, 2, 4, 6, 8, 9, 10]4轮: [2, 1, 3, 5, 2, 4, 6, 8, 9, 10]
[1, 2, 3, 5, 2, 4, 6, 8, 9, 10]
[1, 2, 3, 5, 2, 4, 6, 8, 9, 10]
[1, 2, 3, 5, 2, 4, 6, 8, 9, 10]
[1, 2, 3, 2, 5, 4, 6, 8, 9, 10]
[1, 2, 3, 2, 4, 5, 6, 8, 9, 10]5轮: [1, 2, 3, 2, 4, 5, 6, 8, 9, 10]
[1, 2, 3, 2, 4, 5, 6, 8, 9, 10]
[1, 2, 3, 2, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]6轮: [1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]7轮: [1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]8轮: [1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]9轮: [1, 2, 2, 3, 4, 5, 6, 8, 9, 10]
-------完毕-------
[1, 2, 2, 3, 4, 5, 6, 8, 9, 10]

二、选择排序(Selection Sort)

2.1 选择排序原理

    在未排序的序列中,找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中,继续寻找最小元素,放到已经排序序列的末尾。以此类推,直到所有元素均排序完毕。

序列最小的数字放到index=0的位置,未排序的序列越来越短,长度为0时,排序完毕。

2.2 选择排序代码

A = [12,22,15,1,8]
def selectSort(list):
    for i in range(len(list)-1):
        min = i
        for j in range(i+1, len(list)):
            if list[j]<list[min]:
                min = j
        list[min],list[i] = list[i], list[min]
        print(f"第{i+1}轮排序结果:{list}")
    return list

print(selectSort(A))

PS:每一轮排序确定一个最终位置

2.3 输出结果

>>> 输出结果:
第1轮排序结果:[1, 22, 15, 12, 8]2轮排序结果:[1, 8, 15, 12, 22]3轮排序结果:[1, 8, 12, 15, 22]4轮排序结果:[1, 8, 12, 15, 22]
[1, 8, 12, 15, 22]


三、插入排序(Insertion Sort)

3.1 插入排序原理

构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

实现逻辑
① 从第一个元素开始,该元素可以认为已经被排序
② 取出下一个元素,在已经排序的元素序列中从后向前扫描
③如果该元素(已排序)大于新元素,将该元素移到下一位置
④ 重复步骤③,直到找到已排序的元素小于或者等于新元素的位置
⑤将新元素插入到该位置后
⑥ 重复步骤②~⑤

3.2 插入排序代码

def insertion_sort(arr):
    # 第一层for表示循环插入的遍数
    for i in range(1, len(arr)):
        # 当前需要插入的元素
        current = arr[i]
        # 与当前元素比较的比较元素
        pre_index = i - 1
        while pre_index >= 0 and arr[pre_index] > current:
            # 当比较元素大于当前元素则把比较元素后移
            arr[pre_index + 1] = arr[pre_index]
            # 往前选择下一个比较元素
            pre_index -= 1
        # 当比较元素小于当前元素,则将当前元素插入在 其后面
        arr[pre_index + 1] = current
        print(f"第{i}次插入后的结果为: {arr}")
    return arr

A = [11, 99, 33 , 69, 77, 88, 55, 11, 33, 36,39, 66, 44, 22]
B = insertion_sort(A)
print(B)

PS:每一轮排序后的元素位置与最终元素位置肯可能不同

3.3 输出结果

在这里插入图片描述


四、希尔排序

4.1 希尔排序原理

    希尔排序(Shell’s Sort) 是插入排序的一种,又称“缩小增量排序”(Diminishing Increment Sort,是直接插入排序算法的一种更高效的改进版本。它与插入排序的不同之处在于,它会优先比较距离较远的元素, 该方法因D.L.Shell于1959年提出而得名。

希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

4.2 希尔排序代码

def shell_sort(arr):
    # 取整计算增量(间隔)值 (每次缩小一半)  
    gap = len(arr) // 2
    while gap > 0:
        # 从增量值开始遍历比较
        for i in range(gap, len(arr)):
            j = i
            current = arr[i]
            # 元素与他同列的前面的每个元素比较,如果比前面的小则互换,其实,这就是插入排序
            while j - gap >= 0 and current < arr[j - gap]:
                arr[j] = arr[j - gap]
                j -= gap
            arr[j] = current
        # 缩小增量(间隔)值
        gap //= 2
        print(f"第i轮排序结果:{arr}")
    return arr

print("待排序序列:[3,1,8,9,2,6,0,8]")
sorted_arr = shell_sort([3,1,8,9,2,6,0,8])
print(sorted_arr)

PS:每一轮排序不能确定一个最终位置
三趟排序使用的gap(间隔)值,从远处到近处,依次比较

  • ① gap=4
  • ② gap=2
  • ③ gap=1

4.3 输出结果

待排序序列:[3,1,8,9,2,6,0,8]
第i轮排序结果:[2, 1, 0, 8, 3, 6, 8, 9]
第i轮排序结果:[0, 1, 2, 6, 3, 8, 8, 9]
第i轮排序结果:[0, 1, 2, 3, 6, 8, 8, 9]
[0, 1, 2, 3, 6, 8, 8, 9]

在这里插入图片描述


五、快速排序(Quick Sort)

5.1 快速排序原理

快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-Conquer Method)

快速排序的基本原理是:

  1. 先从数列中取出一个数作为基准数。(可以是第一个、最后一个、中间的或者干脆随机的)
  2. 分区过程,将比这个基准数大的数全放到它的右边,小于的数全放到它的左边。
  3. 再对左右区间重复第二步,直到各区间只有一个数。

5.1.1 分治算法

什么是分治算法?

    字面解释是“分而治之”,即把一个复杂的问题,分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,如此下去,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

分治法的基本步骤
  1. 分解
    将原问题分为若干个规模较小,相互独立,与原问题形式相同的子问题。
  2. 解决
    如果子问题规模较小且容易背解决,则直接解决;否则递归地解各个子问题
  3. 合并
    将各个子问题的解合并为原问题的解。

5.2 快速排序代码

import random
class SortUtil:
    def quickSort(self,alist):
        if not alist:
            return alist

        pivot = random.choice(alist)
        print(f"枢轴元素:{pivot}")
        equalList=[]
        smallerList=[]
        biggerList=[]

        for item in alist:
            if item < pivot:
                smallerList.append(item)
            elif item == pivot:
                equalList.append(item)
            else:
                biggerList.append(item)
        print(f"比枢轴元素大的:{biggerList}",f"\t与枢轴元素相等的:{equalList}",f"\t比枢轴元素小的:{smallerList}")
        return self.quickSort(biggerList)+equalList+self.quickSort(smallerList)

# 待排序列表
a = [3,1,8,9,2,6,0]
su = SortUtil()
blist = su.quickSort(a)
# 排序之后输出
print(blist)


PS:每次都能确定一个最终位置

5.3 输出结果

```cpp 枢轴元素:1 比枢轴元素大的:[3, 8, 9, 2, 6] 与枢轴元素相等的:[1] 比枢轴元素小的:[0] 枢轴元素:9 比枢轴元素大的:[] 与枢轴元素相等的:[9] 比枢轴元素小的:[3, 8, 2, 6] 枢轴元素:6 比枢轴元素大的:[8] 与枢轴元素相等的:[6] 比枢轴元素小的:[3, 2] 枢轴元素:8 比枢轴元素大的:[] 与枢轴元素相等的:[8] 比枢轴元素小的:[] 枢轴元素:2 比枢轴元素大的:[3] 与枢轴元素相等的:[2] 比枢轴元素小的:[] 枢轴元素:3 比枢轴元素大的:[] 与枢轴元素相等的:[3] 比枢轴元素小的:[] 枢轴元素:0 比枢轴元素大的:[] 与枢轴元素相等的:[0] 比枢轴元素小的:[] [9, 8, 6, 3, 2, 1, 0] ```

六、归并排序(Merge Sort)

6.1 归并排序原理

归并排序采用分治法,思想是:先递归拆分数组为两半,再合并数组。

  1. 拆分数组
    假设数组一共有 n 个元素,我们递归对数组进行折半拆分即 n//2 ,直到每组只有一个元素为止。
  2. 合并数组
    算法会从最小数组开始有序合并,这样合并出来的数组一直是有序的,所以合并两个有序数组是归并算法的核心。

6.2 归并排序代码

def merge_sort(lst):
    #递归结束条件
    if len(lst) <= 1:
        return lst

    #分解问题,并递归调用
    middle = len(lst)//2
    left = merge_sort(lst[:middle])
    right = merge_sort(lst[middle:])
    
    #合并左右半部分,完成排序
    merged_lst = []
    while left and right:
        if left[0] <= right[0]: #升序排列时,"="可以保证稳定性
            merged_lst.append(left.pop(0))
        else:
            merged_lst.append(right.pop(0))

    #如果左部分或右部分还有剩余,那就拼接到已排好序的列表中
    merged_lst.extend(right if right else left)
    return merged_lst

A = [11, 99, 33, 69, 77, 88, 55, 11, 33, 36, 39, 66, 44, 22]
B = merge_sort(A)
print("待排序:",A)
print("已排序:",B)

6.3 输出结果

在这里插入图片描述
在这里插入图片描述

待排序: [11, 99, 33, 69, 77, 88, 55, 11, 33, 36, 39, 66, 44, 22]
已排序: [11, 11, 22, 33, 33, 36, 39, 44, 55, 66, 69, 77, 88, 99]

七、堆排序(Heap Sort)

7.1 堆的概念

  • 如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1Ki<=K2i+2 ,则称为(小)堆。
  • 堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树(逻辑层面上)的数组对象(物理层面上)。

7.2 堆排序

堆排序,顾名思义需要用到 堆 ,更具体的说是用到二叉堆

二叉堆是一种特殊的完全二叉树,可分为以下两种:

  • 大根堆:根节点的值最大,任何一个父节点的值,都大于等于它左右孩子节点的值。
  • 小根堆:根节点的值最小,任何一个父节点的值,都小于等于它左右孩子节点的值。

python 中的二叉堆存储在列表中,依靠列表的下标来定位各个节点。

7.3 堆排序原理(+)

7.3.1 构建二叉堆

8,3,5,1,9,6,构建大根堆

步骤:

  1. 先得到无序完全二叉树
  2. 从最后一个非叶子节点调整节点

7.3.2 循环删除当前大根堆的堆顶

输出(pop)堆顶元素后,将堆的最后一个元素与堆顶元素交换,此时堆的性质被破坏,需要向下进行筛选。

7.4 堆排序代码

# 堆排序
from heapq import heappush,heappop

def heapsort(iterable):
    #排好序的存入这个列表里
    h = []
    #构建二叉堆
    for value in iterable:
        heappush(h, value)
    #删除堆顶后,自我调整(堆的最后一个元素放到堆顶,下沉--),再返回列表
    return [heappop(h) for i in range(len(h))]

lst = [8,3,5,1,9,6]
lst2 = heapsort(lst)
print(lst2)

7.5 输出结果

在这里插入图片描述
在这里插入图片描述

待排序队列:[8, 3, 5, 1, 9, 6]
输出:[1, 3, 5, 6, 8, 9]

八、计数排序(Counting Sort)

8.1 计数排序原理

    计数排序是一种线性时间复杂度(O(N))的非比较排序算法,它的基本思想是,对需要排序的数组中的元素进行统计,并构建一个辅助数组来保存每个元素在原始数组中出现的次数。然后,根据这些次数,将元素按照从小到大的顺序重新排列并返回结果。

统计序列中元素个数的排序,不是基于比较。适用于纯数字。

具体实现步骤如下:

  1. 统计需要排序的数据Array中的元素A[i]的出现次数。并将这些出现次数存储在另外一个长度为K的
    数组C中,其中K=max{A[i]}+1。
  2. 对所有的计数累加(从C[0]开始,每次与前面的和相加),得到新的C数组。
  3. 反向填充目标数组B:遍历A找到每个元素x,通过取出C[x]-1作为B的一个下标索引的方法来确定该
    元素放置的正确位置,再将C[x]-1的值减1。
  4. 返回排好序的目标数组B。

8.2 计数排序代码

def counting_sort(array):
    largest = max(array); smallest = min(array) # 获取最大,最小值
    counter = [0 for i in range(largest-smallest+1)] # 用于统计个数的空数组
    idx = 0 # 桶内索引值
    for i in range(len(array)):
        counter[array[i]-smallest] += 1 # 统计每个元素出现的次数
    for j in range(len(counter)):
        while counter[j] > 0:
            array[idx] = j + smallest # 取出元素
            idx += 1
            counter[j] -= 1
    return array

A = [5,2,8,9,1,2]
counting_sort(A)
print(A)

8.3 输出结果

待排序队列:[5, 2, 8, 9, 1, 2]
输出队列:[1, 2, 2, 5, 8, 9]

优势
    计数排序的优势在于它不需要进行任何的比较操作,而是通过把输入的数字映射到统计数组中来实现排序。因此,在处理大量相同重复数字的场景时,计数排序能够表现出良好的性能,甚至快于任何O(N*logN)时间复杂度排序算法。
    同时,计数排序也具有稳定性、简单易于实现等优点。因此,在一些特殊场合下(例如对心电图信号进行分析、统计等),计数排序表现出了不错的效果。


九、桶排序(Bucket Sort)

9.1 桶排序原理

    桶排序是一种常用的线性时间复杂度(O(N))的排序算法,其基本思想是将需要排序的数据按照一定范围划分成若干个桶,每个桶内进行排序,最后按顺序将各个桶中的数据合并起来。 实际上,桶排序是对计数排序的升级版,它更适用于数据范围比较大的情况。

具体步骤如下:

  1. 找出待排序的数组中最大最小的元素。
  2. 根据桶的数量,确定每个桶代表的数值范围,并且在这个范围内建立一个桶。
  3. 遍历原始数据,根据数据所处的区间,将数据分别放入对应的桶中
  4. 对每个桶内的数据分别进行排序(可以使用任意排序算法),例如快速排序、插入排序等。
  5. 将所有的桶中的数据依次连接起来,得到最终的排序结果。

9.2 桶排序代码

def bucket_sort(arr):
    n = len(arr)
    if n <= 1:
        return arr

    max_val, min_val = max(arr), min(arr) # 找出最大和最小值
    bucket_size = (max_val - min_val) // n + 1 # 确定桶的大小
    bucket_num = (max_val - min_val) // bucket_size + 1 # 确定桶的数量
    bucket = [[] for _ in range(bucket_num)] # 初始化桶

    print("bucket_size:",bucket_size)
    print("bucket_num:",bucket_num)
    print("bucket:",bucket)

    # 将数据分配到各个桶内
    for i in range(n):
        index = (arr[i] - min_val) // bucket_size
        bucket[index].append(arr[i])
    print("bucket-将数据分配到各个桶内:",bucket)
    # 对每个桶中的数据进行排序,可选任意算法
    for i in range(bucket_num):
        if len(bucket[i]) > 0:
            bucket[i].sort()

    # 将排序后的数据依次连接起来
    res = []
    for i in range(bucket_num):
        if len(bucket[i]) > 0:
            res.extend(bucket[i])
        print(f"第{i}次连接:{res}")
    return res

lst = [1,7,9,5,1,2,8,4,2,6,10,8]
new_lst = bucket_sort(lst)
print(new_lst)
# output :[1, 1, 2, 2, 4, 5, 6, 7, 8, 8, 9, 10]

9.3 输出结果

bucket_size: 1
bucket_num: 10
bucket: [[], [], [], [], [], [], [], [], [], []]
bucket-将数据分配到各个桶内: [[1, 1], [2, 2], [], [4], [5], [6], [7], [8, 8], [9], [10]]0次连接:[1, 1]1次连接:[1, 1, 2, 2]2次连接:[1, 1, 2, 2]3次连接:[1, 1, 2, 2, 4]4次连接:[1, 1, 2, 2, 4, 5]5次连接:[1, 1, 2, 2, 4, 5, 6]6次连接:[1, 1, 2, 2, 4, 5, 6, 7]7次连接:[1, 1, 2, 2, 4, 5, 6, 7, 8, 8]8次连接:[1, 1, 2, 2, 4, 5, 6, 7, 8, 8, 9]9次连接:[1, 1, 2, 2, 4, 5, 6, 7, 8, 8, 9, 10]
输出结果:[1, 1, 2, 2, 4, 5, 6, 7, 8, 8, 9, 10]

注意,在使用桶排序时,需要保证元素之间有一定的均匀分布。如果元素之间过于集中,则可能导致某些桶内元素过多,从而影响性能。


十、 基数排序(Radix Sort)

10.1 基数排序原理

    基数排序是一种很特别的排序方法,它不基于比较和移动进行排序,而基于关键字各位的大小排序。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。

    基数排序的基本思想是: 将整数按位数切割成不同的数字,然后按每个位数进行比较。由于整数也可以表示字符串(如数字的字符表示),基数排序也可以扩展到非整数的排序,比如字符串的排序。

10.2 基数排序实例

实例讲解
假设我们有以下一组数字需要排序:[170, 45, 75, 90, 802, 24, 2, 66]。

第一步:确定最大数的位数
在这个例子中,最大数是802,有三位数。
第二步:从最低位开始,依次进行排序

  1. 按个位排序:
    • 使用计数排序(或其他稳定的排序算法)按个位数对数组进行排序。
    • 排序后数组变为:[2, 24, 45, 66, 75, 90, 170, 802](这里只展示了排序的结果,没有展示计数排序的过程)。
  2. 按十位排序:
    • 注意,在按十位排序时,如果两个数的十位相同,则它们的相对位置应该和之前按个位排序时保持一致,以保证排序的稳定性。
    • 使用计数排序或其他稳定的排序算法,但这次我们按十位数来分组和计数。
    • 排序后数组可能变为:[2, 24, 45, 66, 75, 802, 90, 170](注意,这里的排序可能需要根据实际的计数排序实现来调整,因为90和170的十位都是9和1,但在最终排序中它们的位置是基于百位和之前的排序结果的)。
  3. 按百位排序(如果有的话):
    • 同样使用计数排序,但按百位来分组和计数。
    • 排序后数组最终为:[2, 24, 45, 66, 75, 90, 170, 802]。

    基数排序通过“分配”和“收集”两个过程来实现排序,其中分配过程将元素分配到不同的桶中,收集过程则将桶中的元素合并回原数组。在实际应用中,计数排序经常被用作基数排序中的分配和收集过程。

def counting_sort_for_radix(arr, exp):
    """
    用于基数排序中的计数排序子程序,根据当前位exp进行排序
    :param arr: 输入的数组
    :param exp: 当前考虑的位数(1表示个位,10表示十位,以此类推)
    """
    n = len(arr)

    # 输出数组,用于存放排序后的结果
    output = [0] * n

    # 计数数组,用于存放每个元素的出现次数
    count = [0] * 10

    # 存储arr中每个元素在当前位exp的值
    for i in range(n):
        index = arr[i] // exp
        count[(index % 10)] += 1

        # 更改count数组,现在它包含实际位置信息
    for i in range(1, 10):
        count[i] += count[i - 1]

        # 构建输出数组
    i = n - 1
    while i >= 0:
        index = arr[i] // exp
        output[count[(index % 10)] - 1] = arr[i]
        count[(index % 10)] -= 1
        i -= 1

        # 将排序后的数组拷贝回原数组,以便下一轮排序
    for i in range(len(arr)):
        arr[i] = output[i]


def radix_sort(arr):
    """
    基数排序函数
    :param arr: 输入的数组
    """
    # 找到最大数以确定最大位数
    max_val = max(arr)

    # 对每个位数进行计数排序
    exp = 1
    while max_val // exp > 0:
        counting_sort_for_radix(arr, exp)
        exp *= 10

    # 测试基数排序


if __name__ == "__main__":
    arr = [170, 45, 75, 90, 802, 24, 2, 66]
    radix_sort(arr)
    print("排序后的数组:", arr)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2097035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

可重入VI,VI模板和动态VI之间的差异 转

可重入VI 当您想要同时运行同一VI的多个实例时&#xff0c;将使用可重入VI。当VI不可重入时&#xff0c;VI只有一个数据空间。因此&#xff0c;一次只能有一个调用者运行VI&#xff0c;因此调用者可能必须“等待轮到它”时才能使用VI。这是VI的默认选项&#xff0c;但您可以将V…

精通大模型:八本必读书籍,一篇搞定所有知识点!

如果你想深入了解大模型领域&#xff0c;无论是为了学术研究还是实际应用&#xff0c;选择合适的书籍是非常重要的。以下是精选的八本大模型相关书籍&#xff0c;涵盖了从基础理论到高级实践的内容&#xff0c;可以帮助你构建全面的知识体系。 《大模型应用开发极简入门》 简介…

Hadoop之HDFS的原理和常用命令及API(java)

1、简介 书接上回&#xff0c;上篇博文中介绍如何安装Hadoop和基本配置&#xff0c;本文介绍Hadoop中分布式文件组件--HDFS&#xff0c;在HDFS中&#xff0c;有namenode、datanode、secondnamenode这三个角色&#xff0c;本文将详细介绍这几个组件是如何进行协作的&#xff0c;…

PHP一键预约便捷高效咨询小程序系统源码

一键预约&#xff0c;开启便捷高效咨询新时代 —— 你的专属咨询小程序 &#x1f680; 【开篇&#xff1a;告别繁琐&#xff0c;拥抱便捷新体验】 在这个快节奏的时代&#xff0c;时间就是金钱&#xff0c;效率就是生命。你还在为寻找咨询服务而四处奔波&#xff0c;或是为了预…

社区团购小程序系统源码+界面diy+分销+团长+供应商+拼团+菜谱+秒杀+预售+配送,开启社区营销新模式

社区团购小程序类似美团优选,兴盛优选平台.是一款针对小区居民开发的在线购物平台&#xff0c;旨在为用户提供便捷、实惠的购物体验&#xff1b;同时还提供了“限时抢购”和“优惠券营销”等多种实惠的购物体验&#xff0c;是小区居民们的不二之选。 一、 特点与优势 社区化运…

SprinBoot+Vue健康管管理微信小程序的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue3.6 uniapp代码 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平…

k8s - Volume 简介和HostPath的使用

K8S 的持久化 K8S 实现持久化存储的方法有很多种 例如 卷 (Volume), 持久卷(PV), 临时卷(EV) 等&#xff0c; 还有很多不常用的选项上图没有列出来 其中Volume 本身也分很多种 包括 Secret, configMap(之前的文章covered了)&#xff0c; hostPath, emptyDir等 本文主要focus…

[工具使用]ellisys

工具打开&#xff1a; 1.连接ellisys电源&#xff0c;ellisys Computer接口USB连接电脑&#xff0c;Logic接口与板子出信号的GPIO口连接 工具配置 1.点击"Configure" 2.在打开的Recording options中选择Wireless选项卡 2.选择Wired选项卡​ i.勾选Logic transit…

十五、多线程(基础)

文章目录 一、线程介绍1.1 程序1.2 进程1.3 线程 二、线程使用2.1 创建线程的两种方式2.2 继承Thread类创建线程2.3 实现Runnable接口创建线程2.4 多线程执行2.5 继承Thread vs 实现 Runnable的区别2.6 线程终止 三、线程方法3.1 常用方法3.2 用户线程和守护线程 四、线程生命周…

MindSearch CPU-only 版部署

准备 创建环境 申请获取硅基流动 API Key 测试 hugging face 上传 /workspaces/codespaces-blank git clone https://huggingface.co/spaces/<你的名字>/<仓库名称>把token挂到仓库上&#xff0c;让自己有写权限 务必先初始化 git init git remote add space h…

打架监测识别摄像机

打架监测识别摄像机 是一种基于人工智能和图像识别技术的智能监控设备&#xff0c;旨在实时监测环境中的暴力冲突和打架行为。这种摄像机通常被广泛应用于监狱、学校、公共交通和其他管理需要的场所&#xff0c;以预防和控制不良事件的发生。 打架监测识别摄像机能够自动识别出…

try里面放return,finally还会执行吗?

引言 喜欢请点赞&#xff0c;支持点在看。 关注牛马圈&#xff0c;干货不间断。 趣聊 今天&#xff0c;在与同事讨论编程语言特性时&#xff0c;我们谈到了一个有趣的话题&#xff1a;在JavaScript中&#xff0c;finally块中的return语句是否会覆盖try块中的return。我首先通过…

【STM32项目设计】STM32F411健康助手--硬件SPI (硬件NSS/CS)驱动st7735--1.8寸TFT显示屏(1)

#include "lcd_driver.h"static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);//液晶IO初始化配置 void LCD_Driver_Init(void) {SPI_InitTypeDef SPI_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* 使能 LCD_SPI 及GPIO 时钟 *//*!< SPI_LCD…

程序员学CFA——财务报告与分析(七)

财务报告与分析&#xff08;七&#xff09; 存货存货的初始确认简介初始确认存货的初始入账成本费用化支出 发出存货的计量发出存货的计价方法个别计价法先进先出法后进先出法加权平均法总结对比 存货盘点方法实地盘存法永续盘存法总结归纳 后进先出法的特殊问题后进先出储备后…

安全测试|AWVS渗透测试神器工具详解,零基础入门到精通,收藏这一篇就够了

前言 Acunetix Web Vulnerability Scanner(简称AWVS)是一款知名的自动化网络漏洞扫描工具&#xff0c;它通过网络爬虫测试你的网站安全&#xff0c;检测流行安全漏洞。它可以扫描任何可通过Web浏览器访问的和遵循HTTP/HTTPS规则的Web站点和Web应用程序。适用于任何中小型和大型…

终于有人将多模态重点内容做成了动画

CLIP是入门多模态的最佳选择&#xff0c;后续多模态模型基本上都延续了它的思想&#xff1a;分别用图像编码器和文本编码器将图像和文本编码到一个共享的特征空间中&#xff0c;然后通过多模态融合方法将配对的图像文本特征向量拉进。 [1] 给定 ↳ 一个包含3个文本-图像对的小…

C++期末知识点概述

《大学 C知识点概述》 在大学的计算机课程中&#xff0c;C作为一门重要的编程语言&#xff0c;有着广泛的应用和丰富的知识点。 一、基础语法 数据类型&#xff1a;C包含多种数据类型&#xff0c;如整数类型&#xff08;int、short、long 等&#xff09;、浮点类型&#xff…

shell脚本编写之传参

我们知道命令可以带参数&#xff0c;同样脚本文件运行时也可以带有参数。 1、脚本内获取参数 脚本内获取参数的格式为&#xff1a;$n n代表一个数字&#xff0c;1 为执行脚本的第一个参数&#xff0c;2 为执行脚本的第二个参数&#xff0c;以此类推。 举例&#xff0c;仍然…

新手必看 | 信息收集打点篇

0x1 前言 本篇文章主要是汇总自己在以往的信息收集打点中的一些总结&#xff0c;然后给师傅们分享下个人信息打点的各种方式&#xff0c;以及使用工具的快、准、狠的重要性。让师傅们在后面的一些红队和众测包括src的项目中可以拿到一个不错的结果。 0x2 信息打点方向 探讨下…

天气数据爬取

目录 历史气象数据获取浏览器访问模拟 历史气象数据获取 主要的python包 requests BeautifulSoup re pandas lxml 浏览器访问模拟 根据浏览器Request-Header参数&#xff0c;让request模拟浏览器行为 import requests from bs4 import BeautifulSoup import re import pandas…