Java8大排序算法


一.冒泡排序

  基本思想:通過對待排序序列此前向后,依次比較相鄰元素的值,若發現逆序則進行交換,使得較大的值從前面移動到后面,

          類似於水下的氣泡一樣(是所有排序算法中效率最低的)                                                         

 

                   

public static void BobbleSort(int[] arr){
        /*冒泡排序,時間復雜度為O(n^2)*/
        if (arr == null || arr.length == 0){
            return;
        }
        int temp = 0; // 臨時變量,用於存放大的數
        boolean flag = false; // 是否進行過交換,默認為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;// 
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            if (!flag){ // 如果flag==false,表示沒有進行過交換,直接退出即可
                break;
            }else{
                flag = false; // 要將flag重置,進行下一次的判斷
            }

        }
    }
}

測試,使用80000個隨機數來進行測試

public static void main(String[] args) {
        int[] array = createRandomArr(80000);
        showTime("排序開始時間");

        BobbleSort(array);
        showTime("排序結束時間");
    }

    public static void showTime(String str){
        Date d = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dStr = sdf.format(d);
        System.out.println(str+": "+dStr);
    }

    public static int[] createRandomArr(int n){
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = (int)(Math.random() * 800000);
        }
        return arr;
    }

執行結果: 排序耗時9秒

 

 

二.選擇排序

  基本思想:第一次從arr[0]~arr[n-1]中選出最小值和arr[0]進行交換,第二次從a[1]~a[n-1]選出最小值和a[1]進行交換,第三次從a[2]~a[n-1]選出

       最小值和a[2]進行交換,直到執行n-1次,得到一個排序碼從小到大的有序序列

  

