06_排序算法

  • 排序算法的介紹
  • 排序算法的分類
  • 算法的時間復(fù)雜度
    • 衡量一個程序執(zhí)行時間的兩種方法
    • 時間頻度
    • 時間復(fù)雜度
    • 常見的時間復(fù)雜度
    • 平均時間復(fù)雜度和最壞時間復(fù)雜度
  • 算法的空間復(fù)雜度
    • 基本介紹
  • 冒泡排序
  • 選擇排序
  • 插入排序
  • 希爾排序
  • 快速排序
  • 歸并排序
  • 基數(shù)排序
  • 常用排序的算法比較
  • 相關(guān)術(shù)語解釋

1. 排序算法的介紹

  • 排序算法(Sort Algorithm)俭驮,排序是將 一組數(shù)據(jù)厘熟,以 指定的順序進(jìn)行 排列的過程

2. 排序算法的分類

  • 內(nèi)部排序:需要處理的數(shù)據(jù)都加載到 內(nèi)部存儲(內(nèi)存)中進(jìn)行排序

  • 外部排序:數(shù)據(jù)量過大谴仙,無法全部加載到內(nèi)存中,需要借助 外部存儲(文件等) 進(jìn)行排序

  • 常見排序算法分類:

image-20220324223051315.png

3. 算法的時間復(fù)雜度

3.1 衡量一個程序執(zhí)行時間的兩種方法

  • 事后統(tǒng)計法:這種方法可行掂榔,但是有兩個問題:一個是需要實際運行該程序绩郎,另一個是依賴于計算機的硬件、軟件等環(huán)境因素肉拓。這種方式要在同一臺計算機的相同狀態(tài)運行毫痕,才能比較因宇。

  • 事前估算法:通過分析某個算法的 時間復(fù)雜度 來判斷哪個算法更優(yōu)

3.2 時間頻度

  • 時間頻度:一個算法中的語句執(zhí)行次數(shù)稱為語句頻度或時間頻度饲化,記為 T(n)

3.3 時間復(fù)雜度

  • 時間復(fù)雜度:算法中的基本操作語句的重復(fù)執(zhí)行次數(shù)是問題規(guī)模 n 的某個函數(shù) 用 T(n) 表示。若有 某個輔助函數(shù) f(n), 使得當(dāng) n 趨近于無窮大時弥姻, T(n)/f(n)的極限值為不等于零的常數(shù),則稱 f(n) 是 T(n) 的同數(shù)量級函數(shù)。記作 T(n)=O(f(n)), O(f(n))稱為算法的漸進(jìn)時間復(fù)雜度,簡稱時間復(fù)雜度

  • 計算時間復(fù)雜度的方法

    ① 用常數(shù) 1 代替運行時間中的所有加法常數(shù)

    ② 修改后的運行次數(shù)函數(shù)中判耕,只保留最高階項

    ③ 去除最高階項的系數(shù)

3.4 常見時間復(fù)雜度

  • 常見時間復(fù)雜度從小到大依次為: O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(nk)<n(2n)
  • 隨著問題規(guī)模 n 不斷增大岖赋,算法的執(zhí)行效率越低疚脐。

3.5 平均時間復(fù)雜度和最壞時間復(fù)雜度

  • 平均時間復(fù)雜度:所有可能的輸入實例均以等概率出現(xiàn)的情況下,該算法的運行時間
  • 最壞時間復(fù)雜度:一般討論時間復(fù)雜度均是最壞情況下的時間復(fù)雜度

4. 算法的空間復(fù)雜度

  • 空間復(fù)雜度(Space Complexity): 該算法所耗費的存儲空間可岂,它也是問題規(guī)模 n 的函數(shù),是對一個算法在運行過程中臨時占用存儲空間大小的量度。
  • 從用戶使用體驗上看柑营,更看重程序的執(zhí)行速度站刑,一些緩存產(chǎn)品和算法 本質(zhì)就是空間換時間

