iOS 排序算法總結、二分法查找


本文均是自己終結,查考網址:http://student.zjzk.cn/course_ware/data_structure/web/paixu/paixu8.5.1.1.htm

還有一個:二分插入排序  平均時間O(n2)   穩定

 1、插入排序

在要排序的一組數中,假設前面(n-1) [n>=2] 個數已經是排好順序的,現在要把第n個數插到前面的有序數中,使得這n個數也是排好順序的。如此反復循環,直到全部排好順序。

直接插入排序是穩定的。算法時間復雜度O(n2)--[n的平方]

 

main()

{

int  a[10],j,i,m;

for(j=1;j<10;j++)

   {

m=a[j];

      for(i=j-1;i>=0;i--)

{

if(a[i]<m)

break;

         else

a[i+1]=a[i];

}

a[i+1]=m;

}

}

 

加注釋的版本:

void lnsertSort(SeqList R)
   { //對順序表R中的記錄R[1..n]按遞增序進行插入排序
    int i,j;
    for(i=2;i<=n;i++) //依次插入R[2],…,R[n]
      if(R[i].key<R[i-1].key){//若R[i].key大於等於有序區中所有的keys,則R[i]
                              //應在原有位置上
        R[0]=R[i];j=i-1; //R[0]是哨兵,且是R[i]的副本
        do{ //從右向左在有序區R[1..i-1]中查找R[i]的插入位置
         R[j+1]=R[j]; //將關鍵字大於R[i].key的記錄后移
         j-- ;
         }while(R[0].key<R[j].key); //當R[i].key≥R[j].key時終止
        R[j+1]=R[0]; //R[i]插入到正確的位置上
       }//endif
   }//InsertSort

 

 

2、希爾排序

D.L.shell於1959年在以他名字命名的排序算法中實現了這一思想。算法先將要排序的一組數按某個增量d分成若干組,每組中記錄的下標相差d.對每組中全部元素進行排序,然后再用一個較小的增量對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成一組,排序完成。

下面的函數是一個希爾排序算法的一個實現,初次取序列的一半為增量,

以后每次減半,直到增量為1。

希爾排序是不穩定的。

 

void shell_sort(int *x, int n)

{

int h, j, k, t;

for (h=n/2; h>0; h=h/2) /*控制增量*/

{

       for (j=h; j<n; j++) /*這個實際上就是上面的直接插入排序*/

       {

          t = *(x+j);

          for (k=j-h; (k>=0 && t<*(x+k)); k-=h)

          {

   if(*(x+k)<t)

  break;

            else

             *(x+k+h) = *(x+k);

          }

          *(x+k+h) = t;

       }

}

}

 

 

3、冒泡排序

在要排序的一組數中,對當前還未排好序的范圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較后發現它們的排序與排序要求相反時,就將它們互換。

冒泡排序是穩定的。算法時間復雜度O(n2)--[n的平方]

 

main()

{

int a[10],i,j,k;

for(i=0;i<9;i++)

for(j=0;j<10-i;j++)

 if(a[j]>a[j+1])

 {

k=a[j];

a[j]=a[j+1];

a[j+1]=k;

 }

}

 

 

4、快速排序

快速排序是對冒泡排序的一種本質改進。它的基本思想是通過一趟掃描后,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次掃描只能確保最大數值的數移到正確位置,而待排序序列的長度可能只減少1。快速排序通過一趟掃描,就能確保以某個數為基准點的左邊各數都比它小,右邊各數都比它大。然后又用同樣的方法處理它左右兩邊的數,直到基准點的左右只有一個元素為止。

顯然快速排序可以用遞歸實現,當然也可以用棧化解遞歸實現。

快速排序是不穩定的。最理想情況算法時間復雜度O(nlog2n),最壞O(n2)

main()

{

int a[10],i;

quick_sort(a,0,9);

}

quick_sort(int L[],int first,int end)

{

int split;

if(end>first)

{

split=quick(first,end,L);//進行一次希爾排序,返回值為本次排序基准值的下標值

       quick_sort(L,first,split-1);//上面的排序完成后再對基准點左右的數組進行同樣的排序操作

       quick_sort(L,split+1,end);

   }

}

quick(int first,int end,int L[])

{

int left=first,right=end;

int key=L[first];

while(left<right)

{

while((left<right)&&(L[right]>=key))

right--;

if(left<right)

L[left++]=L[right];

while((left<right)&&(L[left]<=key))

left++;

if(left<right)

L[right--]=L[left];

}

L[left]=key;

return left;

}

 

 

5、選擇排序

在要排序的一組數中,選出最小的一個數與第一個位置的數交換;然后在剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最后一個數比較為止。

選擇排序是不穩定的。算法復雜度O(n2)--[n的平方]

 

main()

{

int t,k,i,j,a[10];

for(i=0;i<9;i++)

   {

k=i;

      for(j=i+1;j<10;j++)

if(a[k]>a[j])

k=j;

      t=a[i];

a[i]=a[k];

a[k]=t;

   }

}

 

 

6、堆排序

堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。堆的定義如下:具有n個元素的序列(h1,h2,...,hn),當且僅當滿足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2)

時稱之為堆。在這里只討論滿足前者條件的堆。

由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項。完全二叉樹可以

很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲順序,使之成為一個堆,這時堆的根節點的數最大。然后將根節點與堆的最后一個節點交換。然后對前面(n-1)個數重新調整使之成為堆。依此類推,直到只有兩個節點的堆,並對它們作交換,最后得到有n個節點的有序序列。

從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素

交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反復調用滲透函數

實現排序的函數。有最大堆和最少堆之分。

堆排序是不穩定的。算法時間復雜度O(nlog2n)。

 

功能:滲透建堆

void sift(int *x, int n, int s)

{

int t, k, j;

t = *(x+s); /*暫存開始元素*/

k = s;   /*開始元素下標*/

j = 2*k + 1; /*右子樹元素下標*/

while (j<n)

{

/*判斷是否滿足堆的條件:滿足就繼續下一輪比較,否則調整。*/

        if (j<n-1 && *(x+j) < *(x+j+1))

   {

          j++;

        }

        if (t<*(x+j)) /*調整*/

        {

           *(x+k) = *(x+j);

           k = j; /*調整后,開始元素也隨之調整*/

           j = 2*k + 1;

        }

        else /*沒有需要調整了,已經是個堆了,退出循環。*/

        {

            break;

        }

}

*(x+k) = t; /*開始元素放到它正確位置*/

}

 

功能:堆排序

void heap_sort(int *x, int n)

{

int i, k, t;

int *p;

for (i=n/2-1; i>=0; i--)

{

      sift(x,n,i); /*初始建堆*/

}

for (k=n-1; k>=1; k--)

{

      t = *(x+0); /*堆頂放到最后*/

      *(x+0) = *(x+k);

      *(x+k) = t;

      sift(x,k,0); /*剩下的數再建堆*/

}

}

 

7. 歸並排序(Merge Sort)

利用"歸並"技術來進行排序。歸並是指將若干個已排序的子文件合並成一個有序的文件。
   1、算法基本思路
     設兩個有序的子文件(相當於輸入堆)放在同一向量中相鄰的位置上:R[low..m],R[m+1..high],先將它們合並到一個局部的暫存向量R1(相當於輸出堆)中,待合並完成后將R1復制回R[low..high]中。

(1)合並過程
     合並過程中,設置i,j和p三個指針,其初值分別指向這三個記錄區的起始位置。合並時依次比較R[i]和R[j]的關鍵字,取關鍵字較小的記錄復制到R1[p]中,然后將被復制記錄的指針i或j加1,以及指向復制位置的指針p加1。
     重復這一過程直至兩個輸入的子文件有一個已全部復制完畢(不妨稱其為空),此時將另一非空的子文件中剩余記錄依次復制到R1中即可。

(2)動態申請R1
     實現時,R1是動態申請的,因為申請的空間可能很大,故須加入申請空間是否成功的處理。

   2、歸並算法
  void Merge(SeqList R,int low,int m,int high)
    {//將兩個有序的子文件R[low..m]和R[m+1..high]歸並成一個有序的
     //子文件R[low..high]
     int i=low,j=m+1,p=0; //置初始值
     RecType *R1; //R1是局部向量,若p定義為此類型指針速度更快
     R1=(ReeType *)malloc((high-low+1)*sizeof(RecType));
     if(! R1) //申請空間失敗
       Error("Insufficient memory available!");
     while(i<=m&&j<=high) //兩子文件非空時取其小者輸出到R1[p]上
       R1[p++]=(R[i].key<=R[j].key)?R[i++]:R[j++];
     while(i<=m) //若第1個子文件非空,則復制剩余記錄到R1中
       R1[p++]=R[i++];
     while(j<=high) //若第2個子文件非空,則復制剩余記錄到R1中
       R1[p++]=R[j++];
     for(p=0,i=low;i<=high;p++,i++)
       R[i]=R1[p];//歸並完成后將結果復制回R[low..high]
    } //Merge

 

8.二分法查找和二分法插入

首先申明,二分法查找只適用與已排序的數列,如果是混亂數列。。我也無能為力~

有一個數組 v 已經按升序排列了,數組 v 有 n=20 個元素。數組中有個元素 x,如何知道 x 位於該數組的第幾位呢?

解決這個問題的一個普遍方法是二分法查找。下面是程序:

int binsearch(int x, int v[], int n) {    
   int low, high, mid;       
   low = 0;          
   high = n - 1;  
 while (low <= high) {                     
   mid = (low + high) / 2;                              
   if(x < v[mid])                                 
     high = mid - 1;                        
   else if (x > v[mid])                                      
     low = mid + 1;                         
   else                                              
     return mid;                              // 看看循環執行了多少次                               
 printf("mid = %d, low = %d, high = %d \n", mid, low, high); 
 }           
 return -1; //沒有查找出來返回-1
}

思路很簡單:首先將輸入值 x 與數組 v 的中間元素比較,如果 x 小於中間的元素,則將 high 值設為 中間元素-1,同理,若 x 大於中間元素,則將中間元素 + 1作為 low,再在low 與 high之間進行查找

 

二分法插入排序 
算法思想簡單描述:
在插入第i個元素時,對前面的0~i-1元素進行折半,先跟他們
中間的那個元素比,如果小,則對前半再進行折半,否則對后半
進行折半,直到left>right,然后再把第i個元素前1位與目標位置之間
的所有元素后移,再把第i個元素放在目標位置上。

二分法沒有排序,只有查找。所以當找到要插入的位置時。移動必須從最后一個記錄開始,向后移動一位,再移動倒數第2位,直到要插入的位置的記錄移后一位。

二分插入排序是穩定的,平均時間O(n2)

     void binsort(ref int[] data1)

1、二分法查找插入位置
  如果R[i]<R[m]成立,那右指針就要向左移動中間指針一位,否則,左指針要向左移動中間指針一位。反復查找,直到左指針大於右指針時停止。
2、后移,有點迷惑,什么時候需要后移呢?有哪些記錄需要移動呢?
  雖然我們很清楚的知道,我們需要后移那些排序碼大於R[i]的記錄,但難免會問自己這樣幾個問題。其實它相當於需要移動從i-1到左指針的記錄。
3、插入
  由1中得到的左指針其實就是元素要插入的位置。

4、算法

        {

           int left,right,num;

            int middle,j;

            for( int i = 1;i < data1.Length;i++)

            {

                // 准備

                left = 0;

                right = i-1;

                num = data1[i];

                

                // 二分法查找插入位置

                while( right >= left)

                {

                    // 指向已排序好的中間位置

                    middle = ( left + right ) / 2;

                    if( num < data1[middle] )

                    // 插入的元素在右區間

                        right = middle-1; 

                    else

                    // 插入的元素在左區間

                        left = middle+1;    

                }

                // 后移排序碼大於R[i]的記錄

                for( j = i-1;j >= left;j-- )

                {

                    data1[j+1] = data1[j];

                }

                // 插入

                data1[left] = num;

            }

 

                    // 插入的元素在左區間

                        left = middle+1;    

                }

 

                // 后移排序碼大於R[i]的記錄

                for( j = i-1;j >= left;j-- )

                {

                    data1[j+1] = data1[j];

                }

                // 插入

                data1[left] = num;

            }

 


免責聲明!

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



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