數據結構中的排序算法。
排序算法的相關知識:
(1)排序的概念:所謂排序就是要整理文件中的記錄,使之按關鍵字遞增(或遞減)次序排列起來。
(2)穩定的排序方法:在待排序的文件中,若存在多個關鍵字相同的記錄,經過排序后這些具有相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的。相反,如果發生改變,這種排序方法不穩定。
(3)排序算法的分類(分為5類):插入排序、選擇排序、交換排序、歸並排序和分配排序。
(4)排序算法兩個基本操作:<1>比較關鍵字的大小。
<2>改變指向記錄的指針或移動記錄本身。
具體的排序方法:
插入排序
<1>插入排序(Insertion Sort)的思想:每次將一個待排序的記錄按其關鍵字大小插入到前面已經排好序的子記錄中的適當位置,直到全部記錄插入完成為止。
<2>常用的插入排序方法有直接插入排序和希爾排序。
(1)直接插入排序
<1>算法思路:把一個記錄集(如一個數組)分成兩部分,前半部分是有序區,后半部分是無序區;有序區一開始有一個元素r[0],無序區一開始是從r[1]到之后的所有元素;然后每次從無序區按順序取一個元素r[i],拿到有序區中由后往前進行比較,每次比較時,有序區中比r[i]大的元素就往后移動一位,直到找到小於r[i]的元素,這時r[i]插到小元素的后面,則完成一趟直接插入排序。如此反復,從無序區不斷取元素插入到有序區,直到無序區為空,則插入算法結束。
<2>算法演示:
//直接插入排序:#include<iostream> using namespace std;
void InsertSort(int r[],int n);
int main() { int r[]={24,1,56,2,14,58,15,89}; InsertSort(r,8); for(int i=0;i<8;i++) { cout<<r[i]<<'\t'; } cout<<endl; return 0; }
void InsertSort(int r[],int n) { for(int i=1;i<n;i++) { for(int j=i-1,s=r[i];s<r[j] && j>=0;j--) { r[j+1]=r[j]; } r[j+1]=s; } }
(2)折半插入排序
<1>算法思路:我們看到在直接插入排序算法中,需要在有序區查找比r[i]的小的元素,然后插入到這個元素后面,但這里要注意這個元素是從無序區算第一個比r[i]小的元素。折半插入排序就是在有序區折半查找這個元素。
<2>算法演示:
//折半插入排序#include<iostream> using namespace std;
void BinInsertSort(int r[],int n);
int main() { int r[]={53,34,76,23,55,28,63,88,34,66}; BinInsertSort(r,10); for(int i=0;i<10;i++) { cout<<r[i]<<"\t"; } cout<<endl; return 0; }
void BinInsertSort(int r[],int n) { for(int i=1;i<n;i++) { int s=r[i]; int low=0; int high=i-1; while(low <= high) { int mid=(low+high)/2; if(s < r[mid]) { high=mid-1; } else { low=mid+1; } } for(int j=i-1;j>=high+1;j--) { r[j+1]=r[j]; } r[high+1]=s; //r[high+1]是要找的元素 } }
(3)希爾排序(Shell Sort)
<1>算法思路:把整個記錄近一個步長step(一般取記錄長度的1/2),分成step個組,再分別對每個級進行直接插入排序;然后再把整個記錄近一個新的步長(一般取step/2)分成step/2個組,再分別對每個組進行直接插入排序;如此不斷的縮小步長,反復分組排序,直到步長等於1為此(步長為1則不可能再分組,1是元素之間距離的最小值)。可以看出,希爾排序實質是一種分組插入方法。
<2>算法演示:
//希爾排序:#include<iostream> using namespace std;
void ShellSort(int r[],int n); int main() { int r[]={24,1,56,2,14,58,15,89}; ShellSort(r,8); for(int i=0;i<8;i++) { cout<<r[i]<<'\t'; } cout<<endl; return 0; }
void ShellSort(int r[],int n) { int step=n/2; while(step >= 1) { for(int i=step;i<n;i+=step) { for(int j=i-step,s=r[i];s<r[j] && j>=0;j-=step) { r[j+step]=r[j]; } r[j+step]=s; } step/=2; } }
選擇排序
<1>選擇排序的思想:每一趟從待排序的記錄中選出關鍵字最小的記錄,順序放在已經排好的記錄最后,直到全部記錄排序完畢。
<2>常用的選擇排序方法有直接選擇排序和堆排序。
(1)直接選擇排序
<1>算法思路:把待排序的n個元素看成一個有序區和一個無序區,開始的時候有序區為空,無序區包含了全部n個元素。排序的時候,每次從無序區中選擇比較出其中最小一個元素放到有序區中。如此反復操作,無序區中每小一個元素,有序區中就多一個元素,直到無序區的所有元素都轉到有序區中。
<2>算法演示:
//簡單選擇排序:#include<iostream> using namespace std;
void SelectSort(int r[],int n);
int main() { int r[]={53,34,76,23,55,28,63,88,34,66}; SelectSort(r,10); for(int i=0;i<10;i++) { cout<<r[i]<<"\t"; } cout<<endl; return 0; }
void SelectSort(int r[],int n) { for(int i=0;i<n-1;i++) { int small_loc=i; for(int j=i+1;j<n;j++) { if(r[small_loc] > r[j]) { small_loc=j; } } if(small_loc != i) { int temp=r[i]; r[i]=r[small_loc]; r[small_loc]=temp; } } }
(2)堆排序
<1>算法思路:大根堆二叉樹中的非終端結點的元素值均大於它的左右孩子的值,因此知道堆的最大值是它的根結點。當根結點移出,則重新調整堆后,堆的次大值稱為根結點,依次操作,可以得到堆的從大到小的有序序列。這個算法過程就是堆排序。
堆排序有一個建堆、篩選堆、調整堆的過程。
<2>算法演示:
//堆排序:#include<iostream> using namespace std;
void HeapAdjust(int r[],int i,int j); void HeapSort(int r[],int n);
int main() { int r[]={53,34,76,23,55,28,63,88,34,66}; HeapSort(r,10); for(int i=0;i<10;i++) { cout<<r[i]<<"\t"; } cout<<endl; return 0; }
void HeapAdjust(int r[],int i,int j) //調整堆{ int child=2*i; int temp=r[i]; //temp臨時存放根結點 while(child <= j) //沿大兒子向下調整 { if(child<j && r[child+1]>r[child]) child++; if(temp >= r[child]) break; r[child/2]=r[child]; child=2*child; } r[child/2]=temp; }
void HeapSort(int r[],int n) //建堆{ for(int i=(n-1)/2;i>=0;--i) { HeapAdjust(r,i,n-1); //初始建堆 } for(i=n-1;i>0;--i) { //將當前堆頂元素與當前堆尾元素互換 int temp=r[0]; r[0]=r[i]; r[i]=temp; HeapAdjust(r,0,i-1); //將剩下的元素重新調整成堆 } }
交換排序
<1>交換排序的思想:兩兩比較待排序記錄的關鍵字,發現兩個記錄的次序相反時即進行交換,直到沒有反序的記錄為止。
<2>常用的交換排序方法有冒泡排序和快速排序。
(1)冒泡排序
<1>算法思路:通過相鄰元素的值的大小比較,並交換值較大的(較小的)元素,使得元素從一端移到到另一端,就像水底冒出的氣泡一樣。
<2>算法演示:
//起泡法排序:#include<iostream> using namespace std; #define N 5 //N為數的總個數
void BubbleSort(int r[],int n);
int main() { int i; int a[N]; cout<<"請輸入"<<N<<"個數字:"; for(i=0;i<N;i++) { cin>>a[i]; } BubbleSort(a,N); for(i=0;i<N;i++) { cout<<a[i]<<"\t"; } cout<<endl; return 0; }
void BubbleSort(int r[],int n) { for(int i=0;i<n-1;i++) //進行n-1次循環,實現n-1趟比較 { for(int j=0;j<n-1-i;j++) //在每一趟中進行n-1-i次比較 { if(r[j]>r[j+1]) { int temp=r[j]; r[j]=r[j+1]; r[j+1]=temp; } } } }
(2)快速排序
<1>算法思路:通過一趟排序將准備排序的元素集合分成兩個部分,其中一部分的元素的值都小於另一部分,然后對這兩部分的元素集合內部再分別重復進行上面的排序過程,直到所有的元素都排列有序。
<2>算法演示:
//快速排序:#include<iostream> using namespace std;
int Partition(int r[],int low,int high); void QuickSort(int r[],int low,int high);
int main() { int r[]={53,34,76,23,55,28,63,88,34,66}; QuickSort(r,0,10-1); for(int i=0;i<10;i++) { cout<<r[i]<<"\t"; } cout<<endl; return 0; }
int Partition(int r[],int low,int high) { int pivotkey=r[low]; int i=low; int j=high; while(i<j) { while(i<j && r[j]>pivotkey) j--; if(i<j){r[i]=r[j];i++;} while(i<j && r[i]<pivotkey) i++; if(i<j){r[j]=r[i];j--;} } r[j]=pivotkey; return j; }
void QuickSort(int r[],int low,int high) { if(low<high) { int pivot=Partition(r,low,high); QuickSort(r,low,pivot-1); QuickSort(r,pivot+1,high); } }
歸並排序
<1>歸並排序的思想:假設數組r有n個元素,那么可以看成數組r是由n個有序的子序列組成,每個子序列的長度為1,然后再兩合並,得到了一個長度是2(或1)的有序子序列,再兩兩合並,如此重復,直到得到一個長度為n的有序數據序列為止,這種排序方法稱為二路歸並排序。
<2>常用的交換排序方法有二路歸並排序和三路歸並排序。
(1)二路歸並排序
<1>算法思路:如上。
<2>算法演示:
//二路歸並排序#include <iostream> using namespace std; int *a=new int[20]; int n=0;
//歸並排序,排序結果放到了b[]中void Merge(int a[],int b[],int left ,int mid,int right)//此處的right指向數組中左后一個元素的位置{ int i=left; int j=mid+1; int k=left; while(i<=mid && j<=right) { if(a[i]<=a[j])b[k++]=a[i++]; else b[k++]=a[j++]; } while(i<=mid) b[k++]=a[i++]; while(j<=right) b[k++]=a[j++]; }
//從b[]中又搬到了a[]中void Copy(int a[],int b[],int left,int right)//right同上{ for(int i=left;i<=right;i++) a[i]=b[i]; }
//划分並排序void MergeSort(int a[],int left,int right)//同上{ int *b = new int[right-left+1]; if(left<right) { //將當前傳經來的數組划分成更小的大小幾乎相同的數組 int i=(left+right)/2; MergeSort(a,left,i); MergeSort(a,i+1,right); //將小數組合成大數組,同時排序,結果放到b[]中 Merge(a,b,left,i,right); //從b[]中挪到a[]中 Copy(a,b,left,right); } }
void Input() { cout<<"Please Input array's size:"; cin>>n; cout<<"Array's elemants:"<<endl; for(int i=0;i<n;i++) cin>>a[i]; //調用算法 MergeSort(a,0,n-1); }
void Output() { for(int i=0;i<n;i++) cout<<a[i]<<" "; cout<<endl; }
int main() { Input(); Output(); return 0; }