之前我們跟隨筆者重溫了資料結構中的查詢演算法和部分排序演算法,現在我們繼續跟隨筆者繼續學習一些基本的排序演算法。
選擇排序
使用條件:可對比大小的集合。
演算法思想:每一趟從待排序的資料元素中選出最小或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的資料元素排完。
舉例編程:int b[10]={77,1,65,13,81,93,10,5,23,17}
- //簡單選擇排序
- void SimpleSelect(int b[10])
- {
- int temp;
- int i;
- for(i=0;i<9;i++)
- {
- for(int j=i+1;j<9;j++)
- {
- if(b[i]>b[j])
- {
- temp=b[i];
- b[i]=b[j];
- b[j]=temp;
- }
- }
- }
- cout<<"the sort is:";
- for(int i=0;i<10;i++)
- {
- cout<<b[i]<<" ";
- }
- cout<<endl;
- }
效能分析:時間複雜度為On^2)
堆排序
使用條件:可對比大小的集合。
演算法思想:其實堆排序是簡單選擇排序的一種進化,它最主要是減少比較的次數。什麼是堆?若將序列對應看成一個完全二叉樹,完全二叉樹中所有非終端節點的值均不大於或者不小於)其左右孩子節點的值,可以稱作為堆。由堆的性質可以知道堆頂是一個最大關鍵字(或者最小關鍵字)。在輸出堆頂後,使剩下的元素又建成一個堆,然後在輸出對頂。如此反覆執行,便能得到一個有序序列,這個過程成便是堆排序。
堆排序主要分為兩個步驟:
舉例編程:int b[10]={77,1,65,13,81,93,10,5,23,17}
- //堆排序
- void HeapSort(int b[10])
- {
- void HeapAdjuest(int b[10],int min,int max);
- void Sawp(int *a,int *b);
- int i;
- //因為是完成二叉樹,所以從最後一個非葉子節點開始堆轉換
- for(i=9/2;i>=0;i--)
- {
- HeapAdjuest(b,i,9);
- }
- //拿出堆頂資料在從新堆排序
- for(i=9;i>0;i--)
- {
- Sawp(&b[i],&b[0]);
- HeapAdjuest(b,0,i-1);
- }
- }
- //堆調整(大頂堆)
- //min 資料需要調整在數組中的開始位置
- //max 資料需要調整在資料中的結束位置
- void HeapAdjuest(int b[10],int min,int max)
- {
- if(max<=min)return ;
- int temp;
- temp=b[min];
- int j;
- //延它的孩子節點迴圈
- for(j=2*min;j<=max;j*=2)
- {
- //選擇它的大孩子
- if(j<max&&b[j]<b[j+1])
- {
- j++;
- }
- //堆頂小於它的孩子不做處理
- if(temp>b[j])
- {
- break;
- }
- //將大的數替換成小的數
- b[min]=b[j];
- min=j;
- }
- b[min]=temp;
- }
- //交換函數
- void Sawp(int *a,int *b)
- {
- int temp;
- temp=*a;
- *a=*b;
- *b=temp;
- }
效能分析:時間複雜度時間複雜度O(nlogn)
歸併演算法又稱2路歸併演算法
使用條件:可對比大小的集合。
演算法思想:假設初始序列含有n個記錄,則可看成n個有序的子序列,每個子序列長度為1,然後兩兩歸併,得到[n/2]個長度為2或者為1這裡長度為1可能這裡序列長度是奇數,那麼最後一個序列就落單了,所以長度為1);在兩兩歸併,如此重複,直至得到一個長度為n的有序序列為止。
舉例編程:int b[10]={77,1,65,13,81,93,10,5,23,17}
- //歸併排序
- void MergeSort(int b[10],int d[10],int min,int max)
- {
- //用與存放中間分地區得到的序列
- int c[10];
- void Merge(int c[10],int d[10],int min,int mid,int max);
- if(min==max)d[min]=b[min];
- else
- {
- //平分成兩個地區
- int mid=(min+max)/2;
- //將這個地區進行歸併排序
- MergeSort(b,c,min,mid);
- //將這個地區進行歸併排序
- MergeSort(b,c,mid+1,max);
- //兩個地區歸併
- Merge(c,d,min,mid,max);
- }
- }
- //將有序序列d[min-mid]與d[mid+1-max]歸併成有序序列c[min-max]
- void Merge(int c[10],int d[10],int min,int mid,int max)
- {
- int i,j,k;
- for(i=j=min,k=mid+1;j<=mid&&k<=max;i++)
- {
- if(c[j]>c[k])
- {
- d[i]=c[k];
- k++;
- }
- else
- {
- d[i]=c[j];
- j++;
- }
- }
- if(j<=mid)
- {
- for(;j<=mid;j++,i++)
- {
- d[i]=c[j];
- }
- }
- if(k<=max)
- {
- for(;k<=max;k++,i++)
- {
- d[i]=c[k];
- }
- }
- }
效能分析:時間複雜度O(nlogn)
總結
因為不同的排序方法適應不同的應用換進和要求,選擇合適的排序方法考慮以下因素:
那麼這麼多排序演算法,到底什麼時候用什麼樣的演算法呢?
如果n比較小例如n<=50),可採用直接插入排序或者簡單選擇排序。
如果序列初始狀態基本有序,則可選用直接插入排序,冒泡排序。
如果n比價大,則可採用時間複雜度為O(nlogn)的演算法:快速排序,堆排序,歸併排序。