5.冒泡排序

  • 基本思想: 依次比較相鄰元素的值中贝,若發(fā)現(xiàn)逆序則交換
  • 優(yōu)化:如果一趟比較下來沒有進(jìn)行過交換,就說明序列有序
  • 圖解
image-20220324224951603.png
  • 代碼實現(xiàn):
public class BubbleSort {
    public static void main(String[] args) {
        int arr[] = {3,9,-1,10,20};
//        int arr[] = {-1, 3, 9, 10, 20};

        //為了容易理解晶丘,把冒泡排序的過程展示一下

        System.out.println("排序前");
        System.out.println(Arrays.toString(arr));

        bubbleSort(arr);
        
        System.out.println("排序后");
        System.out.println(Arrays.toString(arr));

/*
        //第二趟排序黍氮,就是將第二大的數(shù)排在倒數(shù)第二位
        for(int j = 0; j < arr.length - 1 - 1;j++){
            if(arr[j] > arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
        System.out.println("第二趟排序后的數(shù)組");
        System.out.println(Arrays.toString(arr));

        //第三趟排序,就是將第三大的數(shù)排在倒數(shù)第三位
        for(int j = 0; j < arr.length - 1 - 2;j++){
            if(arr[j] > arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
        System.out.println("第三趟排序后的數(shù)組");
        System.out.println(Arrays.toString(arr));

        //第四趟排序浅浮,就是將第四大的數(shù)排在倒數(shù)第四位
        for(int j = 0; j < arr.length - 1 - 3;j++){
            if(arr[j] > arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
        System.out.println("第四趟排序后的數(shù)組");
        System.out.println(Arrays.toString(arr));*/
    }

    //將冒泡排序算法沫浆,封裝成一個方法
    public static void bubbleSort(int[] arr){
        //冒泡排序的時間復(fù)雜度O(n^2)
        int  temp = 0; //臨時變量,交換時用
        boolean flag = false; //標(biāo)識變量滚秩,表示是否進(jìn)行過交換
        for(int i = 0; i < arr.length - 1;i++){

            //第一躺排序专执,就是將最大的數(shù)排在最后
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //如果前面的數(shù)比后面的數(shù)大,則交換
                if(arr[j] > arr[j+1]){
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
//            System.out.printf("第%d趟排序后的數(shù)組\n",i+1);
//            System.out.println(Arrays.toString(arr));

            if(!flag){  //在一趟排序中郁油,一次交換都沒有發(fā)生
                break;
            }else{
                flag = false; //重置flag本股,進(jìn)行下次判斷,
            }
        }
    }
}

6. 選擇排序

  • 選擇排序的基本思想:從欲排序的數(shù)據(jù)中桐腌,找到一個最小的拄显,然后與指定位置的值進(jìn)行交換

  • 思路分析圖:

image-20220324225430731.png
  • 思路分析:

    1. 選擇排序一共有 數(shù)組大小-1 輪排序

    2. 每 1 輪排序,又是一個循環(huán)案站,循環(huán)的規(guī)則

      2.1 先假定當(dāng)前這個數(shù)是 最小數(shù)

      2.2 然后與后面的數(shù)比較躬审,如果發(fā)現(xiàn)有更小的數(shù),就重新確定最小數(shù)蟆盐,并得到下標(biāo)

      2.3 當(dāng)遍歷到數(shù)組的最后時承边,就得到本輪的最小數(shù)和下標(biāo)

      2.4 交換

  • 代碼實現(xiàn)

public class SelectSort {
    public static void main(String[] args) {
        int[] arr = {101,34,119,1};
        System.out.println("排序前");
        System.out.println(Arrays.toString(arr));

        selectSort(arr);

        System.out.println("排序后");
       
        System.out.println("排序后");
        System.out.println(Arrays.toString(arr));

    }

