通俗理解

基数排序通俗点讲,就是先把所有待排序的元素按照个位数,十位数,百位数,…划分下去,先按照各个元素的个位数大小,对所有元素进行排序。基数排序是稳定的,也就是说要是同一位数上的数字相同,原先相对位置靠前的元素排位依旧相对靠前。当个位数排序结束后,再按照同样的方法依据更高位数字对元素进行排序,位数不足可看做补0。==排序趟数等于最大元素的位数。==所有位数都排序结束,得到的结果就是最终结果。

图码结合解释基数排序

十进制数基数排序可以理解为10个桶,用来表示10个基数

java中如何标识三位数_数组

以数组nums = **[5, 8 , 11 , 1]**为例进行基数排序,对应10个桶需要一个长度为10的bucket数组,bucket数组是用来存放每一趟排序时各个桶存放的元素个数。再定义一个out数组来存放每一趟排序结束后的数组各元素情况。

int[] out = new int[nums.length];

java中如何标识三位数_数组_02

排序过程

先取得数组中的最大值,初始进制设置为base = 1,每下一趟进制设置为base * 10。

long base = 1; //使用long型是为了防止位数过长而整型溢出 int maxValue = Arrays.stream(nums).max().getAsInt(); //取得数组最大元素11 while(maxValue >= base) { //确定排序趟数,最大排序趟数等于最大值的位数 int[] bucket = new int[10]; //每一趟排序都把bucket重置为全0 //其他代码 base *= 10; }

个位数排序

按照各个位数的数字对10取余决定当前元素应该放入哪一个桶里面。例如11和1的个位数都是1,所以个位数对10取余都是1,两者都应该放入1号桶里面,1号桶有两个元素,1号桶对应的bucket[1]应该为2。

for(int i = 0; i < nums.length; i++) { int digit = (nums[i] / (int)base) % 10; //取得当前位数并决定元素应该放入哪一个桶 bucket[digit] += 1; //每一个桶的元素个数 }

java中如何标识三位数_c++_03

将bucket数组各个位置的元素与前一位元素相加,目的是为了确定 i 号桶里面的元素在排序后的out数组中起始位置应该为多少(注意在排序数组里面下标应该减1)。比如相加后bucket[8] = 4,表明8号桶里面的元素8在排序后的out数组中是从第四个位置 (out数组对应的下标为3)开始存储的

for(int i = 1; i < 10; i++) { //0号桶自然不用处理,因为按照bucket[0]等于多少开始排0号桶元素就可以了,而且0号桶元素位置就是其他桶的参考位置 bucket[i] += bucket[i - 1]; }

java中如何标识三位数_数组_04

接下来可以正向或者逆向将各个桶的元素取出来放到排序后的数组当中,注意基数排序是稳定的,所以如果是逆向拿桶的元素,下一个元素应该在上一个元素的前面。这里以逆序为例子。

for(int i = nums.lenth - 1; i >= 0; i--) { int digit = (nums[i] / (int)base) % 10; //确定元素在几号桶 out[bucket[digit] - 1] = nums[i]; //元素下标应该减1 bucket[digit]--; //每从桶里面排出一个元素,桶的元素就应该减1,同时也是对应排序数组的更左边的下标,保证了基数排序的稳定性 }

java中如何标识三位数_数据结构_05

最后把整个排序数组复制到原数组,原数组就完成了个位数的排序。

System.arraycopy(out,0,nums,0,nums.length);

java中如何标识三位数_java_06

个位数排序结束后桶的情况依图所示。

java中如何标识三位数_c++_07

十位数排序

依照个位数排序过程,十位数的1,5,8由于是为都是0,所以应该放到0号桶,0号桶有3个元素,一号桶有一个元素11。

java中如何标识三位数_java中如何标识三位数_08

十位数排序后,一号桶的元素在排序后的数组里面应该从第四个元素开始放置。

java中如何标识三位数_java中如何标识三位数_09

十位数排序后排序数组的结果应该如下。

java中如何标识三位数_java_10

十位数排序后的桶的结果如下图,由于最大值11只有两位数,所以两趟排序之后排序就结束了。

java中如何标识三位数_java_11

Java代码基数排序

public static void radixSort(int[] nums) {
    long base = 1;

    int[] out = new int[nums.length];
    int maxValue = Arrays.stream(nums).max().getAsInt();

    while(maxValue >= base) {
        int[] bucket = new int[10];
        for(int i = 0; i < nums.length; i++) {
            int digit = (nums[i] / (int)base) % 10;
            bucket[digit]++;
        }
        for(int i = 1; i < 10; i++) {
            bucket[i] += bucket[i - 1];
        }
        for(int i = nums.length - 1; i >= 0; i--) {
            int digit = (nums[i] / (int)base) % 10;
            out[bucket[digit] - 1] = nums[i];
            bucket[digit]--;
        }
        
        System.arraycopy(out,0,nums,0,nums.length);
        base *= 10;
   }
}

C++代码基数排序

class Sort {
public:
    int radixSort(vector<int>& nums) {
        int n = nums.size();
        int base = 1;
        vector<int> out(n);
        int maxValue = *max_element(nums.begin(),nums.end());
        
        while(maxValue >= base) {
            vector<int> bucket(10);
            for(int i = 0; i < n; i++) {
                int digit = (nums[i] / base) % 10;
                bucket[digit]++;
            }
            for(int i = 1; i < 10; i++) {
                bucket[i] += bucket[i - 1];
            }
            for(int i = n - 1; i >= 0; i--) {
                int digit = (nums[i] / base) % 10;
                out[bucket[digit] - 1] = nums[i];
                bucket[digit]--;
            }
            copy(out.begin(),out.end(),nums.begin());
            base *= 10;
        }
        
        return 0;
    }
}