日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

几种常见的排序算法总结

發(fā)布時間:2023/11/23 windows 46 coder
生活随笔 收集整理的這篇文章主要介紹了 几种常见的排序算法总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

常見的幾種排序算法

排序算法有很多,比較常見的有:冒泡排序、選擇排序、插入排序、希爾排序、歸并排序、快速排序、堆排序、計數(shù)排序、桶排序、基數(shù)排序等。并不是所有的都需要會。

本文只會對其中部分算法進行總結(jié)。

冒泡排序

冒泡排序是一種比較簡單的排序方法。也比較好理解,但是通常情況下性能不是很好。在冒泡排序中,序列中的每個數(shù)據(jù)就是水中的泡泡一樣,一個個的向上冒出來,直到冒出水面(達到最大位置)。

(PS:此處說的是從小到大排序,而從大到小排列只需要換個思路)

算法步驟

1、從開頭到結(jié)尾遍歷數(shù)組,比較相鄰的元素。如果前一個比后一個大,就交換他們兩個。

         point
           |
nums = [4,35,23,34,5,4]
// point 此時發(fā)現(xiàn) nums[point] 比 nums[point + 1] 小,調(diào)換他倆的位置。

2、對每一個相鄰的數(shù)據(jù)進行對比,直到序列結(jié)尾的最后一對,此時“最大值”已經(jīng)被移動到了“最后一個位置”。

                point
                  |
nums = [4,23,34,5,35,4]
// 當 point 到達倒數(shù)第二個位置,此時發(fā)現(xiàn) nums[point] 比 nums[point + 1]小
// 調(diào)換她倆位置后,就把 35 放到了最后一個,此時最大值已經(jīng)找出。

3、重復 1和2 操作。但是每次做完 1和2 操縱后,需要遍歷的數(shù)就少一個(最后一個),因為每次都會有一個最大值已經(jīng)被排好了放到了最后。

實現(xiàn)

Java 實現(xiàn)

public class BubbleSort {

    public static void main(String[] args) {
        int[] nums = {12,123,432,23,1,3,6,3,-1,6,2,6};;
        sort(nums);
        System.out.printf("finish !");
    }

    public static void sort(int[] nums){

        int temp ;
        for(int len = nums.length ; len > 0; len --){
            // 第一層遍歷 len 是需要排序的數(shù)組長度。
            for(int i = 0 ; i < len - 1 ; i++){
                // 第二層遍歷,遍歷的數(shù)據(jù),每次都少一。
                // 但是每次都會把一個最大值放到最后 nums[len - 1] 的位置。
                if(nums[i] > nums[i + 1]){
              
                    temp = nums[i];
                    nums[i] = nums[i + 1];
                    nums[i + 1] = temp;
                }

            }
        }
    }
}

選擇排序

選擇排序是一種直觀的排序方法。他和冒泡排序一樣,需要多次遍歷序列。不過冒泡排序,是將最大值挨個的替換相鄰數(shù)據(jù)(冒泡)的方式最后放到最大值的位置的。而選擇排序,通過一個指針(point),標記了最大值所在的索引位置。當遍歷到最后的時候,將標記的最大值所在的位置與最后一個數(shù)交換。

算法步驟

1、從頭到尾的遍歷數(shù)列,遍歷過程中,用一個指針記錄最大值(最小值)所在的位置。
2、將最大值所在位置的數(shù)據(jù)與最后一個交換。
3、重復 1和2 步,每次重復后,需要遍歷的數(shù)列長度就減 1。

實現(xiàn)

Java 版

public class SelectionSort {

    public static void main(String[] args) {
        int[] nums = {12,123,432,23,1,3,6,3,-1,6,2,6};;
        sort(nums);
        System.out.printf("finish !");
    }

    public static void sort(int[] nums){
        int max ; // 最大數(shù)所在的位置。
        int temp;
        for(int len = nums.length ; len > 0; ){
            max = 0;
            for(int i = 0 ; i < len ; i++){
                if(nums[i] > nums[max]){
                    max = i;
                }
            }
            temp = nums[max];
            nums[max]= nums[len - 1];
            nums[ --len] = temp;
        }

    }

}