    //選擇排序
    public static void selectSort(int[] arr){

        //在推到的過程,可以使用循環(huán)
        //選擇排序的時間復(fù)雜度是O(n^2)
        for(int i = 0; i < arr.length - 1;i++) {
            //使用逐步推導(dǎo)的方式石挂,講解選擇排序
            //原始數(shù)組:101博助,34,119痹愚,1
            //算法 先簡單——》后復(fù)雜  富岳,可以把一個復(fù)雜的算法罗心,拆分成簡單的問題 -》 逐步解決

            //第一輪
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) { //說明假定的最小值,并不是最小值
                    min = arr[j];  //重置min
                    minIndex = j;  //重置minIndex
                }
            }
            //將最小值城瞎,放在 arr[0], 即交換
            if (minIndex != i) {

                arr[minIndex] = arr[i];
                arr[i] = min;
            }

//            System.out.println("第"+i+1+"輪后,");
//            System.out.println(Arrays.toString(arr));
        }
       /* //第2輪
        minIndex = 1;
        min = arr[1];
        for(int j = 1 + 1; j < arr.length; j++){
            if(min > arr[j]){ //說明假定的最小值疾瓮,并不是最小值
                min = arr[j];  //重置min
                minIndex = j;  //重置minIndex
            }
        }
        //將最小值脖镀,放在 arr[1], 即交換
        if(minIndex!=1){
            arr[minIndex] = arr[1];
            arr[1] = min;

        }

        System.out.println("第2輪后,");
        System.out.println(Arrays.toString(arr));

        //第3輪
        minIndex = 2;
        min = arr[2];
        for(int j = 2 + 1; j < arr.length; j++){
            if(min > arr[j]){ //說明假定的最小值狼电,并不是最小值
                min = arr[j];  //重置min
                minIndex = j;  //重置minIndex
            }
        }
        //將最小值蜒灰,放在 arr[1], 即交換
        if(minIndex!=2){
            arr[minIndex] = arr[2];
            arr[2] = min;

        }

        System.out.println("第3輪后,");
        System.out.println(Arrays.toString(arr));*/
    }
}

7.插入排序

  • 插入排序的基本思想: 把n個待排序的元素看成一個有序表和一個無序表肩碟,開始 有序表只包含一個元素强窖,無序表中有 n-1 個元素,排序過程中削祈,每次從無序表中取出第一個元素翅溺,將它插入到有序表中的適當(dāng)位置。

  • 思路圖解:


    image-20220324230244941.png
  • 代碼實現(xiàn)
