經典十大排序演算法(含升序降序,基數排序含負數排序)

westwolf發表於2021-09-09

寫在前面的話

       雖然已經有很多人總結過這十大排序演算法,優秀的文章也不少,但是Java完整版的好像不多,還存在某些文章程式碼存在錯誤的情況,同時也為了自己練手,決定把所有的寫一遍鞏固下,同時也真誠的希望閱讀到這篇文章的小夥伴們可以自己去從頭敲一遍,不要貼上複製!希望我的文章對你有所幫助,每天進步一點點!!!


       我用通俗的理解寫下對演算法的解釋,對某個演算法的執行過程不是很理解的話或者想看比較官方的解釋的話,單獨搜尋某個演算法,看幾篇不同的解釋,就可以有自己的理解了,這裡我主要展示程式碼以及進行通俗的解釋!整起來,再強調一次,一定要自己敲一遍,這樣才能理解的更深刻!

十大排序演算法對比

圖片描述

關於最後一列的穩定性,我稍微解釋下,例如對序列:1 2 4 2 6 排序,序列中存在兩個2,如果我們把這兩個2標記上(讓他倆不同),排序之後,前面的2還在前面,那麼就稱這種排序是穩定的,反之不穩定。


氣泡排序

圖片描述

圖片描述

本文的圖片來源網路,僅用於大家學習,侵權聯絡刪除!(下同)


完整程式碼:


package com.keafmd.Sequence;


public class BubbleSort {


    //氣泡排序

    public static void bubbleSort(int[] arr, boolean ascending) { //exchange標誌表示為升序排序還是降序排序


        boolean flag = true; //加一個標誌位,記錄上一次是否發生了交換,如果是,我們則進行下一輪,如果沒有,說明已經冒泡好了


        for (int i = 1; i


           


            flag = false; //假定未交換


            for (int j = 0; j


                if (ascending ? arr[j] > arr[j + 1] : arr[j]

                    int temp = arr[j];

                    arr[j] = arr[j + 1];

                    arr[j + 1] = temp;

                    flag = true;

                }


            }

        }

    }


    //氣泡排序 -- 預設不傳參升序

    public static void bubbleSort(int[] arr) {

        bubbleSort(arr, true);

    }

}


測試程式碼:

升序排序(從小到大)

圖片描述

圖片描述

執行結果:

圖片描述

降序排序(從大到小)

圖片描述

執行結果:

圖片描述


下面幾個演算法的測試也就是換了下類名和方法名(換成相應的排序演算法),如果想降序就在陣列後面傳個false即可。我就不一一複製了,我在最下面給出含所有演算法的測試類,需要的自取即可。

快速排序

簡單解釋:

快速排序就是每次找一個基點(第一個元素),然後兩個哨兵,一個從最前面往後走,一個從最後面往前面走,如果後面那個哨兵找到了一個比基點大的數停下來,前面那個哨兵找到比基點大的數停下來,然後交換兩個哨兵找到的數,如果找不到最後兩個哨兵就會碰到一起就結束,最後交換基點和哨兵相遇的地方的元素,然後就將一個序列分為比基點小的一部分和比基點大的一部分,然後遞迴左半部分和右半部分,最後的結果就是有序的了。

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class QuickSort {


    //快速排序

    public static void quickSort(int[] arr) {

        quickSort(arr, true);

    }


    public static void quickSort(int[] arr, boolean ascending) {

        if (ascending) {

            quickSort(arr, 0, arr.length - 1, true);

        } else {

            quickSort(arr, 0, arr.length - 1, false);

        }

    }


    public static void quickSort(int[] arr, int begin, int end, boolean ascending) {

        if (ascending)

            quickSort(arr, begin, end);

        else

            quickSortDescending(arr, begin, end);

    }


    //快排序升序 -- 預設

    public static void quickSort(int[] arr, int begin, int end) {

        if (begin > end) { //結束條件

            return;

        }

        int base = arr[begin];

        int i = begin, j = end;

        while (i

            while (arr[j] >= base && i

                j--;

            }

            while (arr[i]

                i++;

            }

            if (i

                int temp = arr[i];

                arr[i] = arr[j];

                arr[j] = temp;

            }


        }

        //最後將基準為與i和j相等位置的數字交換

        arr[begin] = arr[i];

        arr[i] = base;

        quickSort(arr, begin, i - 1); //遞迴呼叫左半陣列

        quickSort(arr, i + 1, end); //遞迴呼叫右半陣列


    }


    //快排序降序

    public static void quickSortDescending(int[] arr, int begin, int end) {

        if (begin > end) { //結束條件

            return;

        }

        int base = arr[begin];

        int i = begin, j = end;

        while (i

            while (arr[j]

                j--;

            }

            while (arr[i] >= base && i

                i++;

            }

            if (i

                int temp = arr[i];

                arr[i] = arr[j];

                arr[j] = temp;

            }


        }

        //最後將基準為與i和j相等位置的數字交換

        arr[begin] = arr[i];

        arr[i] = base;

        quickSortDescending(arr, begin, i - 1); //遞迴呼叫左半陣列

        quickSortDescending(arr, i + 1, end); //遞迴呼叫右半陣列


    }


}


直接選擇排序

簡單解釋:
陣列分為已排序部分(前面)和待排序序列(後面)
第一次肯定所有的數都是待排序的
從待排序的序列中找到最大或最小的那個元素,放到前面的已排序部分,然後一直找,不斷縮小待排序的範圍,直到所有的數都是已排序的了

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class SelectSort {


    //直接選擇排序

    public static void selectSort(int[] arr, boolean ascending) {

        for (int i = 0; i

            int m = i; //最小值或最小值的下標

            for (int j = i + 1; j

                if (ascending ? arr[j] arr[m]) {

                    m = j; //找到待排序的數中最小或最大的那個數,記錄下標

                }


            }

            //交換位置

            int temp = arr[i];

            arr[i] = arr[m];

            arr[m] = temp;


        }

    }


    public static void selectSort(int[] arr) {

        selectSort(arr, true);

    }

}


堆排序

先理解下大頂堆和小頂堆,看圖
大頂堆,雙親結點的值比每一個孩子結點的值都要大。根結點值最大
小頂堆,雙親結點的值比每一個孩子結點的值都要小。根結點值最小

圖片描述

簡單解釋:
構建好大頂堆或小頂堆結構,這樣最上面的就是最大值或最小值,那麼我們取出堆頂元素,然後重新構建結構,一直取,一直重新構建,那麼最後達到排序的效果了。

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class HeapSort {


    //堆排序

    public static void heapSort(int[] arr) {

        //對傳入的陣列進行建立堆,這裡預設建立大頂堆,進行升序排列

        heapSort(arr, true);

    }


    public static void heapSort(int[] arr, boolean maxheap) {


        //1.構建大頂堆

        for (int i = arr.length / 2 - 1; i >= 0; i--) {

            //從第一個非葉子結點從下至上,從右至左調整結構

            sift(arr, i, arr.length , maxheap);

        }


        //2.調整堆結構+交換堆頂元素與末尾元素

        for (int j = arr.length - 1; j > 0; j--) {


            //現在的陣列第一個就是根結點,最小值所在,進行交換,把它放到最右邊

            int temp = arr[j];

            arr[j] = arr[0];

            arr[0] = temp;


            //重新建立堆

            sift(arr, 0, j , maxheap); //重新對堆進行調整

        }

    }


    //建立堆的方法

   

    private static void sift(int[] arr, int parent, int len, boolean maxheap) {


        int value = arr[parent]; //先取出當前元素i


        for (int child = 2 * parent + 1; child


            if (child+1 arr[child + 1])) { //如果左子結點小於右子結點,child指向右子結點

                child++; //右孩子如果比左孩子大,我們就將現在的孩子換到右孩子

            }


            //判斷是否符合大頂堆的特性, 如果右孩子大於雙親,自然左孩子也大於雙親,符合

            //如果子節點大於父節點,將子節點值賦給父節點(不用進行交換)

            if (maxheap ? value arr[child]) {

                arr[parent]=arr[child];

                parent = child;

            }

            else {//如果不是,說明已經符合我們的要求了。

                break;

            }

        }

        arr[parent] =value; //將value值放到最終的位置



    }


}


歸併排序

