排序[韩顺平算法]
排序算法
各种排序算法比较
类别 | 排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | 复杂性 | 特点 | |
---|---|---|---|---|---|---|---|
插入排序 | 直接插入 | O(N) | O(N2) | O(N2) | O(1) | 稳定 | 简单 |
希尔排序 | O(N) | O(N1.3) | O(N2) | O(1) | 不稳定 | 复杂 | |
选择排序 | 直接选择 | O(N) | O(N2) | O(N2) | O(1) | 不稳定 | |
堆排序 | O(N*log2N) | O(N*log2N) | O(N*log2N) | O(1) | 不稳定 | 复杂 | |
交换排序 | 冒泡排序 | O(N) | O(N2) | O(N2) | O(1) | 稳定 | 简单 |
快速排序 | O(N*log2N) | O(N*log2N) | O(N2) | O(log2n)~O(n) | 不稳定 | 复杂 | |
归并排序 | O(N*log2N) | O(N*log2N) | O(N*log2N) | O(n) | 稳定 | 复杂 | |
基数排序 | O(d(r+n)) | O(d(r+n)) | O(d(r+n)) | O(rd+n) | 稳定 | 复杂 |
注:r代表关键字基数,d代表长度,n代表关键字个数
-
如何理解:对数阶x=O(log2n)
-
x叫做以2为底N的对数
int i=1;
while(i<n){
i=i*2;
//当i乘以2,乘完之后,i距离n就越近,假设循环x次之后,i就大于n了,循环结束
//理解为循环并没有进行n次,而是根据i值和n值的大小差距进行了x次
//假如n=1024,i值的变化就是1,2,4,8,16...直到i大于1024
//而循环次数并不是进行1024次,而是i大于1024一共执行的次数
//所以次数x=log₂1024
//x = log₂2¹º
//x=10* log₂2
//x=10
}
冒泡排序
思路
1.通过两两比较两个数之间的大小进行位置交换,比较完前两个就两个索引同时往后移一个位置,继续进行比较
2.每一趟比较的次数为未比较的个数-1
3.比较的趟数就是个数-1,因为当其他数字都顺序排好了,最后剩下的一个数肯定是在顺序的位置上啊
4.优化:如果在某一趟排序过程中没有发生一次交换,就说排序已经完成,提前结束排序!
package com.Yjm.sort;
public class BubbleSort {
/*
* 冒泡排序
* */
//思路
//1.先进行一趟排序
public static void main(String[] args) {
int[] arr = {11, 5, 1, 10};
//定义临时变量
//int temp;
/* for (int i = 0; i < arr.length - 1 - 0; i++) {//因为是前后交换的,而每一趟比较的次数
//判断两个相邻数的大小,进行交换
//
if (arr[i] > arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
//到此就可以完成一趟排序,排好一个数
}
}
System.out.println(Arrays.toString(arr));
//第二趟排序
for (int i = 0; i < arr.length - 1 - 1; i++) {// arr.length - 1 - 1因为是最后的一个数已经排好,所以只需要比较前面的数字即可
//判断两个相邻数的大小,进行交换
//
if (arr[i] > arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
//到此就可以完成一趟排序,排好一个数
}
}
System.out.println(Arrays.toString(arr));*/
//隨機生成一個10000個元素的數組,用于测试
int[] ints = new int[100000];
for (int i = 0; i < 100000; i++) {
//遍歷賦值給數組
ints[i] = (int) Math.random() * 100;
}
//測試運行時間
//打印毫秒值
long star = System.currentTimeMillis();
System.out.println(star);
//由此可得直接嵌套循环完成趟数
//直接調用
bubbleSort(ints);
long end = System.currentTimeMillis();
//总运行时间
System.out.println("运行时间:" + (end - star) / 1000);
}
//生成方法
public static void bubbleSort(int[] arr) {
int temp;
for (int i = 0; i < arr.length - 1; i++) {//趟数只需比个数-1
//每一趟比较
for (int j = 0; j < arr.length - 1 - i; j++) {// arr.length-i是因為每一趟比較完就多一個數字確定位置,就不用再比較了
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
选择排序
思路
-
首先确定排序的顺序,如果要从小到大就选择一组中最小的那一个放在第一位
-
接着就在剩下的一组中选择最小的排在未排序中的第一位也就是整个数组的第二位
- 代码实现思路
-
假设数组的第一个为最小值,把最小的一个赋值给临时最小值变量
-
定义临时最小索引变量,记录数组下标的索引
-
for循环遍历数组,然后进行比较 确定最小值
-
接着把最小值赋值给临时变量之后就根据最小临时下标把值赋值给第一个位置
代码实例
package com.Yjm.sort;
import java.util.Arrays;
public class SelectSort {
/*选择排序思路
* 1.首先确定排序的顺序,如果要从小到大就选择一组中最小的那一个放在第一位
* 2.接着就在剩下的一组中选择最小的排在未排序中的第一位也就是整个数组的第二位
*
* 代码实现思路
* 1.假设数组的第一个为最小值,把最小的一个赋值给临时最小值变量
* 2.定义临时最小索引变量,记录数组下标的索引
* 3.for循环遍历数组,然后进行比较 确定最小值
* 4.接着把最小值赋值给临时变量之后就根据最小临时下标把值赋值给第一个位置
*
* */
public static void main(String[] args) {
int[] arr = {101, 34, 119, 1,33,10,45};
selectSort(arr);
/*打印结果
[1, 34, 119, 101, 33, 10, 45]
[1, 10, 119, 101, 33, 34, 45]
[1, 10, 33, 101, 119, 34, 45]
[1, 10, 33, 34, 119, 101, 45]
[1, 10, 33, 34, 45, 101, 119]
[1, 10, 33, 34, 45, 101, 119]
*/
}
public static void selectSort(int[] arr) {
//原始数组:101,34,119,1
//选择排序时间复杂度是O(n2)
for (int j = 0; j < arr.length - 1; j++) {
//创建临时最小值变量
int min = arr[j];//假设第一个就是最小的
int minIndex = j;//记录下标
//遍历数组进行比较
for (int i = j + 1; i < arr.length; i++) {//i从1开始,因为已经假设下标0的值为最小了,所以直接和后面开始比较就行
if (min > arr[i]) {
//那我就重新记录最小值?
min = arr[i];
//然后再修改最小值的索引
minIndex = i;
}
}
//上面的for循环并没有进行数组元素的交换,只是查找得到了最小值而已
//所以要在循环结束之后进行交换
//拿到最小的值,是不是要把我的值给别人
//上面的min=arr[i];已经拿到最小值了
//先把临时最小值放到最小的值的索引,但是不会覆盖最小值,因为已经得到了
arr[minIndex] = arr[j];
//然后就把得到的最小值min赋值给第一位
arr[j] = min;
System.out.println(Arrays.toString(arr));
}
}
}
插入排序
思路
- 创建变量记录待插入元素和待插入元素的前一个位置
- 通过循环(条件是需要插入的位置不越界和 待插入的元素满足比较条件,大于或者小于)
- 循环体中把比待插入元素大的数据后移一位,待插入的元素位置也要往前继续检索即 index–
- 最后把记录下来的待插入元素进插入待插入位置
代码实例
package com.Yjm.sort;
import java.util.Arrays;
/**
* @author 游锦民
* @version 1.0
*/
public class InsertSort {
/*插入排序
* 思路
* 1.创建变量记录待插入元素和待插入元素的前一个位置
* 2.通过循环(条件是需要插入的位置不越界和 待插入的元素满足比较条件,大于或者小于)
* 3.循环体中把比待插入元素大的数据后移一位,待插入的元素位置也要往前继续检索即 index--
* 4.最后把记录下来的待插入元素进插入待插入位置
* */
public static void main(String[] args) {
int[] arr = {11, 34, 119, 1, 33, 10, 45};
insertSort(arr);
/*打印结果
第1趟排序
[11, 34, 119, 1, 33, 10, 45]
第2趟排序
[11, 34, 119, 1, 33, 10, 45]
第3趟排序
[1, 11, 34, 119, 33, 10, 45]
第4趟排序
[1, 11, 33, 34, 119, 10, 45]
第5趟排序
[1, 10, 11, 33, 34, 119, 45]
第6趟排序
[1, 10, 11, 33, 34, 45, 119]
*/
}
public static void insertSort(int[] arr) {
int insertVal = 0;
int insertIndex = 0;
for (int i = 1; i < arr.length; i++) {
//创建变量记录待插入元素和位置
insertVal = arr[i];//因为第一位默认已经排好
insertIndex = i - 1;//记录待插入位置的前一位,就是排好的元素的最后一位
//进行循环遍历比较
//插入值大于已经排好的元素就要比较对吧
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {//insertVal < arr[insertIndex]大于小于决定排序顺序
//能进来循环就满足插入值大于排好的元素哦,那就把排好的元素往后移动
//不用担心被覆盖后面的插入值,因为已经使用 insertIndex = i - 记录下来了
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//可以添加判断是否需要插入,如果已经排好根本不用插入就不再赋值
//就是如果需要插入的位置就是原位 insertIndex+1 == i 就不进行插入
if (insertIndex + 1 != i) {
//把记录下来的插入值赋值给待插的位置
//因为上面的insertIndex--;表示位置已经后移了一位,所以要往前+1才能到达插入的位置
arr[insertIndex + 1] = insertVal;
}
System.out.println("第" + i + "趟排序");
System.out.println(Arrays.toString(arr));
}
}
}
希尔排序
思路(交换法)
1.对有序序列在插入时采用交换法
2.嵌套循环进行交换
3.内嵌的循环最主要是需要交换的两个数进行交换
思路(位移法)
1.同样通过根据增量逐步缩小步长去比较
2.根据增量gap,把gap个元素进行比较并且进行插入
3.比较的条件就是前一个和+gap个比较,小的就后移
4.当然是要循环遍历完整个数组
代码实例
import java.util.Arrays;
public class ShellSort {
/*
* 希尔排序
* 交换法思路
* 1.对有序序列在插入时采用交换法
* 2.嵌套循环进行交换
* 3.内嵌的循环最主要是需要交换的两个数进行交换
*
* */
public static void main(String[] args) {
int[] arr = {9, 6, 7, 1, 5, 3, 0, 2, 4, 8};
shellSort(arr);
int[] ints = new int[800000];
for (int i = 0; i < 800000; i++) {
//遍歷賦值給數組
ints[i] = (int) (Math.random() * 100000);
}
long star = System.currentTimeMillis();
System.out.println("开始时间 " + star / 1000);
//由此可得直接嵌套循环完成趟数
//直接調用
shellSort2(ints);
long end = System.currentTimeMillis();
System.out.println("结束时间 " + end / 1000);
//总运行时间
System.out.println("位移法希尔排序总运行时间:" + (end - star));
}
public static void shellSort(int[] arr) {
//定义临时变量用于交换
int temp = 0;
//通过for循环完成步长条件判断
for (int tag = arr.length / 2; tag > 0; tag = tag / 2) {//循环结束条件是步长不断的缩小一倍
//1.外循环遍历的是每一组
//假如间距是从tag个开始
for (int i = tag; i < arr.length; i++) {//如果是tag个就从tag开始
//内循环遍历每两个数进行交换
for (int j = i - tag; j >= 0; j -= tag) {
//j会随着i的增大而增大
//而j -= tag 代表每次交换的步长
if (arr[j] > arr[j + tag]) {
temp = arr[j];
arr[j] = arr[j + tag];
arr[j + tag] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
/* //接下来就缩短交换步长
for (int i = 2; i < arr.length; i++) {//如果是5个就从5开始
//内循环遍历每两个数进行交换
for (int j = i - 2; j >= 0; j -= 2) {
//j会随着i的增大而增大
//而j -= 5 代表每次交换的步长
if (arr[j] > arr[j + 2]) {
temp = arr[j];
arr[j] = arr[j + 2];
arr[j + 2] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
//接下来就缩短交换步长
for (int i = 1; i < arr.length; i++) {//如果是5个就从5开始
//内循环遍历每两个数进行交换
for (int j = i - 1; j >= 0; j -= 1) {
//j会随着i的增大而增大
//而j -= 5 代表每次交换的步长
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));*/
}
//位移法实现希尔排序
//思路
//1.同样通过根据增量逐步缩小步长去比较
//2.根据增量gap去,把gap个元素进行比较并且进行插入
//3.比较的条件就是前一个和+gap个比较,小的就后移
//4.当然是要循环遍历完整个数组
public static void shellSort2(int[] arr) {
//增量gap,并逐步的缩小增量
for (int gap = arr.length / 2; gap > 0; gap = gap / 2) {//循环结束条件是步长不断的缩小一倍
//从第gap个元素,逐个对其所在的组进行插入排序
for (int i = gap; i < arr.length; i++) {
//定义临时变量和下标存储当前需要比较的元素
int j = i;//临时下标
int temp = arr[j];//存储需要比较的变量
//进行判断
//当前元素和增量步长后的元素比较
//为什么要循环?
//因为使用的是位移法
//如果满足条件就把后面的元素往前移动进行覆盖
//因为需要插入的元素已经保存到temp中了
//移动 的条件 需要移动的数组元素下标不越界并且记录下来的临时变量
if (arr[j] < arr[j - gap])
while (j - gap >= 0 && temp < arr[j - gap]) {
//满足条件进行位移
arr[j] = arr[j - gap];
//位移之后temp < arr[j - gap]就跳出循环
//然后进行
//进行j-=gap,因为上面已经把j下标该位置赋值了j - gap上的元素,而j-gap这个位置还没交换过来,所以j-gap就能把j的下标得到j -gap的值
//然后下面的循环外面就可以赋值
j -= gap;
}
//当退出while循环就把记录temp赋值给arr[j]
arr[j] = temp;
}
}
// System.out.println(Arrays.toString(arr));
}
}
快速排序
代码实例
package com.Yjm.sort;
import java.util.Arrays;
/**
* @author 游锦民
* @version 1.0
*/
public class QuickSort {
//思路:
//1.通过中间值来交换左右的值
//2.当左大于就交换到右边,右边同理
//3.最后通过中间值的左右各自分别递归调用本身,再去交换顺序就能得到最后的有序
public static void main(String[] args) {
int[] arr = {0, 1, 2, 2, 3, 3, 4, 3, 6, 7, 8, 9};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int left, int right) {//参数传递数组,左右值和中间值?
//具体如何交换?
//从中间值可以往两边遍历依次比较
//定义变量获取左右下标
int l = left;
int r = right;
//定义中轴值
//中轴如何获取?
//获取数组的左右下标的和除以2 对应的元素即可
int prvot = arr[(left + right) / 2];
//如何比较?
//循环遍历左右比较
while (l < r) {//当左右下标没有越界证明能够正常遍历比较
//先从中轴的左边开始遍历,依次和右边的中轴值比较
while (arr[l] < prvot) {//就是当这个条件不满足就表示找到了 左边的元素大于中轴值
//如果没有找到就递增下标
l++;
}
//找完左边找右边
while (arr[r] > prvot) {//就是当这个条件不满足就表示找到了 右边的元素小于中轴值
//如果没有找到就递增下标
r--;
}
//都交换完之后就跳出整个循环
//如果左右下标都遍历到中轴了或者越界就表示不能再进行循环交换
if (l >= r) {
break;
}
//上面两个循环如果被跳出表示找到对应的元素
//就进行元素之间交换
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//交换的之后如果发现该值和中轴值相等就不用进行交换,直接遍历下一个
//就是为了防止左右有多个值和中轴相同进入死循环
//这里是防止左边和右边的数和中间值一样,你不继续往后走,会一直循环前面的代码
if (arr[l] == prvot) {
//但是为什么是r--呢?而不是l++?
//因为能到这一步表示左边的值是和中轴相等的,所以没必要动它
r--;
}
if (arr[r] == prvot) {
l++;
}
}
//当比较完之后就递归调用本身再分别比较左右两边
//如果l==r,必须让这两个错开,l++,r--,否则出现栈溢出
//如果左右相等的话,就不会满足上面while循环会一直递归调用本身,所以才导致栈溢出
if (l == r) {
l++;
r--;
}
//向左递归
if (left < r) {//当左边的下标小于已经遍历过来的右边的下标,就进行递归调用,因为r是会一直遍历向左边移动
quickSort(arr, left, r);
}
//向右边递归
if (right > l) {//当右边的下标大于左边的下标才进行递归
quickSort(arr, l, right);
}
}
}
归并排序
思路
分:把一组数据递归拆分,直到每一组只有一个元素为止。
治:将已经有序的子序列合并成一个有序序列,两者一一比较,小的就放到一个中转的数组当中,接着后移遍历比较后面的元素。合并次数就是元素个数-1
代码实例
public class MergetSort {
public static void main(String[] args) {
int[] arr = {11, 34, 119, 1, 33, 10, 45};
int[] temp = new int[arr.length];
mergeSort(arr, 0, arr.length - 1, temp);
System.out.println(Arrays.toString(arr));
}
//分+和方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
//获取中间索引
int mid = (left + right) / 2;
//向左递归分解
//右边有序下标传递的就是mid中间值,会随着中间索引改变而改变,作为递归的出口
mergeSort(arr, left, mid, temp);
//向右递归分解
mergeSort(arr, mid + 1, right, temp);
//分解完之后就进行排序合并
merge(arr, left, mid, right, temp);
}
}
/**
* 合并方法
*
* @param arr 排序数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 中转的临时数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;//左边有序序列的初始化索引
int j = mid + 1;//右边有序序列的初始化索引
int t = 0;//指向temp数组的当前索引
//一.先把左右两边有序的数据按照规则填充到temp数组
//直到左右两边的有序序列有一边处理完毕为止
while (i <= mid && j <= right) {//循环遍历的条件就是左边的索引还没到mid中奖索引和右边的有序索引没有到最右边的索引
//进行条件判断
//如满足左边小于右边就进行填充到temp数组
if (arr[i] < arr[j]) {
temp[t] = arr[i];
//接着temp数组下标就要后移
t++;
//而用于填充的元素下标也要后移
i++;
} else {//反之,当右边小的话就把右边填充
temp[t] = arr[j];
t++;
j++;
}
}
//接着把剩余的元素全部拷贝到temp数组当中
while (i <= mid) {//左边的下标还没有到中间索引,说明左边还有元素
temp[t] = arr[i];
t++;
i++;
}
//也有可能是右边有剩余元素,同样需要拷贝到temp数组
while (j <= right) {
temp[t] = arr[j];
t++;
j++;
}
//注意 : 当你上面的流程都执行完才下来到这一步的
//三.将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t = 0;//临时数组就能直接从下标0开始进行拷贝
int tempLeft = left;//left是左边有序的初始化索引,没有随着递归调用会发生改变,但是肯定是在arr数组中的开头
while (tempLeft <= right) {//当左边数组下标没有到最右边(即数组不越界)就满足拷贝条件
arr[tempLeft] = temp[t];//第一次递归合并tempLeft= 0,right =1;
t++; //循环遍历第二次tempLeft = 2; right =3;
//....
tempLeft++; //最后一次tempLeft = 0; right =7;//因为递归用,当比较完合并完所有元素才会拷贝所有
}
}
}
基数排序
基数排序(桶排序)介绍
- 基数排序(radixsort) 属于“分配式排序”(distributionsort) ,又称“桶子法”(bucket sort)或binsort, 顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
- 基数排序法是属开稳定性的排序,基数排序法的是效率高的稳定性排序法
- 基数排序(Radix Sort)是桶排序的扩展
- 基数排序是1887年赫尔曼何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
说明
- 基数排序是对于传统桶排序的扩展,速度很快
- 基数排序是经典的空间换时间的方式,占用内存很大,当对海量数据排序容器造成OutOfMemoryError
- 基数排序是稳定的。注 :假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[i], 且r[j在r(i]之前, 而在排序后的序列中,r[]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]
- 有负数的数组,我们不用基数排序来进行排序,有需要可以参考其他文章。
基数排序基本思想
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dlrg1Y4O-1680417774394)(D:\Java学习\Typora笔记\笔记图片\image-20230401232236001.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6iq9VfoA-1680417774395)(D:\Java学习\Typora笔记\笔记图片\image-20230401232258384.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R5QHip6X-1680417774396)(D:\Java学习\Typora笔记\笔记图片\image-20230401232325382.png)]
代码实例(推导实现)
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int arr[] = {53, 3, 542, 748, 14, 214};
radixSort(arr);
/*
打印结果
第一轮排序后的数组arr:[542, 53, 3, 14, 214, 748]
第二轮排序后的数组arr:[3, 14, 214, 542, 748, 53]
第三轮排序后的数组arr:[3, 14, 53, 214, 542, 748]
*/
}
//基数排序方法(推导方法)
public static void radixSort(int[] arr) {
//定义一个二维数组表示10个桶,每个桶就是一个一维数组
//1.二维数组表示10个桶,因此数组行下标(第一个)就设置为[10]
//2.二维数组中的列下标(第二个)[arr.length]表示每一个一维数组即每一个桶存放的元素
//2.1因为有可能一组元素中可能发生对应的位数全部相同,所以为了防止数据溢出,就把每一个一维数组大小定为[arr.length]
//注意:基数排序就是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//创建一维数组记录每一个桶每次存放的数据个数
//如何理解?
//1.该数组长度为10是表示有10个桶,而数组中的下标对应就是桶的序号
//2.而每一个下标存放的元素就是,对应的桶存放的数据个数
//如:bucketElementCounts[0]的元素, 记录就是桶0 bucket[0]放入的的数据个数
int[] bucketElementCounts = new int[10];
//第1轮(针对每个元素的个位进行排序)
for (int i = 0; i < arr.length; i++) {
//取出每个元素的个位值
//该元素对10取模即可,得到的余数就是十位数
int dightOfElment = arr[i] % 10;
//放入对应的桶中
//这里要好好理解
//1.第一个行下标[dightOfElment]代表就是10个桶中对应的桶,假如上面的取模得到的余数是1,得到就是桶1
//2.第二个列下标[bucketElementCounts[dightOfElment]]表示就是对应的桶要存放的元素arr[i],例如桶1的数组下标0,就是存放arr[i],
// 2.1 可能不理解为什么桶1的下标是从0开始,因为上面的int[] bucketElementCounts = new int[10];没有进行赋值就全部下标的元素默认赋值为0
//3.bucketElementCounts[dightOfElment]表示该桶存放的元素个数,例如得到的dightOfElment=1,那么就表示这个就是桶1的元素个数
bucket[dightOfElment][bucketElementCounts[dightOfElment]] = arr[i];
//4. 每放入一个元素接着就对桶中的元素记录值递增1
bucketElementCounts[dightOfElment]++;
}
//遍历所有桶中的元素依次放入到原来数组
int index = 0;
//根据桶的个数去遍历,bucketElementCounts的长度就是桶的个数
for (int i = 0; i < bucketElementCounts.length; i++) {
//判断桶中是否有数据.如果有就放入到原数组
if (bucketElementCounts[i] != 0) {
//循环该i桶的元素
//bucketElementCounts[i]表示该桶bucket[i][j]当前拥有的元素个数
for (int j = 0; j < bucketElementCounts[i]; j++) {
//依次取出元素放入到arr数组中
arr[index++] = bucket[i][j];
}
}
//每一轮处理之后要对每个记录的桶的元素个数归0
bucketElementCounts[i] = 0;
}
System.out.println("第一轮排序后的数组arr:" + Arrays.toString(arr));
//第2轮(针对每个元素的个位进行排序)同理
for (int i = 0; i < arr.length; i++) {
//取出每个元素的十位值
//该元素除以100得到百位再对10取模即可,得到的余数就是百位数
int dightOfElment = arr[i] / 10% 10;
//放入对应的桶中
//这里要好好理解
//1.第一个行下标[dightOfElment]代表就是10个桶中对应的桶,假如上面的取模得到的余数是1,得到就是桶1
//2.第二个列下标[bucketElementCounts[dightOfElment]]表示就是对应的桶要存放的元素arr[i],例如桶1的数组下标0,就是存放arr[i],
// 2.1 可能不理解为什么桶1的下标是从0开始,因为上面的int[] bucketElementCounts = new int[10];没有进行赋值就全部下标的元素默认赋值为0
//3.bucketElementCounts[dightOfElment]表示该桶存放的元素个数,例如得到的dightOfElment=1,那么就表示这个就是桶1的元素个数
bucket[dightOfElment][bucketElementCounts[dightOfElment]] = arr[i];
//4. 每放入一个元素接着就对桶中的元素记录值递增1
bucketElementCounts[dightOfElment]++;
}
//遍历所有桶中的元素依次放入到原来数组
index = 0;
//根据桶的个数去遍历,bucketElementCounts的长度就是桶的个数
for (int i = 0; i < bucketElementCounts.length; i++) {
//判断桶中是否有数据.如果有就放入到原数组
if (bucketElementCounts[i] != 0) {
//循环该i桶的元素
//bucketElementCounts[i]表示该桶bucket[i][j]当前拥有的元素个数
for (int j = 0; j < bucketElementCounts[i]; j++) {
//依次取出元素放入到arr数组中
arr[index++] = bucket[i][j];
}
}
//每一轮处理之后要对每个记录的桶的元素个数归0
bucketElementCounts[i] = 0;
}
System.out.println("第二轮排序后的数组arr:" + Arrays.toString(arr));
//第3轮(针对每个元素的个位进行排序)同理
for (int i = 0; i < arr.length; i++) {
//取出每个元素的十位值
//该元素除以10得到百位和十位再对10取模即可,得到的余数就是个位数
int dightOfElment = arr[i] / 100 % 10;
//放入对应的桶中
//这里要好好理解
//1.第一个行下标[dightOfElment]代表就是10个桶中对应的桶,假如上面的取模得到的余数是1,得到就是桶1
//2.第二个列下标[bucketElementCounts[dightOfElment]]表示就是对应的桶要存放的元素arr[i],例如桶1的数组下标0,就是存放arr[i],
// 2.1 可能不理解为什么桶1的下标是从0开始,因为上面的int[] bucketElementCounts = new int[10];没有进行赋值就全部下标的元素默认赋值为0
//3.bucketElementCounts[dightOfElment]表示该桶存放的元素个数,例如得到的dightOfElment=1,那么就表示这个就是桶1的元素个数
bucket[dightOfElment][bucketElementCounts[dightOfElment]] = arr[i];
//4. 每放入一个元素接着就对桶中的元素记录值递增1
bucketElementCounts[dightOfElment]++;
}
//遍历所有桶中的元素依次放入到原来数组
index = 0;
//根据桶的个数去遍历,bucketElementCounts的长度就是桶的个数
for (int i = 0; i < bucketElementCounts.length; i++) {
//判断桶中是否有数据.如果有就放入到原数组
if (bucketElementCounts[i] != 0) {
//循环该i桶的元素
//bucketElementCounts[i]表示该桶bucket[i][j]当前拥有的元素个数
for (int j = 0; j < bucketElementCounts[i]; j++) {
//依次取出元素放入到arr数组中
arr[index++] = bucket[i][j];
}
}
//每一轮处理之后要对每个记录的桶的元素个数归0
bucketElementCounts[i] = 0;
}
System.out.println("第三轮排序后的数组arr:" + Arrays.toString(arr));
}
}
代码实例(最终实现)
import java.util.Arrays;
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];
}
}
//验证最大的数字是几位数
//max+"" 转换成字符串之后获取字符串长度
int maxLenght = (max + "").length();
//定义一个二维数组表示10个桶,每个桶就是一个一维数组
//1.二维数组表示10个桶,因此数组行下标(第一个)就设置为[10]
//2.二维数组中的列下标(第二个)[arr.length]表示每一个一维数组即每一个桶存放的元素
//2.1因为有可能一组元素中可能发生对应的位数全部相同,所以为了防止数据溢出,就把每一个一维数组大小定为[arr.length]
//注意:基数排序就是使用空间换时间的经典算法
int[][] bucket = new int[10][arr.length];
//创建一维数组记录每一个桶每次存放的数据个数
//如何理解?
//1.该数组长度为10是表示有10个桶,而数组中的下标对应就是桶的序号
//2.而每一个下标存放的元素就是,对应的桶存放的数据个数
//如:bucketElementCounts[0]的元素, 记录就是桶0 bucket[0]放入的的数据个数
int[] bucketElementCounts = new int[10];
//循环每一轮比较
// maxLenght表示有多少位数,就代表进行多少轮
//n是为了根据数字的步长递增获取位数
for (int k = 0, n = 1; k < maxLenght; k++, n *= 10) {
//每一轮比较(针对每个元素的个位进行排序)
//针对每一个数进行排序处理,依次的个十百千万
for (int i = 0; i < arr.length; i++) {
//取出每个元素的对应的位值
int dightOfElment = arr[i] / n % 10;
//放入对应的桶中
//这里要好好理解
//1.第一个行下标[dightOfElment]代表就是10个桶中对应的桶,假如上面的取模得到的余数是1,得到就是桶1
//2.第二个列下标[bucketElementCounts[dightOfElment]]表示就是对应的桶要存放的元素arr[i],例如桶1的数组下标0,就是存放arr[i],
// 2.1 可能不理解为什么桶1的下标是从0开始,因为上面的int[] bucketElementCounts = new int[10];没有进行赋值就全部下标的元素默认赋值为0
//3.bucketElementCounts[dightOfElment]表示该桶存放的元素个数,例如得到的dightOfElment=1,那么就表示这个就是桶1的元素个数
bucket[dightOfElment][bucketElementCounts[dightOfElment]] = arr[i];
//4. 每放入一个元素接着就对桶中的元素记录值递增1
bucketElementCounts[dightOfElment]++;
}
//遍历所有桶中的元素依次放入到原来数组
int index = 0;
//根据桶的个数去遍历,bucketElementCounts的长度就是桶的个数
for (int i = 0; i < bucketElementCounts.length; i++) {
//判断桶中是否有数据.如果有就放入到原数组
if (bucketElementCounts[i] != 0) {
//循环该i桶的元素
//bucketElementCounts[i]表示该桶bucket[i][j]当前拥有的元素个数
for (int j = 0; j < bucketElementCounts[i]; j++) {
//依次取出元素放入到arr数组中
arr[index++] = bucket[i][j];
}
}
//每一轮处理之后要对每个记录的桶的元素个数归0
bucketElementCounts[i] = 0;
}
System.out.println("第" + (k + 1) + "轮排序后的数组arr:" + Arrays.toString(arr));
}
}
}