public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {101,34,119,1};
        insertSort(arr);
    }

    //插入排序
    public static void insertSort(int[] arr){
        int insertval = 0;
        int insertIndex = 0;
        //使用for循環(huán)髓抑,將代碼簡化
        for (int i = 1; i < arr.length; i++) {

            //定義待插入的數(shù)
            insertval = arr[i];
            insertIndex = i-1; //即arr[i]的前面這個數(shù)的下標(biāo)

            //給 insertVal 找到插入的位置
            //說明
            //1.insertIndex >= 0 保證在給 insertVal 找插入位置咙崎,不越界
            //2. insertIndex < arr[insertIndex] 待插入的數(shù),還沒有找到插入的位置(小于inserIndex位置)
            //3. 就需要將 arr[insertIndex] 后移
            while(insertIndex >= 0 && insertval < arr[insertIndex]){
                arr[insertIndex + 1] = arr[insertIndex]; //值向后移
                insertIndex--; //索引減一
            }
            //當(dāng)退出 while 循環(huán)時吨拍,說明插入的位置找到 insertIndex;
            if(insertIndex + 1 != i){

                arr[insertIndex + 1] = insertval;
            }

            System.out.println("第"+i+"輪插入后:");
            System.out.println(Arrays.toString(arr));
        }

     /*   //使用逐步推到的方式來講解褪猛,便于理解
        //第一輪 {101,34,119,1};  => {34,101,119,1}

        //定義待插入的數(shù)
        int insertval = arr[1];
        int insertIndex = 1-1; //即arr[1]的前面這個數(shù)的下標(biāo)

        //給 insertVal 找到插入的位置
        //說明
        //1.insertIndex >= 0 保證在給 insertVal 找插入位置,不越界
        //2. insertIndex < arr[insertIndex] 待插入的數(shù)羹饰,還沒有找到插入的位置
        //3. 就需要將 arr[insertIndex] 后移
        while(insertIndex >= 0 && insertval < arr[insertIndex]){
            arr[insertIndex + 1] = arr[insertIndex]; //
            insertIndex--;
        }
        //當(dāng)退出 while 循環(huán)時伊滋,說明插入的位置找到 insertIndex;
        arr[insertIndex + 1] = insertval;

        System.out.println("第1輪插入后:");
        System.out.println(Arrays.toString(arr));

        //第2輪
        insertval = arr[2];
        insertIndex = 2-1; //即arr[1]的前面這個數(shù)的下標(biāo)

        while(insertIndex >= 0 && insertval < arr[insertIndex]){
            arr[insertIndex + 1] = arr[insertIndex]; //
            insertIndex--;
        }
        //當(dāng)退出 while 循環(huán)時,說明插入的位置找到 insertIndex;
        arr[insertIndex + 1] = insertval;

        System.out.println("第2輪插入后:");
        System.out.println(Arrays.toString(arr));

        //第3輪
        insertval = arr[3];
        insertIndex = 3-1; //即arr[1]的前面這個數(shù)的下標(biāo)

        while(insertIndex >= 0 && insertval < arr[insertIndex]){
            arr[insertIndex + 1] = arr[insertIndex]; //
            insertIndex--;
        }
        //當(dāng)退出 while 循環(huán)時队秩,說明插入的位置找到 insertIndex;
        arr[insertIndex + 1] = insertval;

        System.out.println("第3輪插入后:");
        System.out.println(Arrays.toString(arr));*/
    }
}

8. 希爾排序(縮小增量排序)

  • 基本思想:把記錄按下標(biāo)的一定增量分組笑旺,對每組使用直接插入排序算法排序;隨著增量逐漸減少刹碾,每組包含的關(guān)鍵詞越來越多燥撞。當(dāng)增量減至1,整個文件恰被分成一組迷帜,算法便終止
  • 希爾排序的示意圖
image-20220324230655134.png
image-20220324230717604.png