簡單解釋:
該演算法是採用分治法,把陣列不斷分割,直至成為單個元素,然後比較再合併(合併的過程就是兩部分分別從頭開始比較,取出最小或最大元素的放到新的區域內,繼續取兩部分中最大或最小的元素,直到這兩部分合並完,最後所有的都合併完,最後形成完整的有序序列)

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class MergeSort {


    //歸併排序

    public static void mergeSort(int []arr ,boolean ascending){

        int[] temp = new int[arr.length]; //在排序前,先建好一個長度等於原陣列長度的臨時陣列,避免遞迴中頻繁開闢空間

        mergeSort(arr,0,arr.length-1,temp,ascending);

    }

    public static void mergeSort(int []arr){

        mergeSort(arr,true);

    }


   

    public static void mergeSort(int []arr,int left,int right,int[] temp,boolean ascending){

        if(left


            //對半分,比如總長度是10,left=0,right=9,mid=4確實是中間分了,0~4,5~9

            //當長度9,left=0,right=8,mid=4,0~4,5~8

            int mid = left + (right-left)/2; // 防止越界的寫法

            //int mid = (left+right)/2;


            mergeSort(arr,left,mid,temp,ascending); //左邊歸併排序,使得左子序列有序

            mergeSort(arr,mid+1,right,temp,ascending); //右邊歸併排序,使得右子序列有序


            merge(arr,left,mid,right,temp,ascending); //將兩個有序子陣列合並操作

        }

    }


    private static void merge(int[] arr,int left,int mid,int right,int[] temp,boolean ascending){

        int i = left; //左序列起始下標

        int j = mid+1; //右序列起始下標

        int t = 0; //臨時陣列指標

        while(i

            if(ascending?arr[i]arr[j]){ //比較兩個序列第一個元素誰小,誰小先複製誰到temp,然後對應子序列下標加1

                temp[t++] = arr[i++];

            }else {

                temp[t++] = arr[j++];

            }

        }


        while(i

            temp[t++] = arr[i++];

        }


        while(j

            temp[t++] = arr[j++];

        }


        t = 0;


        //將temp中的元素全部複製到原陣列中

        while(left

            arr[left++] = temp[t++];

        }


    }


}


插入排序

簡單解釋:

最簡單的理解就是打地主時我們拿到牌後的整理過程,從第二個牌(假設我們拿起來這個牌開始比較)開始,(說下升序)從後往前比較如果比前面的那個牌小,就把牌往後移動,直到找到一個合適的位置(這個位置的前面的那個牌不比這個要放下的牌大)就把這個牌放到這個位置,慢慢的前面的部分變得有序,直至全部有序即可。

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class StraghtInsertSort {

    //插入排序

    public static void straghtInsertSort(int[] arr) {

        straghtInsertSort(arr, true);//預設進行升序

    }


    public static void straghtInsertSort(int[] arr, boolean ascending) {


        for (int i = 1; i

            int temp = arr[i];

            int j=0; //這就是那個合適的位置

            for (j = i - 1; j >= 0 && (ascending ? temp arr[j]); j--) {

                arr[j + 1] = arr[j];

            }

            //把牌放下,為啥是j+1,

            //是因為上面的迴圈遍歷到不符合情況的時候 j是合適的位置的前面的那個數的位置

            //有點拗口,但是就是這個意思,看圖方便理解下

            arr[j + 1] = temp;



        }


    }

}


希爾排序

簡單解釋:
希爾排序是插入排序的改進版,我們理解一個叫做下標差的的東西,也就是下面那個圖中的增量d,初始下標差為arr.length/2,然後繼續/2,對在同一下標差(相當於把這幾個數單獨拿出來了)的若干個數進行插入排序即可。

圖片描述

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class ShellSort {


    public static void shellSort(int[] arr) {

        shellSort(arr,true);

    }


    public static void shellSort(int[] arr,boolean ascending) {


        for(int d = arr.length/2;d>0;d/=2){


            for(int i=d;i

                int temp = arr[i];

                int j=0;

                for(j=i-d;j>=0&&(ascending?temparr[j]);j-=d){

                    arr[j+d]=arr[j];

                }

                arr[j+d] = temp;

            }

        }


    }

}


計數排序

簡單解釋:
這個排序演算法看名字也很好理解,就是就是額外找個陣列來計數,然後在這個陣列從小到大或從大到小把數取出來即可。

完整程式碼:


package com.keafmd.Sequence;


public class CountSort {


    public static void countSort(int[]arr){

        countSort(arr,true);

    }


