排序算法合集(冒泡,選擇,插入,堆排,快排)


1、冒泡排序

    最初在學c語言時,老師就教的這個排序算法,原理比較簡單:從數組下標為0處開始遍歷,相鄰之間進行比較,若a[i]>a[i+1],則exchange(a[i],a[i+1]),當然也可以將小的往后傳遞,將此過程不斷進行,那么最后數組就有序了。

要點:(1)每遍歷一遍,末尾就得到一個最大值(或最小值),那么接下來的遍歷是不是每次都減少一個元素就好了,因為后邊的已經排好序了啊。

      (2)遍歷n-1遍就排好序了,因為最后一遍只剩一個元素了,它一定放那兒,所以最后一遍就不用遍歷了。

當然如果數據小,又懶得優化,多進行幾遍也一樣可以排序的,比如這樣:

     

 1 for(int i=0;i<n;i++){                              //遍歷了n遍
 2 
 3           for(int j=0;j<n-1;j++){                     //每次比較當前元素和下一個元素,所以循環結束條件為n-1
 4 
 5              {
 6 
 7                  if(a[j]>a[j+1])
 8 
 9                  {    int t=a[j];
10 
11                       a[j]=a[j+1];
12 
13                       a[j+1]=t;    }
14 
15               }
16 
17        }

 

 

 

 那么標准的冒泡排序,就是算法復雜度為n*(n-i)次也就是n^2如下:

 

 1        for(int i=0;i<a.length-1;i++){                              //遍歷n-1遍就夠了
 2 
 3           for(int j=0;j<a.length-i-1;j++){               //每次比較當前元素和下一個元素,每遍歷一遍少比較一個元素,所以循環結束條件為n-i-1
 4 
 5              {
 6 
 7                  if(a[j]>a[j+1])
 8 
 9                  {    int t=a[j];
10 
11                       a[j]=a[j+1];
12 
13                       a[j+1]=t;    }
14 
15               }
16 
17        }

 

 

 

2、選擇排序

    這個也是學c語言的時候和冒泡一起學的,它和冒泡算法復雜度一樣,原理為:從數組下標為0處開始遍歷,每次取出剩下元素的最大值(或最小值)放在當前位置,也就是說,第一次取最大的放第一個位置,第二次取次大的放第二個位置....執行n-1次就好了,最后一個只能放最后了。

標准選擇排序代碼如下:

          

 1  for(int i=0;i<a.length-1;i++){               //遍歷n-1遍就夠了
 2 
 3               int k=i;                                 //k為剩下元素中的最大值下標,初始化為當前位置
 4 
 5               for(int j=i+1;j<a.length-1;j++){        //找出剩下元素中的最大值下標
 6 
 7                  if(a[j]>a[k])
 8 
 9                     k=j;    
10 
11                  }
12 
13              if(k!=i){                               //若最大值不是當前位置的值,則交換,每次將最大值放前邊
14 
15               int t=a[k];
16 
17                   a[k]=a[i];
18 
19                   a[i]=t;  
20 
21                 }
22 
23           }

 

 

 

3、插入排序

   適用於較少元素時,效率比較高,原理:從數組下標為0處開始遍歷,每次保證已經遍歷的元素為有序序列,即每次將要遍歷的數插入到前邊數列的合適位置,保證已經遍歷的元素為有序數列。如:遍歷第一個元素,因為目前前邊只有這一個元素,所以它是有序的,遍歷第二個元素,和第一個元素比較,看它插在他前邊還是后邊,這樣就保證已經遍歷的元素都有序了,之后的類似,和前邊的比較,然后插入合適的位置。

   插入排序代碼如下:

     

 1  for(int i=1;i<a.length-1;i++){       //從下標1開始,一個元素的時候一定有序啊
 2 
 3       int j=i-1;                          // j初始值為前一個元素,因為要將前邊的元素一一和當前元素比較
 4 
 5       int k=a[i];                         //記錄當前位置元素的值,因為如果后邊要移動的話會覆蓋的
 6 
 7       while(j>0&&a[j]>k){                //將大於當前元素值的都依次向前移一位,好空出適合的位置給當前元素值
 8 
 9           a[j+1]=a[j];
10 
11           j--;
12 
13       }
14 
15      a[j+1]=k;                          //因為j處元素值小於等於k (j處跳出循環的),所以k的適合位置為j+1
16 
17    }

 

 

 

4、堆排序

   要想理解堆排序,首先你要知道最大堆,要想理解最大堆,你得知道二叉樹。

  二叉樹:每個節點最多有倆個孩子節點。

  最大堆:父親節點的值總是大於孩子節點的值。

  當然在這里二叉樹的存儲結構不是鏈表,是使用數組存的:(1)數組下標為0處是根節點。

                                                      (2)父親節點下標*2為左孩子下標,父親節點下標*2+1為右孩子下標。

                                                        根據這倆條准則我們就可以將二叉樹存在數組了。

堆排序原理:我們知道最大堆的性質(父親節點的值總是大於孩子節點的值),那么根節點處不就是當前數列的最大值嗎,那么我們每次取根節點的值放在末尾,然后將最大堆的大小-1,更新最大堆,取根節點放后邊.....不斷執行這個過程,直到最大堆中只剩一個元素,此時數組就是一個有序數組了。

根據原理可以看出我們需要編的操作有(1)建最大堆  (2)更新最大堆,其實建立最大堆就是不斷更新最大堆的過程,如果我們將每個結點都執行一遍更新最大堆操作(即父親節點的值總是大於孩子節點的值,不符合的話將父親節點與最大的孩子交換位置),當然執行順序必須是從下往上,然后只需從非葉子節點開始執行就好了(非葉子節點就是有孩子的結點)。

  堆排序代碼如下:

       

 1 //更新最大堆操作
 2 
 3           void dfDui(int x) {                         //參數為父親節點下標
 4 
 5             int lchild=x*2;                           //左孩子下標
 6 
 7             int rchild=x*2+1;                         //右孩子下標
 8 
 9             int max=x;                                //最大值下標初始為父親下標
10 
11           if(lchild<size&&a[lchild]>a[max])           //比較找出最大值
12 
13                max=lchild;
14 
15           if(rchild<size&&a[rchild]>a[max])
16 
17               max=rchild;
18 
19           if(max!=x){           //若父親節點為最大值,則符合性質,否則交換,將最大值移到父親節點處,然后因為孩子節點處已改變,更新此節點。
20 
21             int t=a[max];
22 
23                 a[max]=a[x];
24 
25                 a[x]=t;
26 
27             dfDui(max);
28 
29            }
30 
31        }
32 
33    //建最大堆操作
34 
35           void creatDui(){
36 
37           for(int i=a.length/2+1;i>=0;i--){     //葉子結點數為結點總數一半且都在最后(可以從孩子節點下標的算法為父親節點*2看出),因此                     duDui(i);                       // a.length/2+1處開始為非葉子節點     
38 
39           }
40 
41       }
42 
43    //堆排序操作
44 
45        void sort(){
46 
47         creatDui();                                //建最大堆
48 
49         for(int i=size-1;i>=1;i--){               //每次將第一個數與最后一個數交換,然后大小-1,更新已經改變的根節點
50 
51             int t=a[0];
52 
53                 a[0]=a[size-1];
54 
55                 a[size-1]=t;
56 
57                 size--;
58 
59                 dfDui(0);
60 
61          }
62 
63      }

 

 

 

5、快速排序

    快速排序是實際運用中用的最多的算法,雖然它在最壞的情況下會達到n^2,但它的平均性能非常好,期望時間復雜度為nlgn,而且隱含的常數因子非常小,並且是原址排序。

    快速排序原理:從一組數中任意選出一個數,將大於它的數放右邊,小於它的數放左邊,然后再從左邊和右邊的倆組數中分別執行此操作,知道組中元素數為1,此時,數組就是有序的了。

從原理中可以清楚的看出此操作為遞歸操作,其代碼如下:

           

 1   int partsort(int a[],int l,int r){                 //將比a[r]小的元素放左邊,比它大的放右邊,最后把a[r]放中間
 2 
 3                 int i=l;                                        //i為比a[r]大的元素的下標,初始為開始位置l
 4 
 5                 for(int j=l;j<r;j++){                           
 6 
 7                  if(a[j]<=a[r]){                               //如果元素比a[r]小則和大的元素交換位置,目的讓小的放一起,大的放一起
 8 
 9                    int t=a[j];                                 //可以自己手運行幾遍這個循環
10 
11                        a[j]=a[i];
12 
13                        a[i]=t;
14 
15                        i++;
16 
17                   }
18 
19                }
20 
21               int t=a[i];                                   //a[i]為小元素和大元素的交界處,將a[r]與之交換
22 
23                   a[i]=a[r];
24 
25                   a[r]=t;
26 
27               return i;                                    //返回交界處下標,繼續排前邊的和后邊的這倆組
28 
29       }
30 
31            void quicksort(int a[],int l,int r){
32 
33                if(l<r){                                   //遞歸結束條件為組中只剩一個元素
34 
35                int p=partsort(a,l,r);                    //分成倆組返回交界處
36 
37                    quicksort(a,l,p-1);                  //繼續分左邊
38 
39                    quicksort(a,p+1,r);
40 
41                 }
42 
43            }

 

 

 

主函數中調用代碼為,若數組大小為n,則:quicksort(a,0,n-1);

 

 

待續待續哈..........


免責聲明!

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



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