排序算法是《数据结构与算法》中最基本的算法之一。
内部排序:数据记录在内存中进行排序
外部排序:因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存
int i = 11;
int j = 21;
System.out.println(i);
System.out.println(j);
int temp = i;
i = j;
j = temp;
System.out.println(i);
System.out.println(j);
(1) 优点:语法精炼
(2) 缺点:只能遍历,无现成的游标
找最小值:在一个数组当中,随意挑出一个值,然后依次用其他值和他比较,只要比他小,就替换最小值,一轮结束,就可以找到最小的那个值。
// 找最小值和最大值
int[] is = {11,0,-1,21,93,99,-14};
int minValue = is[0];
// for(int i = 0 ; i < is.length ; i ++){
// int item = is[i];
// }
//for-each 遍历:在数据集当中依次寻访
for(int item : is){
// System.out.println(item);
if(item < minValue){
minValue = item;
}
}
System.out.println(minValue);
// 找最小值和最大值
int[] is = {11,0,-1,21,93,99,-14};
int maxValue = is[0];
//for-each 遍历:在数据集当中依次寻访
for(int item : is){
// System.out.println(item);
if(item > maxValue){
maxValue = item;
}
}
System.out.println(maxValue);
冒泡排序的意义在于:每一轮,把所有的值比较一遍,把最小/最大的挑到一端。
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
算法步骤
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
把最后一个值,放到合适的位置
int[] is = {11,22,33,44,11,55};
//交换的次数
//数据寻访的次数
//复杂度
for(int i = is.length - 1 ; i > 0 ; i --){
if(is[i]<is[i-1]){
int temp = is[i];
is[i] = is[i-1];
is[i-1] = temp;
System.out.println(Arrays.toString(is));
}else{
break;//少4次
}
}
System.out.println(Arrays.toString(is));
//不清楚,这个数据集当中,数据的分布情况
int[] is = {11,0,-1,21,93,99,-14};
for(int i = 0 ; i < is.length - 1 ; i ++){
// 设定一个标识,若为true,则表示此次循环并没有进行交换,也就是待排序列已经有序,排序已经完成
boolean flag = true;
for(int j = i + 1; j < is.length ; j ++){
if(is[i]>is[j]){
int temp = is[i];
is[i] = is[j];
is[j] = temp;
flag = false;
}
}
if (flag) {
break;
}
}
System.out.println(Arrays.toString(is));
选择排序的意义在于:每一轮,把最小或者最大的那个值的游标找出来,然后和基准值交换,一轮只交换一次
选择排序(Selection sort)是一种简单直观的排序算法。
它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
int[] is = {11,0,-1,21,93,99,-14};
// 总共要经过N-1轮比较
for(int i = 0 ; i < is.length - 1 ; i ++){
int minIndex = i;
//每轮需要比较的次数N-i
for(int j = i + 1 ; j < is.length ; j ++){
if(is[j] < is[minIndex]){
//记录目前找到的最小值元素的下标
minIndex = j;
}
}
//将找到的最小值和位置所在的值进行交换
int temp = is[minIndex];
is[minIndex] = is[i];
is[i] = temp;
}
System.out.println(Arrays.toString(is));
插入排序的意义在于:稳定的把后面的值,在已排序好的序列中,找到合适的位置。通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序(Insertion sort)是一种简单直观且稳定的排序算法。
如果有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,算法适用于少量数据的排序,时间复杂度O(n^2)。是稳定的排序方法。插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
算法步骤
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置
int[] is = {11,0,-1,21,93,99,-14};
//j不能等于0,因为j要和前面一个比
//j前面的一个值,一定比他小,后面一定比他大
//就以上两点,倒过来说:j如果没到1,同时j比前面一个小的时候,循环要继续下去
//从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
for(int i = 1 ; i < is.length ; i ++){
for(int j = i ; j > 0 && is[j] < is[j-1]; j --){
int temp = is[j];
is[j] = is[j-1];
is[j-1] = temp;
System.out.println(Arrays.toString(is));
}
}
//另外一种写法
for(int i = 1 ; i < is.length ; i ++){
//记录要插入的数据
int temp = is[i];
//从已经排序的序列最右边的开始比较,找到最小的数
int j =i;
while (j >0 && temp < is[j-1]) {
is[j] = is[j-1];
j--;
}
//存在比其小的数,插入
if (j != i) {
is[j] = temp;
}
}
每一种排序(冒泡、选择、插入)
(1) 最多比较多少次(2) 最少比较多少次(3) 最多交换多少次(4) 最少交换多少次
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
算法步骤
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
(1) 基本思想:
① 基于插入排序
② 对原本的数列进行分组,然后对每一组分别进行插入排序
③ 分组的目标是让最后一次排序前,数列能够基本有序
(2) 步骤:
① 创建一个序列,这个序列是用于对数列进行分组的(由你自己决定)
② 序列的最后一个,必须是增量为1
③根据增量序列,对数列进行分组,每一组分别进行插入排序
(3) 注意:
① 无论怎么设置跨度(增量),都必须要保证最后一次的增量是1
② 分了组以后,每一组都是插入排序
int[] is = {11,2,-1,0,5,8,99,1,21};
//1.创建增量
// int[] gs = {1,3,1};//序列随意,根据自己的需求随意创建
int g = 1;//起始
while(g < is.length) {
g = g * 3 + 1;
}
while(g > 0) {
//2.分组
for(int i = g ; i <is.length ; i ++) {
int temp = is[i];//插入排序开始的游标
int j = i - g;//前一个值的游标
//插入
while(j >= 0 && is[j] > temp) {
is[j + g] = is[j];
j -= g;
}
is[j + g] = temp;
}
g = g / 3;
}
System.out.println(Arrays.toString(is));
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
算法步骤
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;设定两个指针,最初位置分别为两个已经排序序列的起始位置;比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;重复步骤 3 直到某一指针达到序列尾;将另一序列剩下的所有元素直接复制到合并序列尾。
(1) 基本思想
① 分治
② 使用****递归****来完成分治
(2) 步骤
① 申请空间:空间大小为2个分开的数列的长度之和
② 两个游标,分别指向两个数列的起始位置,依次比较,将小的放入合并后的序列,当都排完后,将剩下的一次性放入合并序列
(3) 注意:
① 申请的空间,不要频繁重新申请
② 采用二分和递归的方式,如果进一步挖掘,使用两个线程来完成排序;
public static void main(String[] args) {
int[] is = {11,2,-1,0,5,8,99,1,21};
sort(is);
System.out.println(Arrays.toString(is));
}
public static void sort(int[] is) {
//1.申请一个空间:为了避免重复创建数组,从而导致不必要的性能消耗
int[] temp = new int[is.length];
sort(is,0,is.length-1,temp);
}
//怎么分治
public static void sort(int[] is , int left , int right ,
int[] temp) {
if(left < right) {//左面的游标必然是小于右面的游标
//找中间值
int mid = (left + right) / 2;
//递归分
sort(is,left,mid,temp);//左边递归归并,是使左边有序
sort(is,mid+1,right,temp);//右边递归归并,是使右边有序
//在合并的同时,完成排序工作
merge(is,left,mid,right,temp);
}
}
public static void merge(int[] is,int left , int mid , int right,int[] temp) {
//找到两个数列的游标起始
int i = left;//左面指针开始位置
int j = mid + 1;//右边指针开始位置
int t = 0;//合并的临时数组指针
//依次比较i和j,把小的挑出来,放入到临时数组
while(i <= mid && j <= right) {//i和j,任意一个超出位置,则代表对应的数组已被清空
//比较i和j
if(is[i] <= is[j]) {
temp[t ++ ] = is[i ++];
}else {
temp[t ++ ] = is[j ++];
}
}
//有某个数组被清空了,将另一个没有被清空的数组,剩下来的内容,一并放入临时数组
while(i <= mid) {//j被清空了
temp[t ++] = is[i++];//把i剩下的放入临时数组
}
while(j <= right) {//i被清空了
temp[t ++] = is[j++];//把j剩下的放入临时数组
}
//排好的数列,目前还是保存在temp当中
//拷贝
t = 0;
while(left <= right) {
is[left ++] = temp[ t++ ];
}
}
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
算法步骤
从数列中挑出一个元素,称为 “基准”(pivot)。重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
(1) 基本思想:
① 分治
② 通过冒泡的方式来完成内部排序
③ 三数取中法
(2) 步骤
① 挑出一个元素作为基准值
② 把比基准值小的挑出来,放到基准值前面;把比基准值大的挑出来,放到基准值后面;完成分区工作。
③ 递归的进行这个操作,直到所有的子分区全部完成排序
(3) 注意
① 寻找基准值之后,一定要对三数先完成排序工作
② 一轮排序后,实际上的目标是2个。一个是保证三数是有序的,二个是保证分区后的子分区,基本有序。
public static void main(String[] args) {
int[] is = {11,2,-1,0,5,8,99,1,21};
sort(is,0,is.length -1);
System.out.println(Arrays.toString(is));
}
//left:左指针
//right:右指针
public static void sort(int[] is , int left , int right) {
if(left < right) {
//找基准值
dealPivot(is,left,right);
//找到基准值所在的位置
int pivot = right - 1;
//左指针
int i = left;
//右指针
int j = right - 1;
while(true) {
//从左往右,和基准值比较,找到一个比基准值大的位置停下来
while(is[++i]<is[pivot]) {}//如果比基准值小,则一直循环
//从右往左,和基准值比较,找到一个比基准值小的位置停下来
while(j>left && is[--j]>is[pivot]) {}//如果比基准值大,则一直循环
//当这两个循环全部停下来的时候
//交换i和j
if(i < j) {
swap(is,i,j);
}else {
//i和j发生碰撞,循环结束
break;
}
}
//把基准值和他前面的一个值交换位置
if(i < right) {
swap(is,i,right - 1);
}
//已基准值所在位置开始分区
sort(is , left , i - 1);
sort(is , i + 1 , right);
}
}
//寻找基准值
public static void dealPivot(int[] is , int left , int right) {
//三数取中
int mid = (left + right) / 2;
//三数取出来了,要对这三个数,完成排序
if(is[left] > is[mid]) {
swap(is,left , mid);
}
if(is[left] > is[right]) {
swap(is,left , right);
}
if(is[right] < is[mid]) {
swap(is,right , mid);
}
//把他移动到末尾之前
swap(is,right-1 , mid);
}
public static void swap(int[] is , int a , int b) {
int temp = is[a];
is[a] = is[b];
is[b] = temp;
}
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
堆排序的平均时间复杂度为 Ο(nlogn)。
创建一个堆 H[0……n-1];
把堆首(最大值)和堆尾互换;
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
重复步骤 2,直到堆的尺寸为 1。
(1) 基本思想:
① 通过完全二叉树的方式,来完成数据的堆积
② 寻找大顶堆/小顶堆,交换堆顶或者堆尾;比方说,寻找大顶堆,那么所有的堆调整完毕以后,堆顶一定是最大值。再把堆顶和堆尾交换位置,那么堆尾就变成了最大值;也就是说,堆尾在这个时候,完成了排序。
(2) 步骤
① 交换堆顶和堆尾
② 缩小堆的尺寸
(3) 几个概念
① 大顶堆:
1) is[i]>=is[2i+1]
2) is[i]>=is[2i+2]
② 小顶堆:
1) is[i]<=is[2i+1]
2) is[i]<=is[2i+2]
③ 找第一个非叶子节点
1) Is.length / 2 - 1
(1) 注意:
① 在寻找堆顶的过程,是从下而上,从右往左进行寻找
② 找到堆顶以后,还需要再次进行一轮调整
③ 交换堆顶和堆尾,完成一个元素的排序
注意:以上排序没有局限性,对所有情况都一视同仁,排序工作有一定的前置条件,在特定情况下,比以上的7种都要快
public static void main(String[] args) {
// 堆排序
int[] is = {11,2,-1,0,5,8,99,1,21};
sort(is);
System.out.println(Arrays.toString(is));
}
public static void sort(int[] is) {
//1.构建大顶堆
//1)is[i]>=is[2i+1]
//2)is[i]>=is[2i+2]
//2.从下往上,从右往左寻找第一个非叶子节点
//Is.length / 2 - 1
for(int i = is.length / 2 -1 ; i >= 0 ; i --) {
//找到了某个堆的堆顶的位置
tiaozheng(is,i,is.length-1);
}
//3.调整堆结构:因为前面已经完成了堆顶的一次调整,但是,前面的这个
//调整,只是由下而上,由右往左,调整在寻找大顶堆过程中再次变得无序的
//小堆
//4.同时交换堆顶和末尾元素
for(int j = is.length - 1 ; j > 0 ; j --) {
swap(is, j , 0);
tiaozheng(is , 0 , j);//从上往下,从左往右找,排除堆尾
}
}
//调整大顶堆
//仅仅是在调整
public static void tiaozheng(int[] is , int i , int length) {
int temp = is[i];//从第一个非叶子节点开始
//从i节点开始:判断其下方的两个叶子和他的大小关系
//先判断左面一个
for(int k = i * 2 + 1; k < length ; k = k * 2 + 1) {
if( k + 1 < length && is[k] < is[k+1]) {//找到右叶子节点
k++;//指向右面的节点
//也就是寻找两个叶子节点:哪个大k指向哪个
}
//把叶子节点和堆顶进行比较,如果大于堆顶则交换
if(is[k] > temp) {
is[i] = is[k];//替换堆顶
i = k;//替换指向
}else {
break;//如果不是,这个小堆就算是排序结束了
}
}
is[i] = temp;
}
//交换元素
public static void swap(int[] is , int a , int b) {
int temp = is[a];
is[a] = is[b];
is[b] = temp;
}
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
算法步骤
找出待排序的数组中最大和最小的元素
统计数组中每个值为i的元素出现的次数,存入数组C的第i项
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
(1) 基本思想:将确定范围内的数值放入到一个额外开辟的空间(对应游标位置)当中,要求必须有固定,同时必须是正整数
(1) 步骤
① 重新开辟一个空间,空间大小,上限由待排序数组的最大值决定;
② 将和对应游标相等的值,在该空间当中计数+1
(2) 注意:
① 一定要先找到最大值
② 局限性:
1) 最大或者最小值差距过大,而且不是均匀分布的时候,及其浪费空间
2) 不适合于负数的情况
③ 天然优势:
1) 快
2) 复杂度低
3) 简单易懂
public static void main(String[] args) {
// 计算排序
int[] is = {11,2,11,0,5,8,99,1,21};
is = sort(is);
System.out.println(Arrays.toString(is));
}
public static int[] sort(int[] is) {
//1.寻找最大值,来寻找新开辟空间的上限
int max = is[0];
for(int i = 1 ; i < is.length ; i ++) {
if(is[i] > max) {
max = is[i];
}
}
//2.根据最大值创建空间
int[] count = new int[max+1];
//3.遍历数组,统计各个元素的个数
for(int i = 0 ; i < is.length ; i ++) {
count[is[i]]++;//找到对应的位置,计数+1
}
//4.遍历统计后的数据,输出结果
int index = 0 ;
int[] sorted = new int[is.length];
for(int i = 0 ; i < count.length ; i ++) {
//每个计数要往排序后的数组放若干次
for(int j = 0 ; j < count[i] ; j ++) {
sorted[index++] = i;
}
}
return sorted;
}
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
算法步骤
在额外空间充足的情况下,尽量增大桶的数量
使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中。
(1) 基本思想:
① 把数据划分为若干个范围(桶)
② 每个范围都是可以自由增加数量的
③ 对应范围内的数据,放入到对应的桶当中,并对该桶当中的数据进行排序
④ 拼接所有桶当中的数据
(2) 步骤
① 创建桶
② 确定范围
③ 将范围内的数据放入桶,排序
④ 依次遍历桶
(3) 划定桶的范围
① 桶的最大值和最小值:(最大值-最小值+1)/自定义桶的数量=桶的跨度
② 桶的游标:(当前值-最小值)/跨度
③ 桶的个数越多,那么每个桶当中的数值就越少,那么就排序的效率越高,如果桶的个数大于等于数据的个数,那么桶排序就变成了计数排序。桶的个数越少,每个桶当中的排序次数越多,那么排序的效率就越低,但是越节省空间。当桶的个数只有一个,那么就是一个普通的排序方式。
(4) 注意:
① 优势:如果待排序的数组当中的元素,基本处于均匀分布的情况下,使用桶排序的效率会相对比较高
② 缺点:
1) 前置条件比较多
2) 如果某个桶当中,有数据倾斜的情况,会造成这个桶当中的效率极低
public static void main(String[] args) {
// 桶排序
int[] is = {11,2,17,0,5,8,9,15,21};
is = sort(is);
System.out.println(Arrays.toString(is));
}
@SuppressWarnings("unchecked")
public static int[] sort(int[] is) {
//1.寻找最大和最小值:目的是划分桶的一个跨度
int max = is[0];
int min = is[0];
for(int i : is) {
if(i > max) {
max = i;
}
if(i < min) {
min = i;
}
}
//自定义桶的数量
double bucketCount = 3;
//2.获取跨度
double space = (max - min + 1) / bucketCount;
//3.创建桶
LinkedList<Integer>[] buckets =
new LinkedList[(int)bucketCount];
//4.把数组当中的元素要分配到对应的桶当中
int len = is.length;
for(int i = 0 ; i < len ; i ++) {
//找到桶的游标
int index = (int) Math.floor((is[i] - min) / space);
if(buckets[index] == null) {
//在桶当中的链表中判断
//如果链表当中没有值
buckets[index] = new LinkedList<Integer>();
buckets[index].add(is[i]);
}else {
//如果链表当中有值
//排序:为什么选择插入
int k = buckets[index].size() - 1;
while(k >= 0 && buckets[index].get(k) > is[i]) {
if(k + 1 > buckets[index].size() - 1) {
buckets[index].add(buckets[index].get(k));
}else {
buckets[index].set(k + 1,
buckets[index].get(k));
}
k -- ;
}
if( k + 1 > buckets[index].size() - 1 ) {
buckets[index].add(is[i]);
}else {
buckets[index].set(k + 1, is[i]);
}
}
}
//5.合并桶当中的元素
int count = 0 ;
//遍历桶
for(int i = 0 ; i < bucketCount ; i ++) {
if(buckets[i] != null && buckets[i].size() > 0 ) {
for(int item : buckets[i]) {
is[count ++] = item;
}
}
}
return is;
}
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数
基数排序 vs 计数排序 vs 桶排序
基数排序:根据键值的每位数字来分配桶
计数排序:每个桶只存储单一键值
桶排序:每个桶存储一定范围的数值
(1) 基本思路
① 分别取出数值的个十百千……位
② 对每一位的数值进行分类
③ 依次类推,分别再次比较十位,百位,千位……
(2) 步骤:
① 开辟一个0-9的空间
② 取个十百位
③ 分别对个十百位,进行归类
④ 以此类推,完成排序
(3) 注意:
① 结合了桶排序和计算排序的优点,能够在有限的空间当中,能够不断的完成重复的工作
② 不需要对数值进行过多的比较工作。
③ 只能在正整数范围内进行排序
public static void main(String[] args) {
// 基数排序
int[] is = {11,2,17,0,51,18,9,15,21};
is = sort(is,100);
System.out.println(Arrays.toString(is));
}
public static int[] sort(int[] is,int d) {
//d:位数的上限
int n = 1;//起始位数,1起始位数是个位
int length = is.length;
//排序的桶用于保存每次排序后的结果
int[][] buckets = new int[10][length];
int[] order = new int[10];//保存每个桶里有多少个元素
while(n < d) {
for(int num : is) {
//取当前位的值
int di = (num / n) % 10;
//每个桶里的元素依次往后放
buckets[di][order[di]] = num;
order[di] ++;//桶里的元素个数要递增
}
int index = 0;
//遍历前一个循环生成的元素
for(int i = 0 ; i < 10 ; i ++) {
if(order[i] != 0) {
for(int j = 0 ; j < order[i] ; j ++) {
//从各个桶当中取元素
is[index ++] = buckets[i][j];
}
}
order[i] = 0;//桶里的计数器要归零
}
//准备下一轮循环
//位数要增加
n *= 10;
}
return is;
}