    public static void countSort(int[]arr,boolean ascending){

        int d,min=arr[0],max=arr[0];


        //找出最大、最小值

        for(int i=0;i

            if(arr[i]

                min =arr[i];

            }

            if(arr[i]>max){

                max = arr[i];

            }

        }


        //建立一個用於計數的陣列

        d = min;

        int[] count_map = new int[max-min+1];

        for(int i=0;i

            count_map[arr[i]-d]++;

        }


        int k =0;

        if(ascending){

            for(int i=0;i

                if(count_map[k]>0){

                    arr[i] = k+d;

                    i++;

                    count_map[k]--;

                }else

                    k++;

            }

        }else {

            for(int i=arr.length-1;i>=0;){

                if(count_map[k]>0){

                    arr[i] = k+d;

                    i--;

                    count_map[k]--;

                }else

                    k++;

            }

        }


    }

}


桶排序

簡單解釋:
就是把一個陣列分成幾個桶(其實是幾個區間,從小到大或從大到小的幾個區間)裝,然後讓每個桶(區間)有序,然後取出來放一起就可以了,相當於把幾個有序的段拿出來放一起,自然還是有序的,當然需要是按照區間的順序拿了。

圖片描述

完整程式碼:


package com.keafmd.Sequence;


import java.util.ArrayList;

import java.util.Collections;


public class BucketSort {


    public static void bucketSort(int[] arr){

        bucketSort(arr,true);

    }


    public static void bucketSort(int[] arr,boolean ascending){

        if(arr==null||arr.length==0){

            return;

        }

        //計算最大值與最小值

        int max = Integer.MIN_VALUE;

        int min = Integer.MAX_VALUE;

        for(int i=0;i

            max = Math.max(arr[i],max);

            min = Math.min(arr[i],min);

        }


        //計算桶的數量

        int bucketNUm = (max-min)/ arr.length+1;

        ArrayList> bucketArr = new ArrayList(bucketNUm);

        for(int i=0;i

            bucketArr.add(new ArrayList());

        }


        //將每個元素放入桶中

        for(int i=0;i

            int num = (arr[i]-min)/ (arr.length);

            bucketArr.get(num).add(arr[i]);

        }


        //對每個桶進行排序

        for (int i = 0; i

            //用系統的排序,速度肯定沒話說

            Collections.sort(bucketArr.get(i));

        }


        //將桶中元素賦值到原序列

        int index;

        if(ascending){

            index=0;

        }else{

            index=arr.length-1;

        }


        for(int i=0;i

            for(int j= 0;j

                arr[index] = bucketArr.get(i).get(j);

                if(ascending){

                    index++;

                }else{

                    index--;

                }

            }


        }


    }

}


基數排序

簡單解釋:

首先說一下,我發現好多人寫的基數排序只能排序正整數,其實只要處理下就可以排序含有負數的了,就是我們排序前先把所有的數整體變大(就是減上最小的負數,也就是加了),都變成正數,然後排序好之後,在減下來(加上最小的負數,也就減了)就好了。

基數排序就是按數位排序可分為LSD(從最低位[也就是個位]開始排序)和MSD(從最高位開始排序),下面寫的事LSD基數排序。

基數排序就是把數按位考慮,讓後我們一位數只能是[0,9],就是我們在考慮某位(個位、百位· · ·)的時候就只看這個位的數,放到在[0,9]相應的位置,然後順序取出,最後再按其它位這樣操作(上面說了要不從低位開始到高位,要不就是從高位到低位)

圖片描述

完整程式碼:


package com.keafmd.Sequence;


public class RadixSort {

    public static void radixSort(int[] arr){

        radixSort(arr,true);

    }

    public static void radixSort(int[]arr,boolean ascending){

        int max = Integer.MIN_VALUE;

        int min = Integer.MAX_VALUE;

        //求出最大值、最小值

        for (int i = 0; i

            max = Math.max(max, arr[i]);

            min = Math.min(min, arr[i]);

        }

        if (min

            for (int i = 0; i

                arr[i] -= min;

            }

            max -= min; //max也要處理!

        }

        //很巧妙求出最大的數有多少位

        int maxLength = (max+"").length();

        int[][] bucket = new int[10][arr.length]; //一個二維陣列,一維代表0到9,二維存放符合數

        int[] bucketElementCount = new int[10]; // 用於記錄0到9某位存在數字的個數

        for (int i = 0 ,n = 1 ; i

            for (int j = 0; j

                int value = arr[j]/n % 10;

                bucket[value][bucketElementCount[value]] = arr[j];

                bucketElementCount[value]++;

            }


            //升序

            if(ascending) {

                int index = 0;

                //從左到右,從下到上取出每個數

                for (int j = 0; j

                    if (bucketElementCount[j] != 0) {

                        for (int k = 0; k

                            arr[index] = bucket[j][k];

                            index++;

                        }

                    }

                    bucketElementCount[j] = 0;

                }

            }else { // 降序

                int index=0;

                //從右到左,從下到上取出每個數

                for (int j = bucketElementCount.length-1; j >=0; j--) {

                    if (bucketElementCount[j] != 0) {

                        for (int k = 0; k

                            arr[index] = bucket[j][k];

                            index++;

                        }

                    }

                    bucketElementCount[j] = 0;

                }

            }



           




        }

        if (min

            for (int i = 0; i

                arr[i] += min;

            }

        }


    }

}