public static void selectSort(int[] arr){
        /*選擇排序*/
        if (arr.length == 0 || arr == null){return;}
        int minIndex = 0;
        int minValue = 0;
        for (int i = 0; i < arr.length-1; i++) {
            minIndex = i;   // 記錄最小值的下標,從0開始
            minValue = arr[i]; // 記錄最小值,假設是a[0]開始
            for (int j = i+1; j < arr.length;j++){ // 從i后開始循環
                if (minValue > arr[j]){ // 如果最小的值,並不是a[i],重置minIndex和minValue
                    minValue = arr[j]; // 獲取最小值,和最小值的下標
                    minIndex = j;
                }
            }
            // 將最小的值放在a[i],比較並進行交換
            if (minIndex != i){
                arr[minIndex] = arr[i]; // 把a[0]第一個值先放在a[minIndex]處
                arr[i] = minValue; // 把保存下來的最小值回填到a[0],即找到了全局的最小值
            }
        }

    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時2秒

 

 三.插入排序

   基本思想:把n個待排列的元素看成一個有序表和一個無序表,開始時有序表中只包含一個元素,無序表中包含n-1個元素,排序過程中每次從無序表中抽取

      第一個元素,將它的排序碼依次與有序表元素的排序碼進行比較,將其插入到有序表中的適當的位置,成為新的有序表

public static void insertSort(int[] array){
        /*插入排序*/
        if (array.length == 0 || array == null){return;}
        int insertVal = 0;
        int insertIdx = 0;

        for (int i = 1; i < array.length; i++) {
            // 定義待插入的數
            insertVal = array[i];// 從第二個數開始和第一個數進行比較
            insertIdx = i -1;     // 第一個數的下標
            // 給insertVal找到合適的位置
            // 1.insertIdx >=0保證給insertVal插入的位置不越界
            // 2.insertVal < array[insertIdx] 找到了待插入的數
            // 3.需要將arr[insertIdx]后移
            while (insertIdx >=0 && insertVal < array[insertIdx]){
                array[insertIdx+1] = array[insertIdx];
                insertIdx--;
            }
            // 退出while循環時候說明數已經找到,只要把保留下來的數放到前面即可
            if (insertIdx+1 != i){
                array[insertIdx+1] = insertVal;
            }
            
        }
    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時不到1秒

 

 四.希爾排序

   基本思想:當插入排序插入的數較小時,會導致頻繁移動數組導致執行效率低下,希爾排序是一種優化的插入排序.它將數據按下標的一定增量分組

      對每組直接進行簡單插入排序,隨着增量逐漸減少,每組包含的關鍵詞就越多,當增量減少到1時,所有的數據都已排序完畢

public static void shellSort2(int[] arr){
        /*使用移位法進行希爾排序*/
        if (arr.length == 0 || arr == null){
            return;
        }
        int minVal = 0;  // 記錄最小值
        int minIdx = 0;  // 記錄最小值的下標
        for (int gap = arr.length/2; gap >0 ; gap/=2) { // 每次縮小遍歷次數增量 每次折半,縮小增量
            for (int i = gap; i < arr.length; i++) { // 從gap個位置,逐步對其所在的元素進行插入排序
                minVal = arr[i]; // 假定最小值是arr[i]
                minIdx = i;   // 記錄i
                //1.minIdx-gap >=0 確保插入的位置不會越界
                //2.minVal < arr[minIdx-gap] 找到了待插入的數
                while (minIdx-gap >=0 && minVal < arr[minIdx-gap]){
                    // 同插入排序
                    arr[minIdx] = arr[minIdx-gap]; 
                    minIdx-=gap;
                }
                arr[minIdx] = minVal;
            }
        }

    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時不到1秒

 

 五.快速排序

    基本思想:通過一趟排序將要排序的數據分割成獨立的兩個部分,其中一部分的數據比另一部分的數據都要小,然后在按照此方法對這兩部分數據

       進行快速排序,整個排序通過遞歸實現,以此達到整個數據排列有序

/**
     * 快速排序
     * @param arr 需要排序的數組
     * @param left   左索引
     * @param right  右索引
     */
    public static void quickSort(int[] arr,int left,int right){
        if (arr.length == 0 || arr == null){return;}

        int l = left;
        int r = right;
        int temp = 0;  // 作為交換的臨時變量
        int pivot = arr[(left+right)/2];   // 找到數組中間的值
        //while循環讓比pivot值小的放在左邊,比pivot值大的放在右邊
        while (l<r){
            //在pivot的左邊一直找,直到找到比pivot大的值就退出
            while (arr[l] < pivot){
                l+=1;
            }
            //在pivot的右邊一直找,直到找到比pivot小的值就退出
            while (arr[r] > pivot){
                r-=1;
            }
            // 如果l>=r說明左右兩邊的值已經按照左邊全是小於pivot,右邊都是大於pivot的值來存放
            if (l >= r){
                break;
            }
            //交換
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;

            // 如果交換完成后,發現pivot == arr[l],需要將r--,前移
            if (arr[l] == pivot){
                r-=1;
            }
            // 如果交換完成后,發現pivot == arr[r],需要將l++,后移
            if (arr[r] == pivot){
                l+=1;
            }
        }
        // 如果出現 l==r,必須要把l++,r--,否則會出現棧溢出
        if (l == r){
            l+=1;
            r-=1;
        }
        // 向左遞歸
        if (left < r){
            quickSort(arr,left,r);
        }
        // 向右遞歸
        if (right > l){
            quickSort(arr,l,right);
        }
    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時不到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 mid =( left+right )/2;
            // 向左遞歸
            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; // 初始化i,左邊有序序列的初始索引
        int j = mid + 1; // 初始化j,右邊有序序列的初始索引
        int t = 0; // 臨時數組的索引

        /*1.將左右兩邊的有序數據按照規則填充到temp中,直到左右兩邊的有序序列有一邊處理完為止*/
        while (i <= mid && j <= right){ // 繼續
            if (arr[i] <= arr[j]){ //如果左邊的有序序列當前的元素小於右邊有序序列當前的元素
                temp[t] = arr[i]; // 將左邊有序序列的元素填充到temp中
                t+=1;
                i+=1;
            }else{  //如果右邊的有序序列當前的元素小於左邊有序序列當前的元素
                temp[t] = arr[j];// 將右邊有序序列的元素填充到temp中
                t+=1;
                j+=1;
            }
        }

        /*2.把剩余的數據的一邊依次全部填充到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數組的元素拷貝到arr中,每次都要拷貝*/
        t = 0; // t要清空 int tempLeft = left;
        while (tempLeft <= right){
            arr[tempLeft] = temp[t];
            t+=1;
            tempLeft+=1;
        }

    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時不到1秒

 

 

 七.基數排序

   基本思想:將所有待比較的數統一為同樣的數位長度,數位較短的數前補零.然后從最低位開始,依次進行一次排序.這樣從最低位排序一直到最高位排序完成后

      數列就成了有序序列了.

public static void radixSort(int arr[]){
        /*基數排序算法*/
        if (arr.length == 0 || arr == null ){
            return;
        }
        /*1.找出數組中的最大的數*/
        int max = arr[0];
        for (int ele : arr) {
            if(ele > max){
                max = ele;
            }
        }

        /*2.確定最大數的長度,確定桶排序的循環次數*/
        int maxLength = (max+"").length();


        /*定義一個二維數組表示10個桶,每個桶就是一個一維數組
        * 1.二維數組中包含10個一維數組
        * 2.為了防止放數據導致數據溢出,則每一個一維桶的長度是arr.length
        * */
        int[][] bucket = new int[10][arr.length];

        /*記錄每個桶中存放的有效數據的個數
        * 比如:bucketElementCount[0],記錄的就是bucket[0]中元素的個數
        * */
        int[] bucketElementCount = new int[10];


        // n表示對數據的位數進行處理,第一次個位,第二次十位,第三次百位,以此類推
        for (int i = 0,n=1; i < maxLength; i++,n*=10) {
            for (int j = 0; j < arr.length; j++) {
                // 取出每個數對應的位數
                int digitOfElement = arr[j] / n % 10;
                // 放入到對應的桶中
                bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j];   
                bucketElementCount[digitOfElement]++;  // 每一個桶的記錄數遞增
            }

            /* 按照桶的順序,放回到原來的數組*/
            int index = 0;
            //遍歷每一個桶,並將桶的數據放回到原數組
            for (int k = 0; k < bucketElementCount.length; k++) {
                // 如果桶中有數據,才放入
                if (bucketElementCount[k] != 0){
                    // 循環第k個桶,放入
                    for (int l = 0; l < bucketElementCount[k]; l++) {
                        // 取出元素放入到原數組中
                        arr[index++] = bucket[k][l];
                    }
                }
                // 每次處理完成后,一定要記得將bucketElementCount[k]清空
                bucketElementCount[k] = 0;

            }

        }

    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時不到1秒

 

 

八.堆排序

   基本思想:將待排序的序列構造成一個大頂堆.此時整個序列的最大值就是堆頂的根節點,把它和末尾的元素進行交換.此時末尾的元素就成了最大值.然后將剩余n-1個元素重新構造成一個

      堆.如此反復執行就能到一個有序的序列

/**
     * 把一個二叉樹,調整為大頂堆
     * @param array  待調整的數組
     * @param i      表示非葉子節點在數組中的索引
     * @param length 表示有多少個元素要調整,length逐漸在減少
     */
    public static void adjustHeap(int[] array,int i,int length){
        /*取出當前元素的值,保存在臨時變量中*/
        int temp = array[i];
        /*開始進行調整*/
        /* k=2*i+1,k是i的左子節點*/
        for (int k = 2*i+1;k < length; k = k*2+1){
            /*如果左子節點的值小於右子節點的值*/
            if (k+1 < length && array[k] < array[k+1]){
                k++; // k指向右子節點
            }
            /*如果子節點大於父節點*/
            if (temp < array[k]){
                /*把較大的值賦給當前節點*/
                array[i] = array[k];
                /*i指向k,繼續循環比較*/
                i = k;
            }else {
                break;
            }
        }
        /*當循環結束后,已經把i為父節點的數的最大值放在了頂部,所以此時把temp的值放在調整后的位置*/
        array[i] = temp;
    }
public static void heapSort(int[] array){
        if (array.length == 0 || array == null){
            System.out.println("數組為空,不能排序");
            return;
        }
        int temp = 0;
        /*把一個無序的序列構建成一個堆,根據升序和降序選擇大頂堆還是小頂堆*/
        for (int i = array.length/2-1; i >= 0; i--) {
            adjustHeap(array,i,array.length);
        }

        /**
         * 1.將堆頂的元素和堆底的元素進行交換,把最大的元素放在數組的尾部
         * 2.重新調整結構,然后繼續交換堆頂元素和當前末尾元素,反復執行,使得數組有序
         */
        for (int j = array.length-1; j > 0; j--) {
            /*交換*/
            temp = array[j];
            array[j] = array[0];
            array[0] = temp;
            adjustHeap(array,0,j);
        }

    }

測試,使用80000個隨機數來進行測試

執行結果: 排序耗時不到1秒

 

 

八大排序算法的時間復雜度,空間復雜的比較

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM