全部排序算法總結:冒泡排序,快速排序,插入排序,歸併排序,堆排序,shell排序,選擇排序html
算法排序的穩定性是指:關鍵碼相同的記錄排序先後相對位置不發生改變。舉個例子:ios
待排序數組:int a[] ={1, 2, 2, 3, 4, 5, 6};
算法
在快速排序的隨機選擇比較子(即pivot)階段:shell
若選擇a[2](即數組中的第二個2)爲比較子,,而把大於等於比較子的數均放置在大數數組中,則a[1](即數組中的第一個2)會到pivot的右邊, 那麼數組中的兩個2非原序(這就是「不穩定」)。windows
若選擇a[1]爲比較子,而把小於等於比較子的數均放置在小數數組中,則數組中的兩個2順序也非原序。數組
1. 冒泡排序函數
很簡單的排序,外層循環是n-1趟,內層循環是n-1次兩兩比較。主要思路:從底部往上冒泡,經過無序區中相鄰記錄關鍵字間的比較和位置的交換,使關鍵字最小的記錄如氣泡通常逐漸往上「漂浮」直至「水面」。測試
就是在每一趟內層循環完畢以後,最小的那個值會像氣泡同樣上浮到第一個位置(從小到大排序),這樣循環執行n-1趟,每一趟都是從最後一個值開始進行兩兩比較,把每趟中的最小的值往上浮。(注意內層循環的終止條件是j>i,由於i以前是已經放置好的有序的最小值)ui
代碼:spa
static void bubblesort(int* A,int n){ if(A==NULL) return; for (int i=0;i<n-1;++i) { for (int j=n-1;j>i;--j) { if(A[j]<A[j-1]){int temp=A[j];A[j]=A[j-1];A[j-1]=temp;} } cout<<"第"<<i+1<<"趟冒泡排序:"<<endl; for (int i=0;i<n;++i) { cout<<A[i]<<" "; } cout<<endl; } }
2. 歸併排序(穩定,效率高,採用遞歸)
思路:基本思路就是將數組分紅二組A,B,若是這二組組內的數據都是有序的,那麼就能夠很方便的將這二組數據進行排序。問題是如何讓這二組組內數據有序呢?
能夠將A,B組各自再分紅二組。依次類推,當分出來的小組只有一個數據時,能夠認爲這個小組組內已經達到了有序,而後再合併相鄰的二個小組就能夠了。這樣經過先遞歸的分解數列,再合併數列就完成了歸併排序。具體過程參考下圖:
代碼(先用遞歸分解數組,而後用mergearray合併):
//將有二個有序數列a[first...mid]和a[mid...last]合併。 void mergearray(int a[], int first, int mid, int last, int temp[]) { int i = first, j = mid + 1;//i爲分出的第一個數組的第一個位置,j爲分出來的第二個數組的第一個位置 int m = mid,n = last;//m爲分出的第一個數組的末尾,n爲分出來的第二個數組的末尾 int k = 0; while (i <= m && j <= n) { if (a[i] <= a[j]) temp[k++] = a[i++]; else temp[k++] = a[j++]; } while (i <= m) temp[k++] = a[i++]; while (j <= n) temp[k++] = a[j++]; for (i = 0; i < k; i++) a[first + i] = temp[i]; } void mergesort(int a[], int first, int last, int temp[]) { if (first < last) { int mid = (first + last) / 2; mergesort(a, first, mid, temp); //左邊有序 mergesort(a, mid + 1, last, temp); //右邊有序 mergearray(a, first, mid, last, temp); //再將二個有序數列合併 } } bool MergeSort(int a[], int n) { int *p = new int[n]; if (p == NULL) return false; mergesort(a, 0, n - 1, p); delete[] p; return true; }
1.堆
堆其實是一棵徹底二叉樹,其任何一非葉節點知足性質:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]
即任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。
堆分爲大頂堆和小頂堆,知足Key[i]>=Key[2i+1]&&key>=key[2i+2]稱爲大頂堆,知足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱爲小頂堆。由上述性質可知大頂堆的堆頂的關鍵 字確定是全部關鍵字中最大的,小頂堆的堆頂的關鍵字是全部關鍵字中最小的。
2.堆排序的思想
利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。
其基本思想爲(大頂堆):
1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無序區;
2)將堆頂元素R[1]與最後一個元素R[n]交換,此時獲得新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且知足R[1,2...n-1]<=R[n];
3)因爲交換後新的堆頂R[1]可能違反堆的性質,所以須要對當前無序區(R1,R2,......Rn-1)調整爲新堆,而後再次將R[1]與無序區最 後一個元素交換,獲得新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排 序過程完成。
操做過程以下:
1)初始化堆:將R[1..n]構造爲堆;
2)將當前無序區的堆頂元素R[1]同該區間的最後一個記錄交換,而後將新的無序區調整爲新的堆。
所以對於堆排序,最重要的兩個操做就是構造初始堆和調整堆,其實構造初始堆事實上也是調整堆的過程,只不過構造初始堆是對全部的非葉節點都進行調整。
下面舉例說明:
給定一個整形數組a[]={16,7,3,20,17,8},對其進行堆排序。
首先根據該數組元素構建一個徹底二叉樹,獲得
20和16交換後致使16不知足堆的性質,所以需從新調整
這樣就獲得了初始堆。
此時3位於堆頂不滿堆的性質,則需調整繼續調整
/*堆排序(大頂堆) 2011.9.14*/
#include <iostream>
#include<algorithm>
using namespace std;
void HeapAdjust(int *a,int i,int size) //調整堆
{
int lchild=2*i; //i的左孩子節點序號
int rchild=2*i+1; //i的右孩子節點序號
int max=i; //臨時變量
if(i<=size/2) //若是i是葉節點就不用進行調整
{
if(lchild<=size&&a[lchild]>a[max])
{
max=lchild;
}
if(rchild<=size&&a[rchild]>a[max])
{
max=rchild;
}
if(max!=i)
{
swap(a[i],a[max]);
HeapAdjust(a,max,size); //避免調整以後以max爲父節點的子樹不是堆
}
}
}
void BuildHeap(int *a,int size) //創建堆
{
int i;
for(i=size/2;i>=1;i--) //非葉節點最大序號值爲size/2
{
HeapAdjust(a,i,size);
}
}
void HeapSort(int *a,int size) //堆排序
{
int i;
BuildHeap(a,size);
for(i=size;i>=1;i--)
{
//cout<<a[1]<<" ";
swap(a[1],a[i]); //交換堆頂和最後一個元素,即每次將剩餘元素中的最大者放到最後面
//BuildHeap(a,i-1); //將餘下元素從新創建爲大頂堆
HeapAdjust(a,1,i-1); //從新調整堆頂節點成爲大頂堆
}
}
int main(int argc, char *argv[])
{
//int a[]={0,16,20,3,11,17,8};
int a[100];
int size;
while(scanf("%d",&size)==1&&size>0)
{
int i;
for(i=1;i<=size;i++)
cin>>a[i];
HeapSort(a,size);
for(i=1;i<=size;i++)
cout<<a[i]<<"";
cout<<endl;
}
return 0;
}
設數組爲a[0…n-1]。
1. 初始時,a[0]自成1個有序區,無序區爲a[1..n-1]。令i=1
2. 將a[i]併入當前的有序區a[0…i-1]中造成a[0…i]的有序區間。
3. i++並重復第二步直到i==n-1。排序完成。
下面給出嚴格按照定義書寫的代碼(由小到大排序):
void Insertsort1(int a[], int n) { int i, j, k; for (i = 1; i < n; i++) { //爲a[i]在前面的a[0...i-1]有序區間中找一個合適的位置 for (j = i - 1; j >= 0; j--) if (a[j] < a[i]) break; //如找到了一個合適的位置 if (j != i - 1) { //將比a[i]大的數據向後移 int temp = a[i]; for (k = i - 1; k > j; k--) a[k + 1] = a[k]; //將a[i]放到正確位置上 a[j+1] = temp; } } }
這樣的代碼太長了,不夠清晰。如今進行一下改寫,將搜索和數據後移這二個步驟合併。即每次a[i]先和前面一個數據a[i-1]比較,若是a[i] > a[i-1]說明a[0…i]也是有序的,無須調整。不然就令j=i-1,temp=a[i]。而後一邊將數據a[j]向後移動一邊向前搜索,當有數據a[j]<a[i]時中止並將temp放到a[j + 1]處。