完整測試類

package com.keafmd.Sequence;


import java.util.*;

import java.util.stream.IntStream;

import java.util.stream.Stream;


public class Sort {



    public static void main(String[] args) {


        int[] nums = {12, 4, 25, 47, 58, 34, 25, 9, 99, 26, 1, -13, 162, 10093, -66, -1};

//        int[] nums = {12, 43,56,42,26,11};

        int[] temparr;


        //利用系統Collections.sort方法進行對比


        //將int陣列轉換為Integer陣列

        //1、先將int陣列轉換為數值流

        temparr = nums.clone();

        IntStream stream = Arrays.stream(temparr);

        //2、流中的元素全部裝箱,轉換為流 ---->int轉為Integer

        Stream integerStream = stream.boxed();

        //3、將流轉換為陣列

        Integer[] integers = integerStream.toArray(Integer[]::new);

        //把陣列轉為List

        List tempList = new ArrayList(Arrays.asList(integers));

        //使用Collections.sort()排序

        System.out.println("使用系統的Collections.sort()的對比:");


        //Collections.sort

        Collections.sort(tempList, new Comparator() {

            @Override

            public int compare(Integer o1, Integer o2) {

                return o1-o2;

                //return o2-o1;

            }

        });


        //tempList.sort 也可以排序

       


        //遍歷輸出結果

        for (Integer integer : tempList) {

            System.out.print(integer+" ");

        }


        System.out.println();


        //測試氣泡排序

        System.out.println("測試氣泡排序:");

        temparr = nums.clone();


        BubbleSort.bubbleSort(temparr);


        //降序

        //BubbleSort.bubbleSort(temparr,false);


        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


        //測試快速排序

        System.out.println("測試快速排序:");

        temparr = nums.clone();

        QuickSort.quickSort(temparr);

        //QuickSort.quickSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


        //測試直接選擇排序

        System.out.println("測試直接選擇排序:");

        temparr = nums.clone();

        SelectSort.selectSort(temparr);

        //SelectSort.selectSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


        //測試堆排序

        System.out.println("測試堆排序:");

        temparr = nums.clone();

        HeapSort.heapSort(temparr);

        //HeapSort.heapSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


        //測試歸併排序

        System.out.println("測試歸併排序:");

        temparr = nums.clone();

        MergeSort.mergeSort(temparr);

        //MergeSort.mergeSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


        //測試插入排序

        System.out.println("測試插入排序:");

        temparr = nums.clone();

        StraghtInsertSort.straghtInsertSort(temparr);

        //StraghtInsertSort.straghtInsertSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();



        //測試希爾排序

        System.out.println("測試希爾排序:");

        temparr = nums.clone();

        ShellSort.shellSort(temparr);

        //ShellSort.shellSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();



        //測試計數排序

        System.out.println("測試計數排序:");

        temparr = nums.clone();

        CountSort.countSort(temparr);

        //CountSort.countSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();



        //測試桶排序

        System.out.println("測試桶排序:");

        temparr = nums.clone();

        BucketSort.bucketSort(temparr);

        //BucketSort.bucketSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


        //測試基數排序

        System.out.println("測試基數排序:");

        temparr = nums.clone();

        RadixSort.radixSort(temparr);

        //RadixSort.radixSort(temparr,false);

        for (int i = 0; i

            System.out.print(temparr[i] + " ");

        }

        System.out.println();


    }


}

————————————————

版權宣告:本文為CSDN博主「牛哄哄的柯南」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

原文連結:https://blog.csdn.net/weixin_43883917/article/details/118193663?spm=1001.2014.3001.5501


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4662/viewspace-2797471/,如需轉載,請註明出處,否則將追究法律責任。

相關文章