9.排序算法

1.冒泡排序

基本介绍:

  冒泡排序的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),一次比较相邻元素的值,如果发现逆序,使较大的元素逐渐从前向后移动。

 

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

示例:

public class BubbleSort {
    public static void main(String[] args) {
        int arr[] = {11,3, 9, -1, 10, -2};
        int count=0;
        //冒泡排序
        //第一层for循环是循环次数:例如5个数,总共循环5-1=4次
        System.out.println("原始数据: "+Arrays.toString(arr));
        System.out.println("开始排序------");
        for (int i = 0; i < arr.length - 1; i++) {
            //第二层for循环,是比较次数
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int num=arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1] = num;
                }
                System.out.println("第"+(count++)+"次排序:"+Arrays.toString( arr));
            }
            System.out.println("内层排序结束---");
        }
    }
}

输出:

原始数据: [11, 3, 9, -1, 10, -2]
开始排序------
第0次排序:[3, 11, 9, -1, 10, -2]
第1次排序:[3, 9, 11, -1, 10, -2]
第2次排序:[3, 9, -1, 11, 10, -2]
第3次排序:[3, 9, -1, 10, 11, -2]
第4次排序:[3, 9, -1, 10, -2, 11]
内层排序结束---
第5次排序:[3, 9, -1, 10, -2, 11]
第6次排序:[3, -1, 9, 10, -2, 11]
第7次排序:[3, -1, 9, 10, -2, 11]
第8次排序:[3, -1, 9, -2, 10, 11]
内层排序结束---
第9次排序:[-1, 3, 9, -2, 10, 11]
第10次排序:[-1, 3, 9, -2, 10, 11]
第11次排序:[-1, 3, -2, 9, 10, 11]
内层排序结束---
第12次排序:[-1, 3, -2, 9, 10, 11]
第13次排序:[-1, -2, 3, 9, 10, 11]
内层排序结束---
第14次排序:[-2, -1, 3, 9, 10, 11]
内层排序结束---

结论:发现每次排序都会把最大数放在结尾处!

 

优化,先看案例:

如果给int arr[] = {1, 2, 3, 5, 4}排序呢
输出:
    原始数据: [1, 2, 3, 5, 4]
    开始排序------
    第0次排序:[1, 2, 3, 5, 4]
    第1次排序:[1, 2, 3, 5, 4]
    第2次排序:[1, 2, 3, 5, 4]
    第3次排序:[1, 2, 3, 4, 5]
    内层排序结束---
    第4次排序:[1, 2, 3, 4, 5]
    第5次排序:[1, 2, 3, 4, 5]
    第6次排序:[1, 2, 3, 4, 5]
    内层排序结束---
    第7次排序:[1, 2, 3, 4, 5]
    第8次排序:[1, 2, 3, 4, 5]
    内层排序结束---
    第9次排序:[1, 2, 3, 4, 5]
    内层排序结束---

发现3-9次的排序均一样的,这就是优化的点!
    public static void main(String[] args) {
        int arr[] = {1, 2, 3, 5, 4};
        int count = 0;
        //冒泡排序
        //第一层for循环是循环次数:例如5个数,总共循环5-1=4次
        System.out.println("原始数据: " + Arrays.toString(arr));
        System.out.println("开始排序------");
        //重点1:表示是否进行过交换
        boolean flag = false;
        for (int i = 0; i < arr.length - 1; i++) {
            //第二层for循环,是比较次数
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int num = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = num;
                    //重点2:如果发生交换,该字段置为true;
                    flag = true;
                }
                System.out.println("第" + (count++) + "次排序:" + Arrays.toString(arr));
            }
            //内层排序结束后,判断这个字段
            //没有进行过交换,直接退出!
            if (!flag) {
                break;
            } else {
                //flag=true:进行过交换,这里需要重置下flag,方便下次判断!
                flag = false;
            }
            System.out.println("内层排序结束---");
        }
    }
    
输出:发现减少了3次排序
    原始数据: [1, 2, 3, 5, 4]
    开始排序------
    第0次排序:[1, 2, 3, 5, 4]
    第1次排序:[1, 2, 3, 5, 4]
    第2次排序:[1, 2, 3, 5, 4]
    第3次排序:[1, 2, 3, 4, 5]
    内层排序结束---
    第4次排序:[1, 2, 3, 4, 5]
    第5次排序:[1, 2, 3, 4, 5]
    第6次排序:[1, 2, 3, 4, 5]
 内层排序无法减少,即3-6的排序是无法精简的,因为内层排序是元素一位一位去比较,根本不知道后面元素的大小,两次比较结果相同,并不能说明已经有序:如
 本以为这么修改:
         //上次排序结果
        String str="";
        for (int i = 0; i < arr.length - 1; i++) {
            //第二层for循环,是比较次数
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int num = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = num;
                }
                //重点1:比较上次移动和这次的字符串是否一样
                if (str.equals(Arrays.toString(arr))) {
                    break;
                }
               str=Arrays.toString(arr);
                System.out.println("第"+(count++)+"次排序:"+str);
            }
            System.out.println("内层排序结束---");
        }