我們發(fā)現(xiàn),選擇排序每次找最大值的時候,都要遍歷剩下的所有元素。我們有什么方法可以優(yōu)化每次查找最大(最?。┲档乃俣饶??后面會講到的“堆排序”就是為了優(yōu)化查找最大值的。

插入排序

插入排序的思想是將一個待排序的元素,插入到一個已經(jīng)排序好的元素的指定位置。比如我們打撲克牌的時候,每次拿到一張牌,我們會將他插入到手中已經(jīng)排好順序的手牌中,這樣當我們拿到所有的撲克牌后,手中自然就有序了。

對比到到具體的編程中,我們可以用一個指針將一個序列分割成左右兩部分,左邊認為是已排序號(手中的牌),右邊每次取一個放到左邊的序列中。

算法步驟

有如下數(shù)組:[9,3,4,2]

1、用一個指針 i ,指向數(shù)組的 1 的位置。此指針將數(shù)組分為左右兩邊 [9] 和 [3,4,2]。此時左邊只有一個數(shù),所以是有序的,右邊是無序的。

   i
   |
[9,3,4,2]

2、將 3 依次與前面有序的數(shù)對比,如果比前面的數(shù)小,就將兩個位置上的數(shù)交換直到把 i 位置的數(shù)放到正確的位置。

   i      
   |
[9,3,4,2]    

此時nums[i] < nums[i-1],交換兩個數(shù)。

[3,9,4,2]

3、將 i 向后移一位,然后重復 2操作。

     i      
     |
[3,9,4,2]   

實現(xiàn)

Java 版

public class InsertSort {

    public static void main(String[] args) {

        int[] nums = {5,1,4,6,2,66,-1,34,-9,8};

        sort(nums);

        System.out.println("finish!");

    }


    public static void sort(int[] nums){

        for(int i = 1 ; i < nums.length ; i++){

            for(int j = i ; j > 0; j--){

                if(nums[j] < nums[j - 1]){
                    swap(nums,j,j - 1);
                }else{
                    break;
                }

            }

        }

    }

    public static void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }


}

插入排序有一個優(yōu)化版本“希爾排序”,本文中就不詳細講了,感興趣的可以去搜一下。

歸并排序

要將一個數(shù)組排序,可以先將它分成兩半分別排序,然后再將結(jié)果合并(歸并)起來。這里的分成的兩半,每部分可以使用其他排序算法,也可以仍然使用歸并排序(遞歸)。

我看《算法》這本書里對歸并算法有兩種實現(xiàn),一種是“自頂向下”,另一種是“自底向上”。這兩種方法,個人認為只是前者用了遞歸的方法,后者使用兩個 for 循環(huán)模擬了遞歸壓棧出棧的算法,本質(zhì)上還是相同的(如果理解錯誤,還望大佬指出)。

算法步驟

1、將要排序的序列分成兩部分。
2、將兩部分分別各自排序。然后再將兩個已經(jīng)排序好的序列“歸并”到一起,歸并后的整個序列就是有序的。
3、將兩個有序的序列歸并的步驟:
3.1、先申請一個空間,大小足夠容納兩個已經(jīng)排序的序列。
3.2、設定兩個指針,最初位置分別為兩個已經(jīng)排序序列的起始位置。
3.3、比較兩個指針所指向的元素,選擇相對小的元素放入到合并空間,并移動指針到下一位置。
3.4、重復3.3 步驟。

歸并排序,比較重要的是“分治”思想和“歸并”的操作。

歸并操作,是將兩個“有序”的序列,合并成一個有序的序列的方法。而這兩個有序的序列,又是根據(jù)“分治”思想將一個學列分割成的兩部分(將一個序列不斷的分隔,到最后就剩一個的時候他自然就是有序的)。

實現(xiàn)

Java 版

public class MergeSort {


    public static void main(String[] args) {

        int[] nums = {12,123,432,23,1,3,6,3,-1,6,2,6};;

        sort(nums,0,nums.length -1);

        System.out.printf("finish!");

    }

    public static void sort(int[] nums,int left,int right){
        if(left >= right){
            return;
        }
        // 遞歸的將左半邊排序
        sort(nums,left,right - left / 2 - 1); 
        // 遞歸的將右半邊排序
        sort(nums,right - left / 2 ,right);
        // 此時左右半邊都分別各自有序,再將其歸并到一起。
        // 如:[1,9,10    ,  3,7,8]
        merge(nums,left,right - left / 2,right);
    }
    // 此方法叫做原地歸并,將數(shù)組 nums 根據(jù) mid 分隔開,左右看作是兩個數(shù)組。
    // 類似于 merge(int[] nums1,int[] nums2),將 nums1 和 nums2 歸并
    public static void merge(int[] nums ,int left,int mid,int right){

        int i = left,j = mid;

        int[] temp_nums = new int[nums.length];
        for(int key = left ; key <= right; key++)
            // 將原來數(shù)組復制到臨時數(shù)組中。
            temp_nums[key] = nums[key];

        for(int key = i ; key <= right; key++){
            if(i > mid){
                nums[key] = temp_nums[j++];
            } else if (j > right) {
                nums[key] = temp_nums[i++];
            } else if (temp_nums[i] > temp_nums[j]) {
                nums[key] = temp_nums[j++];
            }else{
                nums[key] = temp_nums[i++];
            }
        }

    }

}

快速排序

