算法基础10:排序算法

排序算法介绍

排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。
排序的分类:

  1. 内部排序:

    指将需要处理的所有数据都加载到内部存储器中进行排序。

  2. 外部排序法:

    数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。

  3. 常见的排序算法分类(见下图)

    基础排序是桶排序的一个扩展

算法的时间复杂度

度量一个程序执行时间的两种方法

  1. 事后统计的方法

    这种方法可行,但是有两个问题:一是要想对设计的算法的运行性能进行评测,需要实际运行该程序;二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,这种方式,要在同一台计算机的相同状态下运行,才能比较那个算法速度更快。

  2. 事前估算的方法

    通过分析某个算法的时间复杂度来判断哪个算法更优

时间频度

时间频度:一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T()。[举例说明]

时间复杂度

  1. 一般情况下,算法中的基本操作语句的重复执行次数是问题规模的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/fn)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(fn)),称O(fn)为算法的渐进时间复杂度,简称时间复杂度。

  2. T(n)不同,但时间复杂度可能相同。如:

    它们的T(n)不同,但时间复杂度相同,都为O(n²):

  3. 计算时间复杂度的方法:

    • 用常数1代替运行时间中的所有加法常数
    • 修改后的运行次数函数中,只保留最高阶项
    • 去除最高阶项的系数

常见的时间复杂度

常数阶 O(1)

无论代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是0(1)

1
2
3
4
5
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;

上述代码在执行的时候,它消耗的时候并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时间复杂度。

对数阶

线性阶 O(n)

1
2
3
4
for(i1;i<=n;++i){
j=i;
j++;
}

说明:这段代码,for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度

线性对数阶

1
2
3
4
5
6
for(m=1;m<n;m++){
i=1;
while(i<n){
i=i*2;
}
}

说明:线性对数阶O(nlogN)其实非常容易理解,将时间复杂度为Olog)的代码循环N遍的
话,那么它的时间复杂度就是n*O(log_2 N),也就是了O(nlog_2 N)

平方阶O(n²)

1
2
3
4
5
6
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
j = i;
j++;
}
}

说明:平方阶O)就更容易理解了,如果把O)的代码再嵌套循环一遍,它的时间复杂度就是On),这段代码其实就是嵌套了2层n循环,它的时间复杂度就是O(nn),即o(n)如果将其中一层循环的n改成m,那它的时间复杂度就变成了O(mn)

平均时间复杂度和最坏时间复杂度

  1. 平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,该算法的运行时间。

  2. 最坏情况下的时间复杂度称最坏时间复杂度。一般讨论的时间复杂度均是最坏情况下的时间复杂度。这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限,这就保证了算法的运行时间不会比最坏情况更长。

  3. 平均时间复杂度和最坏时间复杂度是否一致,和算法有关(如下图)。

算法的空间复杂度

  1. 类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)定义为该算法所耗费的存储空间,它也是问题规模的函数。
  2. 空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。有的算法需要占用的临时工作单元数与解决问题的规模有关,它随着n的增大而增大,当较大时,将占用较多的存储单元,例如快速排序和归并排序算法就属于这种情况
  3. 在做算法分析时,主要讨论的是时间复杂度。从用户使用体验上看,更看重的程序执行的速度。一些缓存产品(redis,memcache)和算法(基数排序)本质就是用空间换时间

冒泡排序

冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

(优化)因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志Iag判断元素是否进行过交换。从而减少不必要的比较。

示例图解

应用实例

我们举一个具体的案例来说明冒泡法。我们将五个无序的数:3,9,-1,10,20使用冒泡排序法将其排成一个从小到大的有序数列。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 冒泡排序
*
* @author:XMD
* @date: 29.8.22
*/
public class BubbleSorting {

public static void main(String[] args) {
int arr[] = {3, 9, -1, 8, 1, 10};

boolean flag = false;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
flag = true;
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
if (!flag) {
break;
} else {
flag = false;
}
}
for (int i : arr) {
System.out.println(i);
}
}
}

时间复杂度为O(n²),因为时嵌套循环

选择排序

选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。

排序思想