输出;
原始数据: [1, 2, 3, 5, 4]
开始排序------
第0次排序:[1, 2, 3, 5, 4]
内层排序结束---
    
结论:    
这时还没有有序,内层排序无法减少优化!

效率问题:

  因为冒泡排序的事件复杂度为O(n²),所以其事件复杂度会随着n的增大而快速增大!

  如果新增80000个随机数,按照冒泡法排序耗时问题:

添加80000个随机数,进行冒泡排序:
     public static void main(String[] args) {
        int arr[] = new int[80000];
        int count = 0;
        boolean flag = false;
        for (int i=0;i<arr.length; i++){
            arr[i]= (int) (Math.random()*arr.length);
        }
        Date date1=new Date();
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int num = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = num;
                    flag = true;
                }
            }
            if (!flag) {
                break;
            } else {
                flag = false;
            }
        }
        Date date2=new Date();
        System.out.println("结束排序,耗时:"+(date2.getTime()-date1.getTime()));
    }
    
输出:耗时13秒多,
    结束排序,耗时:13429

2.选择排序

基本介绍:

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

选择排序的思想:

  选择排序也是一种简单的排序方法,他的基本思想是:

  1. 第一次从arr[0]-arr[n-1]中取最小值,与arr[0]交换,

  2. 第二次从arr[1] -arr[n-1]中取最小值,与arr[1]交换

  3. 第三次从arr[2]-arr[n-1]中取最小值,与arr[2]交换

  4.....

  5. 第n-1次从arr[n-2]-arr[n-1]中选择最小值,与arr[n-2]交换,

  总共交换n-1次,得到一个按照排序从小到大排列的有序序列

 

public class SelectSort {
    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 123};
        //最小数
        int min = 0;
        //最小数的下标
        int minIndex = 0;
        System.out.println("原始:" + Arrays.toString(arr));
        for (int i = 0; i < arr.length; i++) {
            //这里注意:将最小值和最小下标重新赋值!
            min = arr[i];
            minIndex = i;
            for (int j = i; j < arr.length; j++) {
                if (arr[j] < min) {
                    min = arr[j];
                    minIndex = j;
                }
            }
            //不相等时,即发生了排序
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
            System.out.println("第" + i + "次排序结果:" + Arrays.toString(arr));
        }
        System.out.println("排序结果:"+Arrays.toString(arr));
    }
}
输出:
原始:[101, 34, 119, 123]
第0次排序结果:[34, 101, 119, 123]:发现找到最小值34和第一个元素101互换位置
第1次排序结果:[34, 101, 119, 123]:找到次小值101和第二个元素自己
第2次排序结果:[34, 101, 119, 123]:找到第三小值119
第3次排序结果:[34, 101, 119, 123]
排序结果:[34, 101, 119, 123]

选择排序耗时:

public class SelectSort {
    public static void main(String[] args) {
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        Date date1=new Date();
        sort(arr);
        Date date2=new Date();
        System.out.println("选择排序耗时:"+(date2.getTime()-date1.getTime()));
        System.out.println("排序结果:"+Arrays.toString(arr));

    }

    /**
     * 排序
     *
     * @param arr 排序数组
     */
    public static void sort(int[] arr) {
        //最小数
        int min = 0;
        //最小数的下标
        int minIndex = 0;
        for (int i = 0; i < arr.length; i++) {
            //这里注意:将最小值和最小下标重新赋值!
            min = arr[i];
            minIndex = i;
            for (int j = i; j < arr.length; j++) {
                if (arr[j] < min) {
                    min = arr[j];
                    minIndex = j;
                }
            }
            //不相等时,即发生了排序
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }
}
测试输出:发现选择排序的耗时要比冒泡排序快了不知道多少!!!
    选择排序耗时:2583
排序结果:[0, 1, 1, 1, 2, 2, 3, 5, 6, 7, 9, 9, 10, 11, 12, 14, 14, 15, 15, ....79998]