快速排序是一種分治的排序算法,它將一個數(shù)組分成兩個子數(shù)組,將兩部分獨立的排序

快速排序可能是應用最廣泛的排序算法了。快速排序流行的原因是它實現(xiàn)簡單、適用于各種不同的輸入數(shù)據(jù)且在一般應用中比其他排序算法都要快得多。——《算法(第四版)》

算法步驟

  1. 從數(shù)列中挑出一個元素,稱為 "基準"(pivot);
  2. 所有元素比"基準"值小的擺放在前面,所有元素比"基準"值大的擺在后面,相同的數(shù)可以到任一邊。這個稱為分區(qū)(partition)操作。
  3. 遞歸地(recursive)使用同樣的方法把小于基準值元素的子數(shù)列和大于基準值元素的子數(shù)列排序;

算法過程

1、給定一個亂序的數(shù)組

[5,1,4,6,2,66,34,8]

2、選擇第一個為基準數(shù),此時把第一個位置置空。兩個指針,left從左到右,找比 piovt “大”的數(shù);right 從右向左,找比 piovt “小”的數(shù)。

left            right
 |               |
[_,1,4,6,2,66,34,8]
 |
piovt = 5

3、right 從右向左(<-),找比 piovt “小”的數(shù) 2。

  left right
   |     |
[_,1,4,6,2,66,34,8]
 |
piovt = 5

4、left從左到右(->),找到了比 piovt 大的數(shù) 6。

    left  right
       | |
[_,1,4,6,2,66,34,8]
 |
piovt = 5

5、此時將 left 和 right 上的數(shù)對調(diào)。

    left right
       | |
[_,1,4,2,6,66,34,8]
 |
piovt = 5

6、right 繼續(xù)向左查找,直到 left = right。(正常情況下要重復 4、5 步驟多次才會得到 left = right)
此時將 left 位置的數(shù)放到原來 piovt 位置上,將 piovt 放到 left 位置上。

      left
      right
       |
[2,1,4,5,6,66,34,8]
 -     -
 |
piovt = 5

7、此時將整個數(shù)組根據(jù) piovt 分割成兩個部分,左邊都比 piovt 小,右邊都比 piovt 大。遞歸的處理左右兩部分。

實現(xiàn)

Java 版


public class QuickSort {

    public static void main(String[] args) {
        int[] nums = {5,1,4,6,2,66,34,8,34,534,5};

        int[] sorted = sort(nums,0 , nums.length - 1);
        
        System.out.println("finish!");
    }

    // 排序
    public static int[] sort(int[] nums , int left , int right){

        if(left <= right){ 

            // 將 nums 以 mid 分成兩部分
            // 左邊的小于 nums[min]
            // 右邊的大于 nums[min]
            int mid = partition(nums,left,right);
            // 遞歸
            sort(nums,left,mid - 1);
            sort(nums,mid + 1 ,right);

        }

        return nums;
    }

    public static int partition(int[] nums , int left , int right){
        //int pivot = left;
        int i = left , j = right + 1; // 左右兩個指針
        int pivot = nums[left]; // 基準數(shù),比他小的放到左邊,比他大的放到右邊。

        while ( true ){

            // 從右向左找比 pivot 小的。
            while (j > left && nums[--j] > pivot){
                if(j == left){
                    // 到頭了
                    break;
                }
            }

            // 先從左向右找比 pivot 大的。
            while (i < right && nums[ ++ i] < pivot ){
                if( i == right){
                    // 到頭了
                    break;
                }
            }

            if(i >= j ) break;

            // 交換 i 位置和 j 位置上的數(shù)
            // 因為此時 nums[i] > pivot 并且 nums[j] < pivot
            swap(nums,i , j);

        }
        // 由于 left 位置上的數(shù)是 pivot=
        // 此時 i = j 且 nums[i/j] 左邊的數(shù)都小于 pivot , nums[i/j] 右邊的數(shù)都大于 pivot。
        // 此時交換 left 和 j 位置上的數(shù)就是將 pivot 放到中間
        swap(nums,left,j);

        return j ;
    }
    
    // 交換數(shù)組中兩個位置上的數(shù)
    public static void swap(int[] nums , int i1 , int i2){
        int n = nums[i1];
        nums[i1] = nums[i2];
        nums[i2] = n;
    }


}

堆排序

堆排序主要是利用“堆”這種數(shù)據(jù)結(jié)構(gòu)的特性來進行排序,它本質(zhì)上類似于“選擇排序”。都是每次將最大值(或最小值),找出來放到數(shù)列尾部。不過“選擇排序”需要遍歷整個數(shù)列后選出最大值(可以到上面再熟悉下選擇排序算法),“堆排序”是依靠堆這種數(shù)據(jù)結(jié)構(gòu)來選出最大值。但是每次重新構(gòu)建最大堆用時要比遍歷整個數(shù)列要快得多。

