dataStructure_非比较排序:基数排序(基排序/桶排序)radixSort/bucketSort/DigitalSort)&计数排序(coutingSort)

非比较排序:基数排序(基排序/桶排序)radixSort/bucketSort/DigitalSort)&计数排序(coutingSort)

ref

概述

  • 基排序(radixSort),也叫桶排序BucketSort

  • 是一种用多关键字排序思想对单逻辑关键字进行排序的方法

    • 多关键字:例如数据库中的记录之间的排序
      • 例如处理一个关系模式中包含两个可比较大小的字段:k1,k2
      • 当我们选定关键字属性集k=(k1,k2)作为关键字,并且对所有记录进行排序,
      • 遵循规则为:
        • 当排序遇到具有相同的k1的两条记录时,进一步比较k2
        • 对于十进制数而言,100 vs 120
          • 那么先比较百位权上的码,都是1
          • 在比较十位权上的码,后者2>1,因此里可以立即断言,后者比前者大
  • In computer science, radix sort is a non-comparative sorting algorithm.

  • It avoids comparison by creating and distributing elements into buckets according to their radix.

  • For elements with more than one significant digit, this bucketing process is repeated for each digit, while preserving the ordering of the prior step, until all digits have been considered.

  • For this reason, radix sort has also been called bucket sort and digital sort.

  • Radix sort can be applied to data that can be sorted lexicographically, be they integers, words, punch cards, playing cards, or the mail.

    • Lexicographical_order:词汇表顺序
  • 基数排序(英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。

  • 由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

  • 它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度

    • 数位较短的数前面补零

    • 然后,从最低位开始,依次进行一次排序。

    • 这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列

    • 基数排序的方式可以采用LSD(Least significant digital)MSD(Most significant digital)

      • LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

概念

LSD(Least significant digital)

  • LSD最低位优先法:
    • 按关键字权重递增,依次进行排序

MSD(Most significant digital)

  • 和LSD相反
  • 实际生活中,我们用MSD来排序是更多的,因为往往不需要比较低位,只要看最高的记为就可以判断出大小
  • 而如果从低位开始观察,低位的大小分量太轻,还得比较到最高位
  • 因此我们习惯从高位比较到低位

关键字和子关键字

  • 以十进制整数的排序问题为例:

    • 比较排序算法中,我们将一个元素视为最小比较单位

    • 而radixSort中,我们将一个k位数(比100就是一个k=3位数),的各个数码看做最小比较单位

      • 不妨将100这个元素称为一个关键字key

        • 而将三个数码1,0,0称为三个自关键字k1,k2,k3;它们分别代表的权重为100,10,1

操作

分配

  • 入队(桶)
    • 对于十进制数,其基radix=10;后面我们简单用r表示radix
      • 那么就创建10个队列来存储数码
      • 队列数量等于被排序对象的类型(r进制数就创建r个队列)

收集

  • 首尾相接

LSD:的案例

Input list:

  • p0=[170, 45, 75, 90, 2, 802, 2, 66]

Starting from the rightmost (last) digit, sort the numbers based on that digit:

  • p1=[{170, 90}, {2, 802, 2}, {45, 75}, {66}]

    • 分配:

      • 由于是10进制数,基r=10,创建0~9号队列待命

      • 按照个位数对输入序列进行分配(分类)(如果设立10个队列(桶),那么现在用到了其中的4个)

        • 大致流程:

          • 第1个元素170,个位数是0,因此将170分到0号队列

          • 第2个元素45,个位是5,因此将45分配到5号队列

          • …扫描并处理所有元素

          • 后面将用表格表示

          • 待命的队列号 r i r_i ri移入的元素情况
            0170,90
            1
            22,802,2
            3
            4
            545,75
            666
            7-9为空行,
            不画出来了,但是实际统计的时候,
            画出来可以更加方便统计
      • 对于进入同一个队列的元素,是有先后之分的

        • 先扫描到的元素(初始序列中排在前面的)先进入桶

        • 但是如果不区分桶内顺序也要区分

          • 只有桶内也区分先后顺序radixSort才是稳定的

          • 如果不区分,可以完成排序吗?

            • 关键在于最后一趟(第d趟)必须区分,否则显然是不能够保证有序
            • 而且最后一趟之前的某些趟如果桶内无序,可能导致基排序不稳定!
          • 此外必须坚持桶之间是有序的

            • 也就是小号桶内的元素在前,大号桶在后
    • 收集:

      • 再将这些队列首尾相连(收集),构成了一个包含所有元素的序列p1
      • 每个花括号代表一个队列,它们首尾相接

Sorting by the next left digit:

  • p2=[{02, 802, 02}, {45}, {66}, {170, 75}, {90}]

    • 第二趟.

      • 注意是基于p1=[{170, 90}, {2, 802, 2}, {45, 75}, {66}]序列的基础上进行再**分配-收集操作**

      • 分配:在按照十位数(权为10的码)的大小,对这些元素进行分配入队(到对应的基数列中)

        • 如果我们用你表格来统计/展示扫描p1序列后的分配结果,如下

          • 空白地方表示本轮没有(合适的)可插入元素
        • 待命的队列号 r i r_i ri移入的元素情况
          02,802,2
          1
          2
          3
          445
          5
          666
          7170,75
          8
          990

And finally by the leftmost digit:

  • p3=[{002, 002, 045, 066, 075, 090}, {170}, {802}]

    • 第3趟:

      • 基于p2=[{02, 802, 02}, {45}, {66}, {170, 75}, {90}]进行分类-收集操作
      • 这次我们依照百位上的数码来分类
        • 对于百位缺失的小于100的数,只需要在百位上补0即可
    • 待命的队列号 r i r_i ri移入的元素情况
      0002,002,045,066,075,090
      1170
      …(2~7,9行是空的,为了节约篇幅,不画出这些空行了)
      8802
  • 收集这些队列(首尾相连),就得到了升序排列的结果p3=[{002, 002, 045, 066, 075, 090}, {170}, {802}]

小结

  • 从上面的例子中,我们可以感受到:
    • 比较的趟数:取决于被排序序列中位数最多的那个元素(可以看绝对值最大的元素的位数)
      • 假设这个最大值为d
      • 相当于表格的数量
    • 表格的长度(行数):取决于待排序元素的基数radix的大小
      • 记为r
      • 不过,经过优化(改进)后的实现,设立的队列(桶)的数量可以减少
    • 待排序元素数量n会影响将元素分类(入队)的操作次数

性能分析

  • Each step requires just a single pass over the data, since each item can be placed in its bucket without comparison with any other element.

  • 空间效率:

    • 一趟排序需要用到辅助空间为r个队列
      • 每个队列中可包含:一个头指针和一个尾指针
      • 总的空间复杂度为O®
  • 时间效率:

    • 计数排序的问题规模使用3个参数来衡量比较合适
      • 待排序元素n
      • 待排序元素的基r
      • 待排序元素的位数d
    • 那么时间复杂度可以表示为O(d(n+r))
    • 或者说:基数排序的时间复杂度是$ O(k\cdot n) ,其中 ,其中 ,其中 n 是排序元素个数, 是排序元素个数, 是排序元素个数, k$是数字位数。
      • 注意这不是说这个时间复杂度一定优于$ O\left(n\cdot \log_2 \left(n\right)\right)$,
        • k的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集的大小;
        • $ k 决定了进行多少轮处理,而 决定了进行多少轮处理,而 决定了进行多少轮处理,而 n$是每轮处理的操作数目。

改进

  • Some radix sort implementations allocate space for buckets by first counting the number of keys that belong in each bucket before moving keys into those buckets.

    • The number of times that each digit occurs is stored in an array.

    • Although it is always possible to pre-determine the bucket boundaries using counts, some implementations opt to use dynamic memory allocation instead.

参考代码

  • 本代码尽量不使用java独有的特性(独特的内置的方法,以便用其他语言改写)

overview of the algorithm:

在这里插入图片描述

java code

(use the core thinking of counting_sort() to complete every pass of digit sort to iterate the number array)

import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
/*
* @Description:
* @Version: 2.0
* @Author: xuchaoxin
* @Date: 2021-03-27 08:12:30
* @LastEditors: xuchaoxin
* @LastEditTime: 2021-03-27 11:24:26
*/
public class RadixSort {
public static void sort(int[] number, int d, int radix) // d表示最大的数有多少位
{
/* k: to traverse the number[] */
int k = 0;
/* n: as divisor to get digits to sort in that weight */
int n = 1;
/*
* m: count the times of sort has been executed (the numbers is sorted in which
* weight )
*/
int m = 1;
/*
* handle the numbers input:the array stores complete numbers rather than signal
* radix (or use a pointer array non_matrix structure to auxiliary the sort)
*/
int[][] buckets = new int[radix][number.length];
/*
* the count array convey the sort method:counting sort(it's a stable sort
* algorithm,and it is linear time sort method in many occasions ); (counting
* the element of each bucket) the number of the buckets depends on the radix
*/
int[] count = new int[radix];
/* we need to sort d times */
while (m <= d) {/* traverse the elements:to build the buckets */
for (int i = 0; i < number.length; i++) {
/* get the radix(lsd in the range(0,radix)) */
int lsd = ((number[i] / n) % radix);
/*
* according to the lsd ,to insert the elements to the buckets array separately
*/
buckets[lsd][count[lsd]] = number[i];
/*
* counting radix (in count to calculate the index of the next element to place
* in the buckets
*/
count[lsd]++;
}
/* traverse all the buckets */
for (int i = 0; i < radix; i++) {
if (count[i] != 0)
/*
* traverse the elements in the same buckets; use the core part idea of the of
* counting sort: in the later,we iterate the number d times( digit d is the
* highest-order digit. ) (not by merge the sorted buckets)
*/
for (int j = 0; j < count[i]; j++) {
/*
* update the elements in the input array(number[]):it can be think of as a sort
* pass
*/
number[k] = buckets[i][j];
k++;
}
/*
* reset all the counting array's elements in time(as soon as the bucket is
* used)
*/
count[i] = 0;
}
/*
* update the weight to get new signal digits(number.lenth digits totally every
* time) to make a new round sort
*/
n *= radix;
/* reset k */
k = 0;
/* counting the rounds that have been executed */
m++;
}
}
/**
* the max in the input numbers
*/
public static int max(int[] number) {
// Arrays.sort(number);
// return number[number.length-1];
int maxElement = number[0];
for (int i = 0; i < number.length; i++) {
if (maxElement < number[i]) {
maxElement = number[i];
}
}
return maxElement;
}
/* get d(the highest-order digit) */
public static int getD(int maxElement, int radix) {
int d = 0;
while (maxElement > 0) {
maxElement /= radix;
d++;
}
return d;
}
public static void main(String[] args) {
// Scanner sc=new Scanner(System.in);
// ArrayList<Integer> list=new ArrayList<>();
// System.out.println("testing...");
// /* attention! use Ctrl+z to end your input(if you use windows OS ) */
// while(sc.hasNextInt()){//not use hasNext()
// int num= sc.nextInt();
// list.add(num);
// }
// sc.close();
// /*list to int[] */
// // int[] numbers=new int[list.size()];
// // for(int i=0;i<list.size();i++){
// // numbers[i]=list.get(i);
// // }
// // Integer[] numbers=(Integer[])list.toArray();
// /* another method to transition: */
// int[] numbers=list.stream().mapToInt(Integer::intValue).toArray();
/* use the built input to test the function: */
// int[] numbers = { 73, 22, 93, 43, 55, 14, 28, 656844, 35649, 8441, 353, 10 };
/* use random sequence to test: */
Random random=new Random();
int[] numbers=new int[100];
for(int i=0;i<100;i++){
numbers[i]=random.nextInt(900);
}
System.out.println("the input sequence is:");
for(int element:numbers){
System.out.print(element+" ");
}
System.out.println("");
int d = getD(max(numbers), 100);
RadixSort.sort(numbers, d,100);
System.out.println("the sorted sequence:");
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
// System.out.println(getD(max(numbers), radix));
}
}

计数排序是非比较排序,牺牲空间换取时间(Linear Time)

ref

code py

  • (adapted from Introduction to Alogrithm)
'''
Description:
Version: 2.0
Author: xuchaoxin
Date: 2021-03-26 20:58:42
LastEditors: xuchaoxin
LastEditTime: 2021-03-26 23:07:59
'''
"""
for i ← 1 to k
do C[i] ← 0
for j ← 1 to n
do C[A[ j]] ← C[A[ j]] + 1 ⊳C[i] = |{key = i}|
for i ← 2 to k
do C[i] ← C[i] + C[i–1] ⊳C[i] = |{key ≤ i}|
for j ← n down_to 1
do B[C[A[ j]]] ← A[ j]
C[A[ j]] ← C[A[ j]] – 1
"""
def counting_sort(A):
"""counting_sort(),stable sort algorithm;
the function apply to natural numbers,but:
if you want to sort float,then times all the elements a positive integer big enough to make them natural numbers
if you want to sort negative numbers,you can plus all of the element a positive big enough to make them natural numbers
Args:
A (List): list/array to be sort
"""
sizeOfA = len(A) # n
max_element = max(A) # k
count_list = []
result_list = []
""" initial the counting list """
for i in range(0, max_element+1):
# A[i]=0
count_list.append(0)
# result_list.append(0)
for i in range(0, sizeOfA):
result_list.append(0)
""" counting values:(it's ok) """
for i in range(0, sizeOfA):
count_list[A[i]] += 1
""" update the values(element) after the second element in the count_list;
however,the indice start from 0,so substract 1 at first """
count_list[0] -= 1
for i in range(1, max_element+1):
count_list[i] += (count_list[i-1])
""" insert the element of A(A[i]) to correct place in the sorting sequence """
for i in range(sizeOfA-1, -1, -1):
""" in order to find the cause :IndexError: list assignment index out of range
I print some value to locate it(comment out the program statement),the cause is that I didn't initial the list:result_list"""
# print("i=",i)
# print("A[i]=",A[i])
# print("len(count_list)=",len(count_list))
# print("")
result_list[count_list[A[i]]] = A[i]
""" update the indice of count_list:namely,the value which indice=A[i]:count_list[A[i]] """
count_list[A[i]] -= 1
return result_list
def main():
""" test the counting_sort() """
print(counting_sort([4, 2, 2, 6, 9, 0, 1]))
# main()
posted @   xuchaoxin1375  阅读(11)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示