 3.插入排序

简介:

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

插入排序的思想:

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

插入排序代码:

/**
 * 插入排序
 */
public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {17, 3, 1, 4, 20, 9};
        System.out.println("原始数据:"+Arrays.toString(arr));
        sort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        /*
            插入排序的基本思想是:
                1.把n个待排序的元素看成为一个有序表和一个无序表,
                2.开始时有序表中只有一个元素,无序表中包含n-1个元素,
                3.排序过程中每次从无序表中取出一个元素,把它的值和有序表中的值进行比较,
                4.将它插入到有序表真娘怪的适当位置,使之成为新的有序表
         */
        int count = 0;
        for (int i = 1; i < arr.length; i++) {
            //插入的数据
            int insertVal = arr[i];
            //插入数据的前一列
            int insertIndex = i - 1;
            //这里的操作是找到插入位置,并将位置后面的收据全部后移一位!
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
                System.out.println(Arrays.toString(arr));
            }
            arr[insertIndex + 1] = insertVal;
            System.out.println("第" + (++count) + "次排序:" + Arrays.toString(arr));
        }
    }
}

输出:
原始数据:[17, 3, 1, 4, 20, 9]
[17, 17, 1, 4, 20, 9]
第1次排序:[3, 17, 1, 4, 20, 9]
[3, 17, 17, 4, 20, 9]
[3, 3, 17, 4, 20, 9]
第2次排序:[1, 3, 17, 4, 20, 9]
[1, 3, 17, 17, 20, 9]
第3次排序:[1, 3, 4, 17, 20, 9]
第4次排序:[1, 3, 4, 17, 20, 9]
[1, 3, 4, 17, 20, 20]
[1, 3, 4, 17, 17, 20]
第5次排序:[1, 3, 4, 9, 17, 20]
[1, 3, 4, 9, 17, 20]

1.

2.

3.

测试速率:

public static void main(String[] args) {
    int[] arr = new int[80000];
    for (int i = 0; i < arr.length; i++) {
        arr[i] = (int) (Math.random() * 80000);
    }
    Date date1=new Date();
    sort(arr);
    Date date2=new Date();
    System.out.println("选择排序耗时:"+(date2.getTime()-date1.getTime()));
    System.out.println("排序结果:"+Arrays.toString(arr));
}
输出:
选择排序耗时:1061
排序结果:[0, 1, 1, 2, 2, 3,.....79999]

可以在赋值的地方做个优化:

 for (int i = 1; i < arr.length; i++) {
            //插入的数据
            int insertVal = arr[i];
            //插入数据的前一列
            int insertIndex = i - 1;
            //这里的操作是找到插入位置,并将位置后面的收据全部后移一位!
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
             //重点1:这里做个优化,当insertIndex+1=i,即这个数就在合适位置,如
             //{1,5,6,9}给9排序时,发现9是合适的位置:insertIndex+1=i,不用替换
            if (insertIndex + 1 != i) {
                arr[insertIndex + 1] = insertVal;
            }
        }

 

4.希尔排序(较快)

介绍:

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

思想:

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

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

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

交换式的希尔排序
public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        System.out.println("原始数组为:" + Arrays.toString(arr));
        sort(arr);
    }

    public static void sort(int[] arr) {
        int temp = 0;
        int count = 0;
        /*
            1.将数组按照length/2进行分组,对每个分组进行排序,如数组有10个数据,
                第一次分5组,每组两个元素,这两个元素进行排序
            2.五组排序结束后,在分为2组,即length/2/2,再进行插入排序
            3.直到剩余一组后,进行最后一次排序
         */
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            //定义好了分组,gap就是步长,即arr[i]和arr[i+gap]分为一组
            //每个分组都循环一次
            System.out.println("-------------------------");
            for (int i = gap; i < arr.length; i++) {
                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));
        }
    }
}
输出:
    原始数组为:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
    -------------------------
    外层排序:[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
    -------------------------
    外层排序:[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
    -------------------------
    外层排序:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

1.将元素分为10/2=5组进行循环比较

最里层的for循环类似于冒泡排序,把较大的数移到后面!

速率测试
 public static void main(String[] args) {
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        Date date1 = new Date();
        sort(arr);
        Date date2 = new Date();
        System.out.println("选择排序耗时:" + (date2.getTime() - date1.getTime()));
        System.out.println("排序结果:" + Arrays.toString(arr));
    }
    
输出:
    希尔排序耗时:7262
    排序结果:[0, 1, 3,...79997]

移位法:

 public static void sort2(int[] arr) {
        /*
            1.将数组按照length/2进行分组,对每个分组进行排序,如数组有10个数据,
                第一次分5组,每组两个元素,这两个元素进行排序
            2.五组排序结束后,在分为2组,即length/2/2,再进行插入排序
            3.直到剩余一组后,进行最后一次排序
         */
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            //从第gap个元素,诸葛对其所在的组进行直接排序
            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;
                    }
                    arr[j] = temp;
                }
            }
        }
    }

测试速率:
    选择排序耗时:49
    希尔结果:[0, 0, 1, 2, ... 79997, 79999]

5.快速排序(很快)

简介:

  快速排序是对冒泡排序的一种改进

基本思想:

  通过一趟排序。将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,

  然后再按次方法对两部分的数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变得有序

  

逻辑分析:

  1.第一次循环

2.第二次循环

3.这时只需要递归中介值前面和后面两个数组,都按照这种方式处理即可

样例代码

public class QuickSort {
    public static void main(String[] args) {
        int[] arr={19,97,9,17,1,8};
        sort(arr, 0, arr.length-1);
        System.out.println("排序结果:"+Arrays.toString(arr));
    }
    public static void sort(int[] arr, int L, int R) {
        //当左右索引相同时,排序结束,左边数组普遍右边
        if (L >= R) {
            return;
        }
        int left = L, right = R;
        int pivot = arr[left];
        System.out.println("原始数据:"+Arrays.toString(arr));
        while (left < right) {
            System.out.println("--------------:中介值:"+pivot);
            while (left < right && arr[right] >= pivot) {
                right--;
            }
            if (left < right) {
                arr[left] = arr[right];
            }
            System.out.println(Arrays.toString(arr));
            while (left < right && arr[left] <= pivot){
                left++;
            }
            if (left<right){
                arr[right]=arr[left];
            }
            if (left>=right){
                arr[left]=pivot;
            }
            System.out.println(Arrays.toString(arr));
        }
        sort(arr,L,right-1);
        sort(arr,right+1,R);
    }
}
测试输出:符合预期
原始数据:[19, 97, 9, 17, 1, 8]
--------------:中介值:19
[8, 97, 9, 17, 1, 8]
[8, 97, 9, 17, 1, 97]
--------------:中介值:19
[8, 1, 9, 17, 1, 97]
[8, 1, 9, 17, 19, 97]
原始数据:[8, 1, 9, 17, 19, 97]
--------------:中介值:8
[1, 1, 9, 17, 19, 97]
[1, 8, 9, 17, 19, 97]
原始数据:[1, 8, 9, 17, 19, 97]
--------------:中介值:9
[1, 8, 9, 17, 19, 97]
[1, 8, 9, 17, 19, 97]
排序结果:[1, 8, 9, 17, 19, 97]

测试速率

 public static void main(String[] args) {
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        Date date1 = new Date();
        sort(arr,0,arr.length-1);
        Date date2 = new Date();
        System.out.println("选择排序耗时:" + (date2.getTime() - date1.getTime()));
        System.out.println("排序结果:" + Arrays.toString(arr));
    }

测试输出:
    选择排序耗时:34
    排序结果:[0, 1, 5, 5, 5, 5, 6, 6, 8, 8, 11,...79998]

6.归并排序

简介:

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

代码示例:将归并排序分为两部分
1.分:将数组递归分为独立的个体
2.治:将每个独立的个体排序后再合在一起
/**
 * 归并排序
 */
public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
        mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
    }

    /**
     * 1.分
     *
     * @param arr
     * @param left
     * @param right
     * @param temp
     */
    public static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right) {
            int middle = (left + right) / 2;
            //向左继续递归
            mergeSort(arr, left, middle, temp);
            //向右继续递归
            mergeSort(arr, middle + 1, right, temp);
            merge(arr, left, middle, right, temp);
        }
    }

    /**
     * 2.治
     *
     * @param arr    需要排序的数组
     * @param left   数组最左边:原始为0
     * @param middle 中间索引:左边数组的末尾
     * @param right  数组最右边:原始为arr.length-1
     * @param temp   中转数组
     */
    public static void merge(int[] arr, int left, int middle, int right, int[] temp) {
        //左边索引
        int i = left;
        //右边索引
        int j = middle + 1;
        //指向中转数组temp的索引
        int t = 0;
        /*
            1:先把左右两边有序的数据按照规则填充到temp数组中
            直到左右两边的有序序列,有一边处理完毕
         */
        while (i <= middle && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t += 1;
                i += 1;
            } else {
                temp[t] = arr[j];
                t += 1;
                j += 1;
            }
        }
        /*
            有一边处理完毕,但是领另外一边有剩余元素
         */
        //左边有剩余
        while (i <= middle) {
            temp[t] = arr[i];
            i += 1;
            t += 1;
        }
        //右边有剩余
        while (j <= right) {
            temp[t] = arr[j];
            j += 1;
            t += 1;
        }
        /**
         * 将temp数据拷贝到arr中
         * 注意不是每次都拷贝所有
         */
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right) {
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
            System.out.println("---------------");
            System.out.println(":temp:" + Arrays.toString(temp));
            System.out.println(":arr:" + Arrays.toString(arr));
        }
    }
}

1.

2.

 

速率测试
    public static void main(String[] args) {
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        Date date1 = new Date();
        mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
        Date date2 = new Date();
        System.out.println("选择排序耗时:" + (date2.getTime() - date1.getTime()));
        System.out.println("排序结果:" + Arrays.toString(arr));
    }
    
输出:
    选择排序耗时:24
    排序结果:[3, 4, 4..., 79999]

7.基数排序(快速)

介绍

1.基数排序属于分配式排序,又称桶子法,他是通过键值的各个位的值,将要排序的元素分配至某些桶中,达到排序的效果。

2.基数排序法属于稳定性的排序,基数排序法是效率高的稳定性排序法

3.基数排序是桶排序的扩展。

4.金术培训是1887年,霍尔曼.何乐发明的,他是这样实现:将整数按照位数切割成不同的数字,然后按每一个位数分别比较

思想

  1.将所有带比较数值统一为同样的数位长度,数位较短的数前面补零,然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列

  

 

public class RadixSort {
    public static void main(String[] args) {
        int[] arr={53,3,542,748,14,214};
        System.out.println("原始数组:"+Arrays.toString(arr));
        sort(arr);
        System.out.println("排序结果:"+ Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        int count=1;
        /*
         * 定义一个二维数组
         * 1.二维数组包含10个一维数组
         * 2,为了防止放入数时,数据溢出,定义一个一位数组,大小为arr.length里面放着二维数组中的数据数量
         * 3.基数排序是使用空间换时间的经典算法
         */
        int[][] bucket = new int[9][arr.length];
        //定义一个一维数组,存放二维数组中数据的大小
        int[] bucketElementCounts = new int[10];
        /*
            1.获取所有元素中最大的数,因为最大的数有记为就循环几次
         */

        int maxNum = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > maxNum) {
                maxNum = arr[i];
            }
        }
        //循环次数
        int cyclesNum = (maxNum + "").length();
        for (int i = 0, n = 1; i < cyclesNum; i++, n *= 10) {
            /*
                注意:例如542
                    1.当第一次循环取个位数:542%10=2
                    2.当第二次循环取十位数:(542/10)%10=4
                    3.当第三次循环取百位数:(542/10/10)%10=5
             */
            for (int j = 0; j < arr.length; j++) {
                int num = arr[j] / n % 10;
                /*
                    这里细品下
                 */
                bucket[num][bucketElementCounts[num]] = arr[j];
                bucketElementCounts[num]+=1;
            }
            /*
                2.已将数据放入到这10个数组中,现在依次将其拿出放入到arr中即可
             */
            int index = 0;
            for (int m = 0; m < bucketElementCounts.length; m++) {
                if (bucketElementCounts[m] > 0) {
                    for (int y = 0; y < bucketElementCounts[m]; y++) {
                        arr[index++] = bucket[m][y];
                    }
                }
                /*
                    3.最后一步,需要将bucketElementCounts的所有元素置为0
                 */
                bucketElementCounts[m]=0;
            }
            System.out.println("中间第"+(count++)+"次排序结果:"+Arrays.toString(arr));
        }
    }
}
输出:
    原始数组:[53, 3, 542, 748, 14, 214]
    中间第1次排序结果:[542, 53, 3, 14, 214, 748]
    中间第2次排序结果:[3, 14, 214, 542, 748, 53]
    中间第3次排序结果:[3, 14, 53, 214, 542, 748]
    排序结果:[3, 14, 53, 214, 542, 748]

 结论:

  数组中最大数有几位就循环几次!

速率测试:
    public static void main(String[] args) {
        int[] arr = new int[80000];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        Date date1 = new Date();
        sort(arr);
        Date date2 = new Date();
        System.out.println("选择排序耗时:" + (date2.getTime() - date1.getTime()));
        System.out.println("排序结果:" + Arrays.toString(arr));
    }
    
测试输出:
    选择排序耗时:34
    排序结果:[1, 2, 2, 4,... 79999]

 

posted @ 2022-11-25 12:15  努力的达子  阅读(27)  评论(0编辑  收藏  举报