交換法代碼實現(xiàn):

 //使用逐步推導(dǎo)的方式來編寫希爾排序
    //希爾排序時物舒,對有序序列在插入時采用交換法
    public static void  shellSort(int[] arr){

        //使用循環(huán)處理
        int temp = 0;
        int count = 0;
        for(int gap = arr.length/2; gap > 0;gap/=2){
            for(int i = gap; i < arr.length;i++){
                //遍歷各組中的素有元素(共5組,每組有2個元素)戏锹,步長5
                for (int j = i - gap; j >= 0; j-=gap) {
                    //如果當(dāng)前元素大于加上步長后的那個元素冠胯,交換‘
                    if(arr[j] > arr[j+gap]){
                        temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
            System.out.println("希爾排序第"+(++count)+"輪后:"+ Arrays.toString(arr));
        }

移位法代碼實現(xiàn):

//對交換式的希爾排序進(jìn)行優(yōu)化 -》 移位法
    public static void shellSort2(int[] arr){
        //增量 gap,并逐步的縮小增量
        for(int gap = arr.length / 2; gap > 0; gap /= 2 ){
            //從 gap 個元素锦针,逐個對其的組進(jìn)行直接插入排序
            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;
                    }
                    //當(dāng)退出while后荠察,就給 temp 找到插入的位置
                    arr[j] = temp;
                }
            }
            System.out.println(Arrays.toString(arr));
        }
    }

9. 快速排序

  • 基本思想:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分置蜀,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對著兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序悉盆,整個排序過程可以遞歸進(jìn)行盯荤,一次達(dá)到整個數(shù)據(jù)變成有序序列。

  • 示意圖

image-20220325074550709.png

image-20220325074611695.png
  • 代碼實現(xiàn)
  • 代碼1
public class quickSort {
    public static void main(String[] args) {
        int[] arr = {-9,78,0,23,-567,70};
        quickSort(arr,0,arr.length-1);
        System.out.println("arr="+ Arrays.toString(arr));
    }
    public static void quickSort(int[] arr,int left,int right){
        int l = left; //左下標(biāo)
        int r = right; //右下標(biāo)
        int temp = 0; //臨時變量焕盟,交換時使用
        //pivot 中軸值
        int pivot = arr[(left + right)/2];
        //while 循環(huán)的目的秋秤,是讓比pivot 值小的放到左邊
        //比 pivot 值放到右邊
        while(l < r){ //
            //在pivot 的左邊一直找,找到大于等于 pivot 值脚翘,才退出
            while( arr[l] < pivot){
                l += 1;
            }
            //在pivot 的右邊一直找灼卢,找到小于等于 pivot 值,才退出
            while(arr[r] > pivot){
                r -= 1;
            }
            //如果l >= r 說明 pivot 的左右兩邊的值来农,已經(jīng)按照
            // 左邊全部是小于等于pivot 的值鞋真,右邊全部大于等于pivot的值
            if(l >= r){
                break;
            }
            //交換
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //如果交換完后,發(fā)現(xiàn)這個 arr[l] == pivot 值相等 r--沃于, 前移
            if(arr[l] == pivot){
                r -= 1;
            }
            //如果交換完后涩咖,發(fā)現(xiàn)這個 arr[r] == pivot 值相等 l++, 后移
            if(arr[l] == pivot){
                l += 1;
            }
        }

        //如果 l == r,bixu  l++,r--,否則出現(xiàn)棧溢出
        if(l == r){
            l += 1;
            r -= 1;
        }
        //向左遞歸
        if(left < r){
            quickSort(arr,left,r);
        }
        //向右遞歸
        if(right > l){
            quickSort(arr,l,right);
        }
    }

}

  • 代碼2
public class QuickSort {
    private static void swap(int[] data, int i, int j) {
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    private static void subSort(int[] data, int start, int end) {
        if (start < end) {
            int base = data[start];
            int low = start;
            int high = end + 1;
            while (true) {
                while (low < end && data[++low] - base <= 0)
                    ;
                while (high > start && data[--high] - base >= 0)
                    ;
                if (low < high) {
                    swap(data, low, high);
                } else {
                    break;
                }
            }
            swap(data, start, high);
            
            subSort(data, start, high - 1);//遞歸調(diào)用
            subSort(data, high + 1, end);
        }
    }
    public static void quickSort(int[] data){
        subSort(data,0,data.length-1);
    }
    
    
    public static void main(String[] args) {
        int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
        System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
        quickSort(data);
        System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
    }
}

10. 歸并排序

  • 基本思想:采用經(jīng)典的 分治策略
image-20220325080536447.png
image-20220325080615387.png
  • 代碼實現(xiàn):
public class MergetSort {
    public static void main(String[] args) {
        int arr[] = {8,4,5,7,1,3,6,2}; //n -> merge -> n-1
        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; //中間索引
            //向左遞歸進(jìn)行分解
            mergeSort(arr,left,mid,temp);
            //向右遞歸進(jìn)行分解
            mergeSort(arr,mid+1,right,temp);
            //到合并時
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     * 合并的方法
     * @param arr 排序的原始數(shù)組
     * @param left 左邊有序序列的初始索引
     * @param mid 中間索引
     * @param right 右邊索引
     * @param temp 中轉(zhuǎn)的數(shù)組
     */
    public static void merge(int arr[],int left,int mid,int right,int[] temp){
//        System.out.println("xxxx");
        int i = left; //初始化i揽涮,表示 左邊有序序列的初始索引
        int j = mid+1; //初始化j抠藕,右邊有序序列的初始索引
        int t = 0; // 指向temp 數(shù)組的當(dāng)前索引

        //(1)
        //先把左右兩邊(有序)的數(shù)據(jù)按照規(guī)則填充到 temp 數(shù)組
        //直到左右兩邊的有序序列,有一邊處理完畢為止
        while(i <= mid && j <= right){ //繼續(xù)
            //如果左邊的有序序列的當(dāng)前元素蒋困,小于等于右邊有序序列的當(dāng)前元素
            //將左邊的當(dāng)前元素盾似,拷貝到 temp 數(shù)組
            //然后 t 后移, i 后移
            if(arr[i] <= arr[j]){
                temp[t] = arr[i];
                t += 1;
                i += 1;
            } else{ //反之:右邊的有序序列當(dāng)前元素雪标,大于 左邊的零院,并填充到 temp 數(shù)組中
                temp[t] = arr[j];
                t += 1;
                j += 1;
            }
        }

        //(2)
        //把有剩余數(shù)據(jù)的一邊的數(shù)據(jù)依次全部填充到 temp
        while( i <= mid){ //左邊的有序序列還有剩余元素,就全部填充到 temp
            temp[t] = arr[i];
            t += 1;
            i += 1;
        }
        while( j <= right){ //右邊的有序序列還有剩余元素村刨,就全部填充到 temp
            temp[t] = arr[j];
            t += 1;
            j += 1;
        }

        //(3)
        //將 temp 數(shù)組的元素拷貝到 arr
        //注意告抄,并不是每次都拷貝所有
        t = 0;
        int tempLeft = left;
        System.out.println("tempLeft="+tempLeft+"right="+right);
        while(tempLeft <= right){ //第一次合并 tempLeft = 0,right = 1 第二次:tempLeft=2,right=3 第三次嵌牺,tL=0,right=3
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }

    }
}

11.基數(shù)排序

  • 基數(shù)排序(radix sort) 屬于 “分配式排序” 又稱 “桶子法”

  • 基數(shù)排序是屬于穩(wěn)定性的排序打洼,是桶排序的擴展

  • 基本思想: 將所有待比較數(shù)值同一為同樣的數(shù)位長度,數(shù)位較短的數(shù)前面補零逆粹。然后募疮,從最低位開始,依次進(jìn)行排序僻弹,這樣從最低位排序一直到最高位排序完成以后阿浓,數(shù)列就變成一個有序序列。

  • 示意圖:

image-20220325081148541.png
image-20220325081205233.png
image-20220325081315009.png
  • 代碼實現(xiàn):
public class RadixSort {
    public static void main(String[] args) {
        int arr[] = {53,3,542,748,14,214};
        radixSort(arr);
    }
    //基數(shù)排序方法
    public static void radixSort(int[] arr){
        //根據(jù)推導(dǎo)過程
        //1. 得到數(shù)組中最大數(shù)的位數(shù)
        int max = arr[0]; //假設(shè)第一個數(shù)就是最大數(shù)
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        //得到最大數(shù)是幾位數(shù)
        int maxLength = (max + "").length();
        int[][] bucket = new int[10][arr.length];
        int[] bucketElementCounts = new int[10];
        for (int i = 0,n=1; i < maxLength;i++,n*=10){
            //針對每個元素的對應(yīng)位進(jìn)行排序處理蹋绽,第一次是個位芭毙,第二次是十位筋蓖,第三次是百位
            for (int j = 0; j < arr.length; j++) {
                //取出每個元素的個位
                int digitOfElement = arr[j] /n % 10;
                //放入對應(yīng)的桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
            }
            //按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù),放入原來數(shù)組)
            int index = 0;
            //遍歷每一桶退敦,并將桶中的數(shù)粘咖,放入原數(shù)組
            for(int k = 0; k < bucketElementCounts.length;k++){
                //如果桶中有數(shù)據(jù),才放入原數(shù)組
                if(bucketElementCounts[k] != 0){
                    //循環(huán)該桶即第k個桶(第k個一維數(shù)組)侈百,放入
                    for(int l = 0; l < bucketElementCounts[k];l++){
                        //取出元素放到 arr
                        arr[index++] = bucket[k][l];
                    }
                }
                //第i+1輪處理后涂炎,需要將每個 bucketElementCounts[k] = 0;
                bucketElementCounts[k] = 0;
            }

            System.out.println("第"+(i+1)+"輪對個位的排序處理 arr="+ Arrays.toString(arr));

        }
        
       /* //第1輪(針對每個元素的個位進(jìn)行排序處理)
        //定義一個二維數(shù)組,表示10個桶设哗,每個桶就是一個一維數(shù)組
        //說明
        //1. 二維數(shù)組包含 10 個一維數(shù)組
        //2. 為了防止在放入數(shù)的時候,數(shù)據(jù)溢出两蟀,則每個一維數(shù)組(桶)网梢,大小定為arr.length
        //3. 明確:技術(shù)排序是使用空間換時間的經(jīng)典算法
        int[][] bucket = new int[10][arr.length];

        //為了記錄每個桶中,實際存放多少個數(shù)據(jù)赂毯,我們定義一個一維數(shù)組战虏,來記錄各個桶每次放入數(shù)據(jù)的個數(shù)
        //可以這樣理解
        //比如:bucketElementCounts[0] ,記錄的是bucket[0] 桶的放入數(shù)據(jù)的個數(shù),
        int[] bucketElementCounts = new int[10];

        for (int j = 0; j < arr.length; j++) {
            //取出每個元素的個位
            int digitOfElement = arr[j] % 10;
            //放入對應(yīng)的桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
        }
        //按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù)党涕,放入原來數(shù)組)
        int index = 0;
        //遍歷每一桶烦感,并將桶中的數(shù),放入原數(shù)組
        for(int k = 0; k < bucketElementCounts.length;k++){
            //如果桶中有數(shù)據(jù)膛堤,才放入原數(shù)組
            if(bucketElementCounts[k] != 0){
                //循環(huán)該桶即第k個桶(第k個一維數(shù)組)手趣,放入
                for(int l = 0; l < bucketElementCounts[k];l++){
                    //取出元素放到 arr
                    arr[index++] = bucket[k][l];
                }
            }
            //第2輪處理后,需要將每個 bucketElementCounts[k] = 0;
            bucketElementCounts[k] = 0;
        }

        System.out.println("第1輪對個位的排序處理 arr="+ Arrays.toString(arr));

        //第二輪
        for (int j = 0; j < arr.length; j++) {
            //取出每個元素的個位
            int digitOfElement = arr[j] /10 % 10;
            //放入對應(yīng)的桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
        }
        //按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù)肥荔,放入原來數(shù)組)
        index = 0;
        //遍歷每一桶绿渣,并將桶中的數(shù),放入原數(shù)組
        for(int k = 0; k < bucketElementCounts.length;k++){
            //如果桶中有數(shù)據(jù)燕耿,才放入原數(shù)組
            if(bucketElementCounts[k] != 0){
                //循環(huán)該桶即第k個桶(第k個一維數(shù)組)中符,放入
                for(int l = 0; l < bucketElementCounts[k];l++){
                    //取出元素放到 arr
                    arr[index++] = bucket[k][l];
                }
            }
            //第1輪處理后,需要將每個 bucketElementCounts[k] = 0;
            bucketElementCounts[k] = 0;

        }

        System.out.println("第2輪對十位的排序處理 arr="+ Arrays.toString(arr));


        //第3輪
        for (int j = 0; j < arr.length; j++) {
            //取出每個元素的個位
            int digitOfElement = arr[j] /100 % 10;
            //放入對應(yīng)的桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++; //記錄每個桶中的數(shù)據(jù)
        }
        //按照這個桶的順序(一維數(shù)組的下標(biāo)依次取出數(shù)據(jù)誉帅,放入原來數(shù)組)
        index = 0;
        //遍歷每一桶淀散,并將桶中的數(shù),放入原數(shù)組
        for(int k = 0; k < bucketElementCounts.length;k++){
            //如果桶中有數(shù)據(jù)蚜锨,才放入原數(shù)組
            if(bucketElementCounts[k] != 0){
                //循環(huán)該桶即第k個桶(第k個一維數(shù)組)档插,放入
                for(int l = 0; l < bucketElementCounts[k];l++){
                    //取出元素放到 arr
                    arr[index++] = bucket[k][l];
                }
            }
            //第3輪處理后,需要將每個 bucketElementCounts[k] = 0;
            bucketElementCounts[k] = 0;

        }

        System.out.println("第3輪對百位的排序處理 arr="+ Arrays.toString(arr));*/
    }
}

  • 基數(shù)排序的說明:是經(jīng)典空間換時間的方式踏志,占用內(nèi)存很大阀捅,當(dāng)對海量數(shù)據(jù)排序時,容易造成 OutOfMemeoryError
  • 有負(fù)數(shù)的數(shù)組针余,我們不考慮基數(shù)排序

12. 常用排序算法的比較

image-20220325081636835.png

13. 相關(guān)術(shù)語解釋

  • 穩(wěn)定:如果a原本在b的前面饲鄙,而a=b,排序之后凄诞,a仍然在b的前面
  • 不穩(wěn)定:如果a原本在b的前面,而a=b,排序之后忍级,a有可能會出現(xiàn)在b的后面
  • 內(nèi)排序:所有排序操作都在內(nèi)存中完成
  • 外排序:由于數(shù)據(jù)太大帆谍,因此把數(shù)據(jù)放在磁盤中,而排序通過磁盤和內(nèi)存的數(shù)據(jù)傳輸才能進(jìn)行
  • In-place: 不占用額外內(nèi)存
  • Out-place:占用額外內(nèi)存
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轴咱,一起剝皮案震驚了整個濱河市汛蝙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朴肺,老刑警劉巖窖剑,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異戈稿,居然都是意外死亡西土,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門鞍盗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來需了,“玉大人,你說我怎么就攤上這事般甲±哒В” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵敷存,是天一觀的道長墓造。 經(jīng)常有香客問我,道長锚烦,這世上最難降的妖魔是什么滔岳? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮挽牢,結(jié)果婚禮上谱煤,老公的妹妹穿的比我還像新娘。我一直安慰自己禽拔,他們只是感情好刘离,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著睹栖,像睡著了一般硫惕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上野来,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天恼除,我揣著相機與錄音,去河邊找鬼。 笑死豁辉,一個胖子當(dāng)著我的面吹牛令野,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播徽级,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼气破,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了餐抢?” 一聲冷哼從身側(cè)響起现使,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旷痕,沒想到半個月后碳锈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡欺抗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年殴胧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佩迟。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖竿屹,靈堂內(nèi)的尸體忽然破棺而出报强,到底是詐尸還是另有隱情,我是刑警寧澤拱燃,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布秉溉,位于F島的核電站,受9級特大地震影響碗誉,放射性物質(zhì)發(fā)生泄漏召嘶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一哮缺、第九天 我趴在偏房一處隱蔽的房頂上張望弄跌。 院中可真熱鬧,春花似錦尝苇、人聲如沸铛只。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淳玩。三九已至,卻和暖如春非竿,著一層夾襖步出監(jiān)牢的瞬間蜕着,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工红柱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留承匣,地道東北人蓖乘。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓杭跪,卻偏偏與公主長得像琅捏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赐俗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容