堆排序中用到的兩種堆,大頂堆和小頂堆:

1、大頂堆:每個節(jié)點的值都大于或等于其子節(jié)點的值(在堆排序算法中一般用于升序排列);
2、小頂堆:每個節(jié)點的值都小于或等于其子節(jié)點的值(在堆排序算法中一般用于降序排列);

我們給樹的每個節(jié)點編號,并將編號映射到數(shù)組的下標就是這樣:

該數(shù)組從邏輯上是一個堆結(jié)構(gòu),我們用公式來描述一下堆的定義就是:

1、大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
2、小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

這里只要求父節(jié)點大于兩個子節(jié)點,并沒有要求左右兩個子節(jié)點的大小關(guān)系。

算法過程

1、將一個 n 長的待排序序列arr = [0,……,n-1]構(gòu)造成一個大頂堆。
2、此時數(shù)組的 0 位置(也就是堆頂),就是數(shù)組的最大值了,將其與數(shù)組的最后一個數(shù)交換。
3、將剩下 n-1 個數(shù)重復 1和2 操作,最終會得到一個有序的序列。

堆排序是“選擇排序”的一種變體,算法中比較難的地方是用數(shù)組構(gòu)建“大頂堆”或“小頂堆”的過程。

實現(xiàn)堆排序前,我們要知道怎么用數(shù)組構(gòu)建一個邏輯上的最大堆,這里會用到幾個公式(假設當前節(jié)點的序號是 i,可以結(jié)合上圖理解下下面的公式):

1、左子節(jié)點的序號就是:2i + 1;
2、右子幾點的序號就是:2i + 2;
3、父節(jié)點的序號就是:(i-1) / 2 (i不為0);

實現(xiàn)

Java 版

public class HeapSort {

    static int temp ;

    public static void main(String[] args) {

        int[] nums = {5,1,4,6,2,66,-1,34,-9,8};

        sort(nums);

        System.out.println("finish!");
    }


    public static void sort(int[] nums){


        // 第一步要先將 nums 構(gòu)建成最大堆。
        for(int i = (nums.length - 1) / 2 ; i >= 0; i-- ){
            //從第一個非葉子結(jié)點從下至上,從右至左調(diào)整結(jié)構(gòu)
            maxHeapify(nums,i,nums.length);
        }

        // 遍歷數(shù)組
        // j 是需要排序的數(shù)組的最后一個索引位置。
        for(int j = nums.length - 1 ; j > 0 ; j --){
            // 每次都調(diào)整最大堆堆頂(nums[0]),與數(shù)組尾的數(shù)據(jù)位置(nums[j])。
            swap(nums,0,j);
            // 重新調(diào)整最大堆
            maxHeapify(nums,0,j);
        }


    }

    /**
     * 將 nums 從 i 開始的 len 長度調(diào)整成最大堆。
     * (注意:此方法只適合調(diào)整已經(jīng)是最大堆但是被修改了的堆,或者只有三個節(jié)點的堆)
     * len :需要計算到數(shù)組 nums 的多長的地方。
     * i :父節(jié)點在的位置。
     */
    public static void maxHeapify(int[] nums,int i , int len){

        // 是從左子節(jié)點開始
        int key = 2 * i + 1;

        if(key >= len){
            // 說明沒有子節(jié)點。
            return;
        }

        // key + 1 是右子節(jié)點的位置。
        if(key + 1 < len && nums[key] < nums[key + 1]){
            // 此時說明右節(jié)點比左節(jié)點大。
            // 此時只要將父節(jié)點跟 右子節(jié) 點比就行了。
            key += 1;
        }

        if(nums[i] < nums[key]){
            // 子節(jié)點比父節(jié)點大,交換子父界節(jié)點的數(shù)據(jù),將父節(jié)點設置為最大。
            swap(nums,i,key);
            // 此時子節(jié)點上的數(shù)變了,就要遞歸的再去,計算子節(jié)點是不是最大堆。
            maxHeapify(nums,key,len);
        }

    }

    /**
     * 交換 i 和 j 位置的數(shù)據(jù)
     */
    public static void swap(int[] nums,int i,int j){
        temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

maxHeapify 這個方法有很多種實現(xiàn),這里用了個比較容易理解的遞歸實現(xiàn)。我看 dreamcatcher-cx 大佬寫了一種更好的實現(xiàn)方法,比較難理解一點,但是更高效,感興趣的見【參考4】。

參考

1、算法(第四版),by Robert Sedgewick/Kevin Wayne。
2、十大經(jīng)典排序算法,by runnoob.com。
3、神級基礎排序——快速排序,by 江神。
4、圖解排序算法(三)之堆排序,by dreamcatcher-cx。

總結(jié)

以上是生活随笔為你收集整理的几种常见的排序算法总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。