分析图

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ChooseSort {


/**
* 从小到大排序
* 时间复杂度为O(n^2)
* @param ints
* @return
*/
public static void test(int[] ints) {
for (int i = 0; i < ints.length -1; i++) {
int minIndex = i;
int min = ints[i];
for (int j = i + 1; j < ints.length; j++) {
if (min > ints[j]) {
min = ints[j];
minIndex = j;
}
}

if (minIndex != i){
ints[minIndex] = ints[i];
ints[i] = min;
}
}
}

public static void main(String[] args) {
int[] temp = new int[20];
for (int i = 0; i < 20; i++) {
int random = RandomUtils.nextInt(0, 100);
temp[i] = random;
}
test(temp);
System.out.println(JSON.toJSONString(temp));
}
}

时间复杂度为O(n²),因为时嵌套循环

插入排序

插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。

排序思想

插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

图解

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class InsetSort {

public static void main(String[] args) {
int[] temp = new int[20];
for (int i = 0; i < 20; i++) {
int random = RandomUtils.nextInt(0, 100);
temp[i] = random;
}
insetSort(temp);
System.out.println(JSON.toJSONString(temp));
}

private static void insetSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int intsetVal = arr[i];
int intsetIndex = i - 1;
//如果改为从大到小,改成intsetVal > arr[intsetIndex] 即可
while (intsetIndex >= 0 && intsetVal < arr[intsetIndex]){
arr[intsetIndex + 1] = arr[intsetIndex];
intsetIndex -= 1;
}
arr[intsetIndex + 1] = intsetVal;
}

}

}

希尔排序

插入排序存在的问题

希尔排序介绍

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序

基本思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

案例

应用实例

有两种方式

  1. 希尔排序时,对有序序列在插入时采用交换法,并测试排序速度
  2. 希尔排序时,对有序序列在插入时采用移动法,并测试排序速度

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import java.util.Arrays;

/**
* 希尔排序
*
* @author:XMD
* @date: 31.8.22
*/
public class SHellSort {
public static void main(String[] args) {
//自定义数组
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
shellSoret2(arr);
}

/**
* 对有序序列在插入时采用交换法,交换法的效率并不快
* 8W个数据的希尔排序的交换法排序大约用时为:17秒
* 8W个数据的插入算法排序数大约用时为5秒
*
* @param arr
*/
public static void shellSoret(int[] arr) {
int temp;

// //第一轮排序
// //因为第1轮排序,是将10个数据分成了5组
// for (int i = 5; i < arr.length; i++) {
// //遍历各组中所有的元素(共5组,每组有2个元素),步长5
// for (int j = i - 5; j >= 0; j -= 5) {
// //如果当前元素大于加上步长后的那个元素,说明交换
// if (arr[j] > arr[j + 5]) {
// temp = arr[j];
// arr[j] = arr[j + 5];
// arr[j + 5] = temp;
// }
// }
// }
// System.out.println("第一轮结果:" + Arrays.toString(arr));
//
//
// //因为第2轮排序,是将10个数据分成了5/2 = 2组
// for (int i = 2; i < arr.length; i++) {
// //遍历各组中所有的元素(共5组,每组有2个元素),步长5
// for (int j = i - 2; j >= 0; j -= 2) {
// //如果当前元素大于加上步长后的那个元素,说明交换
// if (arr[j] > arr[j + 2]) {
// temp = arr[j];
// arr[j] = arr[j + 2];
// arr[j + 2] = temp;
// }
// }
// }
// System.out.println("第二轮结果:" + Arrays.toString(arr));
//
// //因为第3轮排序,是将10个数据分成了5/2/2 = 1组
// for (int i = 1; i < arr.length; i++) {
// //遍历各组中所有的元素(共5组,每组有2个元素),步长5
// for (int j = i - 1; j >= 0; j -= 1) {
// //如果当前元素大于加上步长后的那个元素,说明交换
// if (arr[j] > arr[j + 1]) {
// temp = arr[j];
// arr[j] = arr[j + 1];
// arr[j + 1] = temp;
// }
// }
// }
// System.out.println("第三轮结果:" + Arrays.toString(arr));


//根据逐步分析,使用循环处理
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
//遍历各组中所有的元素(共gap组,每组有2个元素),步长gap
for (int j = i - gap; j >= 0; j -= gap) {
//如果当前元素大于加上步长后的那个元素,说明交换
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
System.out.println("希尔结果:" + Arrays.toString(arr));
}


/**
* 对有序序列在插入时采用移动法
* 8W个数据的希尔排序的移动法排序大约用时为:1秒
* @param arr
*/
public static void shellSoret2(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j -= gap;
}
//当退出while后,就给temp找到插入的位置
arr[j] = temp;
}
}
}
System.out.println("希尔结果:" + Arrays.toString(arr));
}
}

快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。

基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

示意图

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class QuickSort {

public static void main(String[] args) {
int[] arr = new int[80];
for (int i = 0; i < 80; i++) {
int random = RandomUtils.nextInt(0, 100);
arr[i] = random;
}
// int[] arr = {4, 1, 6, 5, 3, 9, 7, 5, 8, 2, 5};
long millis = System.currentTimeMillis();
quickSort2(arr, 0, arr.length - 1);
System.out.println(System.currentTimeMillis() - millis);

System.out.println("快速排序:" + Arrays.toString(arr));
}

public static void quickSort(int[] arr, int start, int end) {
//基准点位第一个数
int pivot = arr[start];
//开始左下标为开始前面的
int l = start;
//右下标
int r = end;
int temp;

//只要左边的下标小于右边下标就一直循环
while (l < r) {
while (arr[l] <= pivot && l < end) {
l++;
}
while (arr[r] >= pivot && r > start) {
r--;
}

if (l >= r) {
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;

//如果交换完后,发现这个arr[l]=pivot值 相等--,前移
if (arr[l] == pivot) {
r--;
}
//如果交换完后,发现这个arr[r]=pivot值 相等--,后移
if (arr[r] == pivot) {
l++;
}
}

arr[start] = arr[r];
arr[r] = pivot;

if (start < r - 1) {
quickSort(arr, start, r - 1);
}
if (end > r + 1) {
quickSort(arr, r + 1, end);
}
}


public static void quickSort2(int[] arr, int left, int right) {
int l = left;
int r = right;
int pivot = arr[(left + right) / 2];
int temp;
while (l < r) {
while (arr[l] < pivot) {
l++;
}
while (arr[r] > pivot) {
r--;
}
if (l >= r){
break;
}

temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;

//如果交换完后,发现这个arr[l]=pivot值 相等--,前移
if (arr[l] == pivot) {
r--;
}
//如果交换完后,发现这个arr[r]=pivot值 相等--,后移
if (arr[r] == pivot) {
l++;
}
}
if (l == r){
l++;
r--;
}

if (left < r){
quickSort2(arr,left,r);
}
if (right > l){
quickSort2(arr,l,right);
}
}
}

归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

基本思想

难理解

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* 归并排序
*
* @author:XMD
* @date: 1.9.22
*/
public class MergetSort {
public static void main(String[] args) {
int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
//归并排序需要一个额外空间
int[] temp = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, temp);

System.out.println("归并排序:" + Arrays.toString(arr));
}

/**
* 分 + 合 方法
*
* @param arr
* @param left
* @param right
* @param temp
*/
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;//中间索引
//向左递归分解
mergeSort(arr, left, mid, temp);
//向右递归分解
mergeSort(arr, mid + 1, right, temp);
//合并
merget(arr, left, mid, right, temp);
}

}


/**
* 合并方法
*
* @param arr 原始数据
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 中转数据
*/
public static void merget(int[] arr, int left, int mid, int right, int[] temp) {
int i = left; //初始化i 左边有序序列的初始索引
int j = mid + 1; //初始化j,右边有序序列的初始索引
int t = 0;//指向temp数组的当前索引


//第一步
//先把左右两边的数据按照规则填充到temp数组
//直到左右两边的有序序列,又一遍处理完毕为止
while (i <= mid && j <= right) {
//如果左边的有序序列的当前元素,小于等于右有序序列的当前元素
//即将左边的当前元素,拷贝到temp数组
//然后t++ i++
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
t++;
i++;
} else {
////反之,将右边有序序列的当前元素,填充到temp数组
temp[t] = arr[j];
t++;
j++;
}
}


//第二步
//把有剩余数据的一边的数据依次全部填充到temp
while (i <= mid) {
////左边的有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[i];
t++;
i++;
}
while (j <= right) {
////右边的有序序列还有剩余的元素,就全部填充到temp
temp[t] = arr[j];
t++;
j++;
}

//第三步
//将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t = 0;
int tempLeft = left;
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
}

基数排序

  1. 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
  2. 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
  3. 基数排序(Radix Sort)是桶排序的扩展
  4. 基数排序是1887年赫尔曼 · 何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

基本思想

  1. 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
  2. 这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import java.util.Arrays;

/**
* 基数排序
*
* @author:XMD
* @date: 1.9.22
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53, 3, 542, 748, 14, 214};
radixSort(arr);
}

public static void radixSort(int[] arr) {
//根据前面的推导过程,我们可以得到最终的基数排序代码

//得到数组中最大的数的位数
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//得到最大数是几位数
int maxLenght = (max + "").length();


//第一轮 针对每个元素的个位

//定义一个二维数组,表示10个桶,每个桶就是一个数组

//为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
//基数排序是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];

//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
int[] bucketElementCounts = new int[10];

//这里我们使用循环将代码处理

for (int i = 0; i < maxLenght; i++) {
for (int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / (int)Math.pow(10,i) % 10;
//放入到对应的桶
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
//'按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组
int index = 0;
//遍历每一桶,并将桶中是数据,放入到原数组
for (int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中,有数据,我们才放入到原数组
if (bucketElementCounts[k] != 0) {
//循环该桶
for (int l = 0; l < bucketElementCounts[k]; l++) {
//取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
//第1轮处理后,需要将每个bucketElementCounts[k] =0
bucketElementCounts[k] = 0;
}
System.out.println("第" + i + 1 + "轮 完成:" + Arrays.toString(arr));
}


// for (int j = 0; j < arr.length; j++) {
// int digitOfElement = arr[j] % 10;
// //放入到对应的桶
// bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
// bucketElementCounts[digitOfElement]++;
// }
// //'按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组
// int index = 0;
// //遍历每一桶,并将桶中是数据,放入到原数组
// for (int k = 0; k < bucketElementCounts.length; k++) {
// //如果桶中,有数据,我们才放入到原数组
// if (bucketElementCounts[k] != 0) {
// //循环该桶
// for (int l = 0; l < bucketElementCounts[k]; l++) {
// //取出元素放入到arr
// arr[index++] = bucket[k][l];
// }
// }
// //第1轮处理后,需要将每个bucketElementCounts[k] =0
// bucketElementCounts[k] = 0;
// }
// System.out.println("第一轮 完成:" + Arrays.toString(arr));
//
//
//
// //第二轮
// for (int j = 0; j < arr.length; j++) {
// int digitOfElement = arr[j] / 10 % 10;
// //放入到对应的桶
// bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
// bucketElementCounts[digitOfElement]++;
// }
// //'按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组
// index = 0;
// //遍历每一桶,并将桶中是数据,放入到原数组
// for (int k = 0; k < bucketElementCounts.length; k++) {
// //如果桶中,有数据,我们才放入到原数组
// if (bucketElementCounts[k] != 0) {
// //循环该桶
// for (int l = 0; l < bucketElementCounts[k]; l++) {
// //取出元素放入到arr
// arr[index++] = bucket[k][l];
// }
// }
// //第2轮处理后,需要将每个bucketElementCounts[k] =0
// bucketElementCounts[k] = 0;
// }
// System.out.println("第二轮 完成:" + Arrays.toString(arr));
//
//
//
// //第二轮
// for (int j = 0; j < arr.length; j++) {
// int digitOfElement = arr[j] / 100 % 10;
// //放入到对应的桶
// bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
// bucketElementCounts[digitOfElement]++;
// }
// //'按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组
// index = 0;
// //遍历每一桶,并将桶中是数据,放入到原数组
// for (int k = 0; k < bucketElementCounts.length; k++) {
// //如果桶中,有数据,我们才放入到原数组
// if (bucketElementCounts[k] != 0) {
// //循环该桶
// for (int l = 0; l < bucketElementCounts[k]; l++) {
// //取出元素放入到arr
// arr[index++] = bucket[k][l];
// }
// }
// //第3轮处理后,需要将每个bucketElementCounts[k] =0
// bucketElementCounts[k] = 0;
// }
// System.out.println("第二轮 完成:" + Arrays.toString(arr));


}
}

说明:

如果有负数的数组,不用基数排序来排序

常用排序算法对比

-------------本文结束感谢您的阅读-------------