內部排序:在整個排序過程當中不需不訪問外存便能完成,稱這樣的排序問題爲內部排序;html
插入排序: 將無序序列中的一個或幾個記錄「插入」到有序的序列中,從而增長記錄的有序序列的長度。ios
主要思想是將第一個元素看作是有序的,從第二個元素起將待排序的元素插入到有序序列中,使序列逐漸擴大,直到全部的元素都插入到有序序類中。算法
基本思想是將記錄R[i]插入到有序序列R[1..i-1],使記錄的有序序列從R[1..i-1]變爲R[1..i]。sql
直接插入排序算法最好狀況下的時間複雜度爲O(n),最壞狀況的時間複雜度和平均時間複雜度爲O(n^2)。shell
#include "stdafx.h" #include "stdafx.h" #include <iostream> using namespace std; void StrInsSort (int number ,int r[]) { int temp; for(int i=1;i<=number;i++) { temp=r[i];int j=i-1; while(temp<r[j]) { r[j+1]=r[j]; j--; } r[j+1]=temp; } } int _tmain(int argc, int argv[]) { std::cout<<"輸入數組大小"<<endl; std::cin>> argc; std::cout<<"輸入數組內數據"<<endl; for(int j=0;j<argc;j++) { std::cin>>argv[j]; } StrInsSort (argc ,argv); for(int j=0;j<argc;j++) { std::cout << argv[j] << std::endl; } system("pause"); return 0; }
次待排序數組是從0開始的,先假定第0個元素是有序的,從第一個元素起與前邊的有序序列進行比較,T先和下標爲i-1的數組比較,(生成的整個序列是從小到達排列的)若是數組
r[i]>r[i-1],則直接放在r[i]的位置,不然若是T<r[i-1],則將將i--,直到找到一個比T小的,把T放在該數的後邊;數據結構
折半插入與直接插入比較,當第i 個記錄要插入到前i-1個記錄序列時,能夠利用折半查找方式肯定插入位置,以減小比較次數。函數
折半查找明顯減小了關鍵字的「比較」次數,單記錄的移動次數不變,故時間複雜度仍爲O(n^2)。測試
void BinSort (int count, int R[]) { for (int i=1;i<=count;i++) { int left=0; int right=i-1; int temp; while (right>=left) { temp=R[i] ; int mid =(left+right)/2; if (temp>R[mid]) { left=mid+1; } else { right=mid-1; } } for (int j=i-1;j>=right+1;j--) { R[j+1]=R[j]; } R[right+1]=temp; } };
函數寫過程當中,首先肯定有多少個元素要參加排序,因爲次數組是從下表0開始的,因此和直接插入排序同樣,先假設第0個元素是有序的,則從下標爲1的元素開始執行循環,即從1到count個元素都要執行對比循環過程;其循環內部基本思路是,把取到的第i個元素插入到前0到i-1個元素中,由於前i個元素是有序的,因此二分查找是拿第i元素和和前面有序數列中的第mid =(left+right)/2個元素比較,即有序數列中中間的那個元素,由於排序後要生成一個從小到大排序的數列,即升序數列,因此若是temp>R[mid]以下圖所示,ui
0 | 2 | 4 | 6 | 7 | 9 | 11 |
則要是插入 temp後整個數列依然有序,則temp必須在6的後面,因此要最左側的數爲left=mid+1;才能保證在後半部分尋找數據;
而後就要判斷循環何時終止,由於在整個循環過程當中不斷縮小循環的範圍,即left和right的距離愈來愈近,當left==right時,這時mid==left==righ, 這時,
若是
if (temp>R[mid])
{
left=mid+1;
}
如上圖所示,mid在6的位置,此時的temp是>6而<7 的,應該把>=left的或>=mid+1的或>=right+1的元素向後移一位;
而若是
else
{
right=mid-1;
}
如上圖所示,mid應該<6且>4,即放在4和6之間,這樣更應該把>=left的後>=mid的或>=right+1的元素向後移一位;
for (int j=i-1;j>=right+1;j--)
{
R[j+1]=R[j];
} 即對應這段程序;
通過上邊分析,一樣也能夠寫成下面程序,即把>=right+1,改爲>=left;
for (int j=i-1;j>=left;j--)
{
R[j+1]=R[j];
}
R[left]=temp;
基本思想是另設一數組d,將R[1]複製給d[1],並將d[1]看作排好序的「中間」記錄,從第二個起依次將關鍵字小於d[1]的記錄插入到d[1]以前的有序序列中,將關鍵字大於d[1]的記錄插入到d[1]以後的有序序列中。這裏藉助兩個變量first和final來指示排序過程當中有序序列第一個記錄和最後一個記錄在d中的位置。
int BiInsertSort() { //二路插入排序算法 int iRawBuff[6] ={0,9,6,7,3,2}; int final = 0; int first = 0; const int iLenght = 5; int iTempBuff[iLenght] = {0}; iTempBuff[0] = iRawBuff[0]; for (int i = 1; i <= iLenght;i++) { if(iRawBuff[i] > iTempBuff[final]) { //大於當前最大值,後插 final++; iTempBuff[final] = iRawBuff[i]; } if(iRawBuff[i]< iTempBuff[first]) { //小於當前最小值,前插 first = (first-1+iLenght)%iLenght; iTempBuff[first] = iRawBuff[i]; } if(iRawBuff[i] < iTempBuff[final]&&iRawBuff[i] > iTempBuff[first]) { //大於當前最小值,小於當前最大值,中間插 int j = final++; while (iRawBuff[i] < iTempBuff[j]) { iTempBuff[(j+1)%iLenght] = iTempBuff[j]; j = (j-1+iLenght)%iLenght; } iTempBuff[j+1] = iRawBuff[i]; } printf("第%d趟:\n",i-1); for(int k = 0; k < iLenght; k++) { std::cout<<iTempBuff[k]<<"\t"; } std::cout<<std::endl; } //導入輸入到原始數組中 for (int k = 0; k < iLenght; k++) { iRawBuff[k+1] = iTempBuff[(first++)%iLenght]; } return 0; }
運行結果:
因爲first在增長增長的過程當中,沒有最大值的限制,爲了防止生成的數組發生越界,因此對這些數取iLenght的餘數,即用%iLenght。
爲了減小在排序過程當中「移動」記錄的操做,必須改變排序過程當中採用的存儲結構。利用靜態鏈表進行排序,並在排序以後,一次性地調整各個記錄之間的位置,即將每一個記錄都調整到他們應該在的位置上,這樣的排序方法稱爲表插入排序。
基本思想:
使頭結點的next域始終指示最小的那個元素,而後依次向下:每個元素的next域都指示比它稍大的那個元素。最大的元素的next域指示頭結點。
下面分三步給出具體的代碼:
一、用數組初始化表結構。
二、修改next域造成有序的循環鏈表。
三、根據next域信息調整表結構中的數組,是數據從小到大排列。
#include "stdafx.h" #define INT_MAX 100 #define number 100 #include<iostream> #include<time.h> #include<stdlib.h> #include<iomanip> #include<math.h> //排序後按從小到大輸出 using namespace std; typedef struct { int key; int other; //記錄其餘數據域 int next; }STListType; STListType SL[number+1]; void ListInsSort(STListType SL[],int n) { //對記錄序列SL[1..n]進行表插入排序 SL[0].key = 10000; SL[0].next=1; SL[1].next=0; //初始化,假定第一個記錄有序 int k; for (int i=2; i<n ; i++) //查找插入位置 { int j=0; for (k=SL[0].next;SL[k].key<SL[i].key;) //保證[k].key>=[i].key { j=k, k=SL[k].next; //保證是SL[k]裏的老是最大的 } SL[j].next=i; //結點i插入在結點j和結點k之間 SL[j]<=SL[i]<=SL[k] SL[i].next =k; cout<<"ListInsSort-key:\n"; for(int count=0;count<n;count++) cout<<setw(4)<<SL[count].key; cout<<endl; cout<<"ListInsSort-next:\n"; for(int count=0;count<n;count++) cout<<setw(4)<<SL[count].next; cout<<endl; } } void Arrange(STListType SL[],int n) { //根據靜態表SL中各節點的指針值調整記錄位置,使得SL中的記錄關鍵字非遞減有序順序排列 //p指示第i個記錄的當前位置;i指示第i個記錄應在的位置;q指示第i+1個記錄的當前位置; int p=SL[0].next; //p指示第一個記錄的當前位置 int q; for (int i=1;i<n;i++) { //SL[1..i-1]中的記錄關鍵字有序排列,第i個記錄在SL中的當前位置應不小於i //爲了保證<i 的元素都是有序的 while(p<i) //找到第i個記錄,並用p指示其在SL中的當前位置 p=SL[p].next; q=SL[p].next; //q指示還沒有調整的表尾 if (p!=i) { STListType temp; temp = SL[p]; SL[p] = SL[i]; SL[i] = temp; //交換記錄,使第i個記錄到位 SL[i].next=p; //指向被移走的記錄,使得之後能夠由while循環找到 } p=q; //p指示還沒有調整的表尾,爲找第i+1個記錄做準比 cout<<"Arrange-key:\n"; for(int count=0;count<n;count++) cout<<setw(4)<<SL[count].key; cout<<endl; cout<<"Arrange-next:\n"; for(int count=0;count<n;count++) cout<<setw(4)<<SL[count].next; cout<<endl; } } int _tmain() { STListType SL[100]; int n; cout<<"ListInsSort.cpp運行結果:\n"; int b[100],i; srand(time(0)); cout<<"輸入待排序元素個數n:";cin>>n; for(i=1;i<n;i++) { b[i]=rand()%100; SL[i].key=b[i]; } cout<<"排序前數組:\n"; for(i=1;i<n;i++) cout<<setw(4)<<b[i]; cout<<endl; ListInsSort(SL,n); Arrange(SL,n); cout<<"排序後數組:\n"; for(i=1;i<n;i++) cout<<setw(4)<<SL[i].key; cout<<endl; system("pause"); }
希爾排序又稱縮小增量排序,(適用於待排序數列很無序的狀況下);基本思想是將待排序的記錄劃分紅幾組,從而減小參與直接插入排序的數據量,通過幾回分組排序後,記錄的排序已經基本有序,在對全部的記錄實施最後的直接插入排序。
對於希爾排序,具體步驟能夠描述以下:假設待排序的記錄爲n個,先取整數d<n,例如d=n/2,將全部距離爲d的記錄構成一組,從而將整個待排序記錄分割成爲d個子序列(d組),對每一個分組進行直接插入排序,而後再縮小間隔d,例如,取d=d/2,重複上述分組,再對每一個分組分別進行直接插入排序,直到最後取d=1,即將全部的記錄放在一組進行一次直接插入排序,最終將全部記錄從新排列成按關鍵字有序的序列。
Shell提出的選法:d1=n/2 , di+1=di/2
#include<iostream> #include<iomanip> #include<stdlib.h> #include<time.h> using namespace std; #define MAXI 11 typedef int KeyType; typedef int ElemType; struct rec { KeyType key; ElemType data; }; typedef rec sqlist[MAXI]; void shellsort(sqlist b,int n) { int i,j,gap,k; rec x; gap=n/2; while(gap>0) { for(i=gap+1;i<n;i++) { j=i-gap; while(j>0) if(b[j].key>b[j+gap].key) { x=b[j]; b[j]=b[j+gap]; b[j+gap]=x; j=j-gap; } else j=0; for(k=1;k<n;k++) cout<<setw(4)<<b[k].key; cout<<endl; } gap=gap/2; } } void main() {cout<<"運行結果:\n"; sqlist a;int i,n=MAXI; srand(time(0)); for(i=1;i<n;i++) {a[i].key=rand()%80; a[i].data=rand()%100;} cout<<"排序前數組:\n"; for(i=1;i<n;i++) cout<<setw(4)<<a[i].key; cout<<endl; cout<<"數組排序過程演示:\n"; shellsort(a,n); cout<<"排序後數組:\n"; for(i=1;i<n;i++) cout<<setw(4)<<a[i].key; cout<<endl;cin.get();}
交換排序:經過「交換」無序序列中的相鄰記錄從而獲得其中關鍵字最小或最大的記錄,並將他們加入到有序序列中,以增長記錄的有序序列長度。
主要思想是在排序過程當中,經過比較待排序記錄序列中元素的關鍵字,若是發現次序相反,則將存儲位置交換來達到排序目的。
它的基本思想是對全部相鄰記錄的關鍵字值進行比較,若是是逆序(a[j]>a[j+1]),則將其交換,最終達到有序。
#include "stdafx.h" #include "stdafx.h" #include <iostream> using namespace std; int bubbleSort (int number ,int r[]) { int temp; for (int i =0;i<=number-1;i++) { for (int j=0;j<=number-i-1;j++) { if (r[j]>r[j+1]) { temp=r[j]; r[j]=r[j+1]; r[j+1]=temp; } } } return 1; } int _tmain(int argc, int argv[]) { std::cout<<"輸入數組大小"<<endl; std::cin>> argc; std::cout<<"輸入數組內數據"<<endl; for(int j=0;j<argc;j++) { std::cin>>argv[j]; } bubbleSort (argc ,argv); for(int j=0;j<argc;j++) { std::cout << argv[j] << std::endl; } system("pause"); return 0; }
在排序過程當中,比較相鄰的元素,若是第前個比後一個大,就交換他們兩個。對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。針對全部的元素重複以上的步驟,除了最後一個。
for (int i =0;i<=number-1;i++)循環是指(一個元素向上冒泡,直到它找到合適的位置)這個過程的次數,即有多少個元素要進行冒泡操做;冒到上邊的是最大的;
for (int j=0;j<=number-i-1;j++)循環是指進行冒泡操做的每個元素,要通過作少次對比,才能找到合適的位置;
快速排序的基本思想:首先將待排序記錄序列中的全部記錄做爲當前待排序區域,從中任選一個記錄(一般可選取第一個記錄),以它的關鍵字做爲樞紐,凡其關鍵字小於樞紐的記錄均移到該記錄以前,反之,凡關鍵字大於樞紐的記錄均移至該紀錄以後,這樣一趟排序以後,記錄的無序序列R[s..t]將分割成兩部分:R[s..i-1]和R[i+1..t],且R[j].key<=R[i].key<=R[k].key
#include "stdafx.h" #include "stdafx.h" #define number 100 #include<iostream> #include<time.h> #include<stdlib.h> #include<iomanip> #include<math.h> //排序後按從小到大輸出 using namespace std; typedef struct { int key; int other; //記錄其餘數據域 int next; }RecType; RecType S[number+1]; int Partirion(RecType R[],int l,int h) { //交換記錄子序列R[1..h]中的記錄,使樞紐記錄交換到正確的位置,並返回其所在的位置 int i=l; int j=h; //用變量i,j記錄待排序記錄的首尾位置 R[0]=R[i]; //以子表的第一個記錄做爲樞軸,將其暫存到記錄R[0]中 int x=R[i].key; //用變量x存放樞紐記錄的關鍵字 while (i<j) { //從表的兩端交替地向中間掃描 while (i<j&&R[j].key>=x) { j--; } R[i]=R[j]; //將比樞紐小的記錄移到低端 while (i<j&&R[i].key<=x) { i++; } R[j]=R[i]; //將比樞紐大的記錄移到高端 } R[i]=R[0]; //樞紐記錄到位 return i; //返回樞紐位置 } void QuickSort(RecType R[],int s,int t) { //對記錄序列R[s..t]進行快速排序 if (s<t) { int k,i; k=Partirion(R,s,t); QuickSort(R,s,k-1); QuickSort(R,k+1,t); cout<<"QuickSort:\n"; for(i=2;i<=t;i++) cout<<setw(4)<<R[i].key; cout<<endl; } } int _tmain() { RecType SL[100]; int n; cout<<"QuickSort.cpp運行結果:\n"; int b[100],i; srand(time(0)); cout<<"輸入待排序元素個數n:";cin>>n; for(i=1;i<n;i++) { b[i]=rand()%100; SL[i].key=b[i]; } cout<<"排序前數組:\n"; for(i=1;i<n;i++) cout<<setw(4)<<b[i]; cout<<endl; QuickSort(SL,1,n); cout<<"排序後數組:\n"; for(i=2;i<=n;i++) cout<<setw(4)<<SL[i].key; cout<<endl; system("pause"); }
運行結果:
選擇排序:從記錄的無序序列中「選擇」關鍵字最小或最大的記錄,並將他加入到有序子序列中,以增長記錄的有序序列的長度。
基本思想是依次從待排序記錄中選擇出關鍵字值最小(或最大)的記錄、關鍵字值次之的記錄、...並分別將它們定位到序列左側(或右側)的第1位置、第2位置、...,從而使待排序的記錄序列成爲按關鍵字值由小到大(或有大到小)排列的有序序列。
有序序列中全部記錄的關鍵字均小於無序序列中記錄的關鍵字,則第i趟直接選擇排序是從無序序列R[i..n]的n-i+1個記錄中選擇關鍵字最小的記錄加入有序序列的末尾。
#include "stdafx.h" #define number 100 #include<iostream> #include<time.h> #include<stdlib.h> #include<iomanip> #include<math.h> //排序後按從小到大輸出 using namespace std; typedef struct { int key; int other; //記錄其餘數據域 int next; }RecType; RecType S[number+1]; void SelectSort(RecType R[],int n) { //對記錄序列R[1..n]進行直接選擇排序 for (int i=1;i<n;i++) { int k=i; for (int j=i+1;j<=n;j++) { if (R[k].key>R[j].key) {k=j;} } if (i!=k) { RecType temp; temp=R[i]; R[i]=R[k]; R[k]=temp; } } } int _tmain() { RecType SL[100]; int n; cout<<"SelectSort.cpp運行結果:\n"; int b[100],i; srand(time(0)); cout<<"輸入待排序元素個數n:"; cin>>n; for(i=1;i<n;i++) { b[i]=rand()%100; SL[i].key=b[i]; } cout<<"排序前數組:\n"; for(i=1;i<n;i++)cout<<setw(4)<<b[i]; cout<<endl;SelectSort(SL,n); cout<<"排序後數組:\n"; for(i=2;i<=n;i++) cout<<setw(4)<<SL[i].key; cout<<endl; system("pause"); }
基本思想:首先是對n個待排序記錄的關鍵字進行兩兩比較,從中選出[n/2]個較小者再兩兩比較,直到選出關鍵字最小的記錄爲止,此爲一趟排序。
#include "stdafx.h" //錦標賽排序法 #include<iostream> #include<iomanip> #include<math.h> #include<stdlib.h> #include<time.h> using namespace std; class DataNode //勝者樹結點的類定義 {public: int data;//數據值 int index;//樹中的結點號 int active;//參選標誌 }; //錦標賽排序中的調整算法;i是表中當前 //最小元素的下標,即勝者.從它開始向上調整 void UpdataTree(DataNode *tree,int i) {int j; if(i%2==0) //i爲偶數,對手爲左結點 tree[(i-1)/2]=tree[i-1];//i爲奇數,對手爲右結點 else tree[(i-1)/2]=tree[i+1]; i=(i-1)/2; //i上升到雙親結點位置 while(i) {if(i%2==0) j=i-1;//肯定i的對手爲左結點仍是右結點 else j=i+1; if(!tree[i].active||!tree[j].active)//比賽對手中間有一個空 if(tree[i].active) tree[(i-1)/2]=tree[i]; else tree[(i-1)/2]=tree[j]; //非空者上升到雙親結點 else //比賽對手都不爲空 if(tree[i].data<tree[j].data) tree[(i-1)/2]=tree[i]; else tree[(i-1)/2]=tree[j];//勝者上升到雙親結點 i=(i-1)/2; //i上升到雙親結點 }} //創建樹的順序存儲數組tree,將數組a[]中的元素複製到勝者樹中, //對它們進行排序,並把結果返回數組中,n是待排序元素個數 void TournmentSort(int a[],int n) {DataNode *tree; //勝者樹結點數組 DataNode item; int m,i,j=0; for(int k=0;k<n;k++)//計算知足>=n的2的最小次冪的數 { m=(int)pow((double) 2,(double)k); if(m>=n) break; } int bottomRowSize=m; int TreeSize=2*bottomRowSize-1;//計算勝者樹的大小:內結點+外結點數 int loadindex=bottomRowSize-1;//外結點開始位置 tree=new DataNode[TreeSize]; //動態分配勝者樹結點數組空間 for(i=loadindex;i<TreeSize;i++)//複製數組數據到樹的外結點中 {tree[i].index=i;//下標 if(j<n) {tree[i].active=1;tree[i].data=a[j++];}//複製數據 else tree[i].active=0; //後面的結點爲空的外結點 } i=loadindex; //進行初始比較尋找最小的項 while(i) {j=i; while(j<2*i) //處理各對比賽者 {if(!tree[j+1].active||tree[j].data<tree[j+1].data) tree[(j-1)/2]=tree[j]; else tree[(j-1)/2]=tree[j+1];//勝者送入雙親 j+=2; //下一對參加比較的項 } i=(i-1)/2;//i退到雙親,直到i=0爲止 } for(i=0;i<n-1;i++)//處理其餘n-1個元素 {a[i]=tree[0].data;//當前最小元素送數組a tree[tree[0].index].active=0;//該元素相應外結點再也不比賽 UpdataTree(tree,tree[0].index);//從該處向上修改 } a[n-1]=tree[0].data; } //錦標賽排序法的測試 void main() {cout<<"JinBiaoSai.cpp運行結果:\n"; int n,b[100],i; srand(time(0)); cout<<"輸入待排序元素個數n:";cin>>n; for(i=0;i<n;i++) b[i]=rand()%100; cout<<"排序前數組:\n"; for(i=0;i<n;i++) cout<<setw(4)<<b[i]; cout<<endl; TournmentSort(b,n); cout<<"排序後數組:\n"; for(i=0;i<n;i++) cout<<setw(4)<<b[i]; cout<<endl; cin.get();cin.get(); }
樹形選擇排序:
#include "stdafx.h" #include<iostream> #include<string.h> #include<ctype.h> #include<malloc.h> #include<limits.h> #include<stdio.h> #include<stdlib.h> #include<io.h> #include<math.h> using namespace std; #define MAX_SIZE 20 typedef int KeyType; typedef int InfoType; //定義其餘數據類型 struct RedType{ KeyType key; InfoType otherinfo; }; struct SqList { RedType r[MAX_SIZE]; int length; }; void print(SqList L) { int i; for(i=1;i<=L.length;i++) cout<<"("<<L.r[i].key<<","<<L.r[i].otherinfo<<")"; cout<<endl; } void TreeSort(SqList &L) { int i,j,j1,k,k1,l; float n=L.length; RedType *t; l=(int)ceil(log(n)/log(2.0))+1; //徹底二叉樹的層數;ceil取上整,返回比x大的最小整數 k=(int)pow(2.0,l)-1; //k爲l層徹底二叉樹的結點總數 k1=(int)pow(2.0,l-1)-1; //k1爲l-1層二叉徹底二叉樹的結點總數 t=(RedType*)malloc(k*sizeof(RedType)); //二叉樹採用順序存儲 for(i=1;i<=n;i++) //將L.r賦值給葉子結點 t[k1+i-1]=L.r[i]; for(i=k1+n;i<k;i++) //給多餘的葉子結點的關鍵字賦無窮大值 t[i].key=INT_MAX; j1=k1; j=k; while(j1) { //給非葉子結點賦值 for(i=j1;i<j;i+=2) { t[i].key<t[i+1].key?(t[(i+1)/2-1]=t[i]):(t[(i+1)/2-1]=t[i+1]); } j=j1; j1=(j1-1)/2; cout<<"給非葉子結點賦值:"<<endl; //循環一次,找到最小的 print(L); } for(i=0;i<n;i++) { L.r[i+1]=t[0]; //鍵當前最小值賦給L.r[i] j1=0; for(j=1;j<l;j++) //沿樹根找到結點t[0]在葉子結中的序號j1 t[2*j1+1].key==t[j1].key?(j1=2*j1+1):(j1=2*j1+2); t[j1].key=INT_MAX; while(j1) { j1=(j1+1)/2-1; //序號爲j1的節點的雙親節點序號 t[2*j1+1].key<=t[2*j1+2].key?(t[j1]=t[2*j1+1]):(t[j1]=t[2*j1+2]); } cout<<"循環排序:"<<endl; print(L); } free(t); } #define N 8 void main() { RedType d[N]={{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8}}; SqList l; int i; for(i=0;i<N;i++) l.r[i+1]=d[i]; l.length=N; cout<<"排序前:"<<endl; print(l); TreeSort(l); cout<<"排序後:"<<endl; print(l); system("pause"); }
排序過程當中t[k1+i-1]=L.r[i];把L裏的數據存儲t[k1+i-1]中,爲排序須要
k=(int)pow(2.0,l)-1; //k爲l層徹底二叉樹的結點總數
t=(RedType*)malloc(k*sizeof(RedType)); //二叉樹採用順序存儲
的存儲空間;
運行結果:
其左右子樹分別是堆,任何一個結點的值不大(或不小於)左右孩子節點,則最頂端元素必是序列中的最大值或最小值,分別稱爲小頂堆或大頂堆(小堆或大堆)。堆排序是利用堆的特性對記錄序列進行排序的一種排序算法。其基本思想是:先建一個堆,即先選一個關鍵字最大或最小的記錄,而後與序列中的最後一個記錄交換,以後將序列中前n-1個記錄從新調整爲一個堆(調堆的過程稱爲「篩選」),再將堆頂記錄和第n-1個記錄交換,如此反覆至排序結束。注意:第i 次篩選的前提是從2到n-i 是一個堆,即篩選是對一棵左/右子樹均爲堆的徹底二叉樹「調整」根節點使整棵二叉樹爲堆的過程。
#include "stdafx.h" #include <iostream> using namespace std; void sift(int R[],int i,int m) //R[]要排序的數組;i是指是從堆的第幾行的首元素 即i=1 2 4 8 16...;m是指數組中一共多少個元素 { //設R[i+1..m]中各元素知足堆的定義,本算法調整R[i]使序列R[i..m]中的元素知足堆的性質 int temp = R [i]; for (int j=2*i;j<=m;j*=2) { //在堆的同一層進行比較,若是右邊的大於左邊的,則繼續向右查找 ,爲了保證下面和temp比較的R[j],是這一層上最大的一個 if ((j<m)&&(R[j]<R[j+1])) { j++; } if (temp<R[j]) //temp和每一層最大的數比較,若是小於最大的,則把最大的的值賦給R[i] { R[i]=R[j]; i=j; //修改i的值,此時R[i]=R[j]的值相同 } else { break; } } R[i]=temp; //最初要調整的節點放在正確的位置 } void HeapSort( int R[],int n) { int i ; int nCreateTime=1; for (i=n/2;i>0;i--) { sift(R,i,n); //建堆時i從n/2起遞減,是指i從從最底層起依次循環向頂層排序 std::cout<<"建堆"<<nCreateTime<<endl; for(int j=1;j<9;j++) { std::cout << R[j]; } nCreateTime++; std::cout<<endl; } int nAdjustTime=1; for (i=n;i>1;i--) { int temp ; temp=R[i]; R[i]=R[1]; R[1]=temp; sift(R,1,i-1); std::cout<<"從新調整"<<nAdjustTime<<endl; for(int j=1;j<9;j++) { std::cout << R[j]; } nAdjustTime++; std::cout<<endl; } } int _tmain(int argc, _TCHAR* argv[]) { int data[9]={0,6,9,3,1,5,8,9}; std::cout<<"初始序列"<<endl; for(int j=1;j<9;j++) { std::cout << data[j]; } std::cout<<endl; HeapSort( data,8); std::cout<<"生成的有序序列"<<endl; for(int j=1;j<9;j++) { std::cout << data[j]; } std::cout<<endl; system("pause"); return 0; }
運行結果:
歸併排序:經過「歸併」兩個或兩個以上的有序序類,逐步增長有序序列的長度。
其基本思想是:將一個具備n個待排序記錄的序列當作是n個長度爲1的有序序列,而後進行兩兩歸併,獲得n/2個長度爲2的有序序列,在進行兩兩歸併,獲得n/4個長度爲4的有序序列,如此重複,直至獲得一個長度爲n的有序序列爲止。
#include "stdafx.h" #define number 100 #include<iostream> #include<time.h> #include<stdlib.h> #include<iomanip> #include<math.h> //排序後按從小到大輸出 using namespace std; typedef struct { int key; int other; //記錄其餘數據域 int next; }RecType; RecType S[number+1]; void Merge(RecType R[],RecType R1[],int i,int l,int h) { //將有序的R[i..l]和R[l+1..h]歸併爲有序的R1[i..h] int j; int k; for (j=l+1,k=i;i<=l && j<=h;k++) { //將R[]中記錄由小到大地併入R1 if (R[i].key<=R[j].key) { R1[k]=R[i++]; } else { R1[k]=R[j++]; } } //for if(i<=l) { R1[k++]=R[i++]; } if(j<=h) { R1[k++]=R[j++]; //將剩餘的R[j..h]複製到R1 } }//Merge void Msort(RecType R[],RecType R1[],int s,int t) { //將R[s..t]2-路歸併排序爲R1[s..t] RecType R2[100]; if (s==t) { R1[s]=R[s]; } else { int m=(s+t)/2; //將R[s..t]平分爲R[s..m]和R[m+1..t] Msort(R,R2,s,m); //遞歸的將R[s..m]歸併爲有序的R2[s..m] Msort(R,R2,m+1,t); //遞歸的將R[m+1..t]歸併爲有序的R2[m+1..t] Merge(R2,R1,s,m,t); //將R2[s..m]和R2[m+1..t]歸併到R1[s..t] }//if }//Msort int _tmain() { int n; cout<<"MergingSort.cpp運行結果:\n"; int b[100],i; RecType R1[100]; srand(time(0)); cout<<"輸入待排序元素個數n:";cin>>n; RecType SL[100]; for(i=1;i<=n;i++) { b[i]=rand()%100; SL[i].key=b[i]; } cout<<"排序前數組:\n"; for(i=1;i<=n;i++) cout<<setw(4)<<b[i]; cout<<endl; Msort(SL,R1,1,n); cout<<"排序後數組:\n"; for(i=1;i<=n;i++) cout<<setw(4)<<R1[i].key; cout<<endl; system("pause"); }
前面討論的排序算法都是基於關鍵字之間的比較,經過比較判斷出大小,而後進行調整。而分配排序則否則,它是利用關鍵字的結構,經過「分配」和「收集」的辦法來實現排序。
分配排序:經過對無序序列中的記錄進行反覆的「分配」和「收集」操做,逐步是無序序列變爲有序序列。
分配排序分爲:桶排序和基數排序兩類。
計數排序的過程相似小學選班幹部的過程,如某某人10票,做者9票,那某某人是班長,做者是副班長
大致分兩部分,第一部分是拉選票和投票,第二部分是根據你的票數入桶
看下具體的過程,一共須要三個數組,分別是待排數組,票箱數組,和桶數組
var unsorted = new int[] { 6, 2, 4, 1, 5, 9 }; //待排數組
var ballot = new int[unsorted.Length]; //票箱數組
var bucket = new int[unsorted.Length]; //桶數組
最後再看桶數組,先看待排數組和票箱數組
初始狀態,迭代變量i = 0時,待排數組[i] = 6,票箱數組[i] = 0,這樣經過迭代變量創建了數字與其桶號(即票數)的聯繫
待排數組[ 6 2 4 1 5 9 ] i = 0時,能夠從待排數組中取出6
票箱數組[ 0 0 0 0 0 0 ] 同時能夠從票箱數組裏取出6的票數0,即桶號
拉選票的過程
首先6出列開始拉選票,6的票箱是0號,6對其它全部數字說,誰比我小或與我相等,就給我投票,否則揍你
因而,2 4 1 5 分別給6投票,放入0號票箱,6得四票
待排數組[ 6 2 4 1 5 9 ]
票箱數組[ 4 0 0 0 0 0 ]
接下來2開始拉選票,對其它人說,誰比我小,誰投我票,否則弄你!因而1投了一票,其餘人比2大不搭理,心想你可真二
因而2從1那獲得一票
待排數組[ 6 2 4 1 5 9 ]
票箱數組[ 4 1 0 0 0 0 ]
再而後是,
4獲得2和1的投票,共計兩票
1獲得0票,沒人投他
5獲得2,4,1投的三張票
9是最大,獲得全部人(本身除外)的投票,共計5票(數組長度-1票)
投票完畢時的狀態是這樣
待排數組[ 6 2 4 1 5 9 ]
票箱數組[ 4 1 2 0 3 5 ]
入桶的過程
投票過程結束,每人都擁有本身的票數,桶數組說,看好你本身的票數,進入與你票數相等的桶,GO
6共計5票,進入5號桶
2得1票,進入1號桶,有幾票就進幾號桶
4兩票,進2號桶,5三票進3號桶,9有5票,進5號桶
待排數組[ 6 2 4 1 5 9 ]
票箱數組[ 4 1 2 0 3 5 ]
-----------------------
入桶前 [ 0 1 2 3 4 5 ] //裏邊的數字表示桶編號
入桶後 [ 1 2 4 5 6 9 ] //1有0票,進的0號桶
排序完畢,順序輸出便可[ 1 2 4 5 6 9]
能夠看到,數字越大票數越多,9獲得除本身外的全部人的票,5票,票數最多因此9最大,
每一個人最多擁有[數組長度減去本身]張票
1票數最少,因此1是最小的
#include "stdafx.h" #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <stdlib.h> #include<time.h> #include<iomanip> #include<math.h> using namespace std; void CountingSort(int *A,int *B,int *Order,int N,int K) { int *C=new int[K+1]; int i; memset(C,0,sizeof(int)*(K+1)); for (i=1;i<=N;i++) //把A中的每一個元素分配 C[A[i]]++; for (i=2;i<=K;i++) //統計不大於i的元素的個數 { C[i]+=C[i-1]; } for (i=N;i>=1;i--) { B[C[A[i]]]=A[i]; //按照統計的位置,將值輸出到B中,將順序輸出到Order中 Order[C[A[i]]]=i; C[A[i]]--; } } int main() { int *A,*B,*Order,N=15,K=10,i; A=new int[N+1]; B=new int[N+1]; Order=new int[N+1]; for (i=1;i<=N;i++) A[i]=rand()%K+1; //生成1..K的隨機數 cout<<"Before CS:\n"; for (i=1;i<=N;i++) cout<<setw(4)<<A[i]; cout<<endl; CountingSort(A,B,Order,N,K); printf("\nAfter CS:\n"); for (i=1;i<=N;i++) cout<<setw(4)<<B[i]; cout<<endl; cout<<"\nOrder:\n"; for (i=1;i<=N;i++) cout<<setw(4)<<Order[i]; cout<<endl; system("pause"); return 0; }
運行結果:
桶排序(Bucket Sort)也稱箱排序(Bin Sort),其基本思想是:設置若干個桶,依次掃描待排序記錄R[1..n],把關鍵字等於k的記錄所有都裝到第k個桶裏(分配),按序號依次將各非空的桶首尾鏈接起來(收集)。桶的個數取決於關鍵字的取值範圍。
桶排序 (Bucket sort)或所謂的箱排序,是一個排序算法,工做的原理是將數組分到有限數量的桶子裏。
基本思路:
1.設置一個定量的數組看成空桶子。
2.尋訪串行,而且把項目一個一個放到對應的桶子去。
3.對每一個不是空的桶子進行排序。
4.從不是空的桶子裏把項目再放回原來的串行中。
無序數組有個要求,就是成員隸屬於固定(有限的)的區間,如範圍爲[0-9](考試分數爲1-100等)
例如待排數字[6 2 4 1 5 9]
準備10個空桶,最大數個空桶
[6 2 4 1 5 9] 待排數組
[0 0 0 0 0 0 0 0 0 0] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶編號(實際不存在)
1,順序從待排數組中取出數字,首先6被取出,而後把6入6號桶,這個過程相似這樣:空桶[ 待排數組[ 0 ] ] = 待排數組[ 0 ]
[6 2 4 1 5 9] 待排數組
[0 0 0 0 0 0 6 0 0 0] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶編號(實際不存在)
2,順序從待排數組中取出下一個數字,此時2被取出,將其放入2號桶,是幾就放幾號桶
[6 2 4 1 5 9] 待排數組
[0 0 2 0 0 0 6 0 0 0] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶編號(實際不存在)
3,4,5,6省略,過程同樣,所有入桶後變成下邊這樣
[6 2 4 1 5 9] 待排數組
[0 1 2 0 4 5 6 0 0 9] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶編號(實際不存在)
0表示空桶,跳過,順序取出便可:1 2 4 5 6 9
/* 桶排序實現 */ #include "stdafx.h" #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <stdlib.h> #include<time.h> #include<iomanip> #include<math.h> using namespace std; void BucketSort(int *A,int *B,int N,int K) { int *C=new int[K+1]; int i,j,k; memset(C,0,sizeof(int)*(K+1)); for (i=1;i<=N;i++) //把A中的每一個元素按照值放入桶中 C[A[i]]++; for (i=j=1;i<=K;i++,j=k) //統計每一個桶元素的個數,並輸出到B for (k=j;k<j+C[i];k++) B[k]=i; } int main() { int *A,*B,N=100,K=100,i; A=new int[N+1]; B=new int[N+1]; cout<<"排序前:"<<endl; for (i=1;i<=N;i++) { A[i]=rand()%K+1; //生成1..K的隨機數 cout<<setw(4)<<A[i]; } cout<<endl; BucketSort(A,B,N,K); cout<<"排序後:"<<endl; for (i=1;i<=N;i++) { cout<<setw(4)<<B[i]; } cout<<endl; system("pause"); return 0; }
運行結果:
這種特殊實現的方式時間複雜度爲O(N+K),空間複雜度也爲O(N+K),一樣要求每一個元素都要在K的範圍內。更通常的,若是咱們的K很大,沒法直接開出O(K)的空間該如何呢?
首先定義桶,桶爲一個數據容器,每一個桶存儲一個區間內的數。依然有一個待排序的整數序列A,元素的最小值不小於0,最大值不超過K。假設咱們有M個桶,第i個桶Bucket[i]存儲iK/M至(i+1)K/M之間的數,有以下桶排序的通常方法:
1.掃描序列A,根據每一個元素的值所屬的區間,放入指定的桶中(順序放置)。
2.對每一個桶中的元素進行排序,什麼排序算法均可以,例如快速排序。
3.依次收集每一個桶中的元素,順序放置到輸出序列中。
對該算法簡單分析,若是數據是指望平均分佈的,則每一個桶中的元素平均個數爲N/M。若是對每一個桶中的元素排序使用的算法是快速排序,每次排序的時間複雜度爲O(N/Mlog(N/M))。則總的時間複雜度爲O(N)+O(M)O(N/Mlog(N/M)) = O(N+ Nlog(N/M)) = O(N + NlogN - NlogM)。當M接近於N是,桶排序的時間複雜度就能夠近似認爲是O(N)的。就是桶越多,時間效率就越高,而桶越多,空間卻就越大,因而可知時間和空間是一個矛盾的兩個方面。
/*桶排序實現 */ #include "stdafx.h" #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <stdlib.h> #include<time.h> #include<iomanip> #include<math.h> using namespace std; struct linklist { linklist *next; int value; linklist(int v,linklist *n):value(v),next(n){} ~linklist() {if (next) delete next;} }; inline int cmp(const void *a,const void *b) { return *(int *)a-*(int *)b; } /* 爲了方便,我把A中元素加入桶中時是倒序放入的,而收集取出時也是倒序放入序列的,因此不違背穩定排序。 */ void BucketSort(int *A,int *B,int N,int K) { linklist *Bucket[101],*p; //創建桶 int i,j,k,M; M=K/100; memset(Bucket,0,sizeof(Bucket)); for (i=1;i<=N;i++) { k=A[i]/M; //把A中的每一個元素按照的範圍值放入對應桶中 Bucket[k]=new linklist(A[i],Bucket[k]); } for (k=j=0;k<=100;k++) { i=j; for (p=Bucket[k];p;p=p->next) B[++j]=p->value; //把桶中每一個元素取出,排序並加入B delete Bucket[k]; qsort(B+i+1,j-i,sizeof(B[0]),cmp); } } int main() { int *A,*B,N=100,K=100,i; A=new int[N+1]; B=new int[N+1]; for (i=1;i<=N;i++) { A[i]=rand()%K+1; //生成1..K的隨機數 cout<<setw(4)<<A[i]; } cout<<endl; BucketSort(A,B,N,K); cout<<"排序後:"<<endl; for (i=1;i<=N;i++) { cout<<setw(4)<<B[i]; } cout<<endl; system("pause"); return 0; }
運行結果:
基數排序(Radix Sort)是對桶排序的改進和推廣。
基本思想:將一個關鍵字分解成多個「關鍵字」,再利用多關鍵字排序的思想對記錄序列進行排序。基數排序就是藉助「多關鍵字排序」的思想來實現「單關鍵字排序」的算法。
假設有n個記錄的待排序序列{R1,R2,...,Rn},每一個記錄Ri中含有d個關鍵字,則稱上述記錄序列對關鍵字有序,是指對於序列中的任意兩個記錄Ri和Rj(1<=i<j<=n)都知足下列(詞典)有序關係;
具體作法爲:
(1)待排序的記錄以指針相鏈,構成一個鏈表。
(2)「分配」時,按當前「關鍵字位」的取值,將記錄分配到不一樣的「鏈隊列」中,每一個隊列中記錄的「關鍵字位」相同。
(3)「收集」時,按當前關鍵字取值從小到大將各隊首尾相連接成一個鏈表;對每一個關鍵字位均重複2)和3)兩步。以下所示:
p->369->367->167->239->237->138->230->139
第一次分配獲得:
B[0].f->230<-B[0].e
B[7].f->367->167->237<-B[7].e
B[8].f->138<-B[8].e
B[9].f->369->239->139<-B[9].e
第一次收集獲得:
p->230->367->167->237->138->369->239->139
第二次分配獲得:
B[3].f->230->237->138->239->139<-B[3].e
B[6].f->367->167->369<-B[6].e
第二次收集獲得:
p->230->237->138->239->139->367->167->369
第三次分配獲得:
B[1].f->138->139->167<-B[1].e
B[2].f->230->237->239<=B[2].e
B[3].f->367->369<-B[3].e
第三次收集以後便獲得記錄的有序序列:
p->138->139->167->230->237->239->367->369
#include "stdafx.h" #include <iostream> #include<stdlib.h> #include<iomanip> #include<math.h> using namespace std; int maxbit(int data[],int n) //輔助函數,求數據的最大位數 { int d = 1; //保存最大的位數 int p =10; for(int i = 0;i < n; ++i) { while(data[i] >= p) { p *= 10; ++d; } } return d; } void radixsort(int data[],int n) //基數排序 { int d = maxbit(data,n); int * tmp = new int[n]; int * count = new int[10]; //計數器 int i,j,k; int radix = 1; for(i = 1; i<= d;i++) //進行d次排序 { for(j = 0;j < 10;j++) count[j] = 0; //每次分配前清空計數器 for(j = 0;j < n; j++) { k = (data[j]/radix)%10; //統計每一個桶中的記錄數 count[k]++; } for(j = 1;j < 10;j++) count[j] = count[j-1] + count[j]; //將tmp中的位置依次分配給每一個桶 for(j = n-1;j >= 0;j--) //將全部桶中記錄依次收集到tmp中 { k = (data[j]/radix)%10; tmp[count[k]-1] = data[j]; count[k]--; } for(j = 0;j < n;j++) //將臨時數組的內容複製到data中 data[j] = tmp[j]; radix = radix*10; } delete [] tmp; delete [] count; } int _tmain(int argc, _TCHAR* argv[]) { int data[10]={267,6,9,679,0,999,99,9,22,11}; std::cout<<"初始序列"<<endl; for(int j=0;j<10;j++) { std::cout <<setw(4)<< data[j]; } std::cout<<endl; radixsort( data,10); std::cout<<"生成的有序序列"<<endl; for(int j=0;j<10;j++) { std::cout << setw(4)<<data[j]; } std::cout<<endl; system("pause"); system("pause"); return 0; }
運行結果:
排序法 | 平均時間 | 最差情形 | 穩定度 | 額外空間 | 備註 |
冒泡 | O(n2) | O(n2) | 穩定 | O(1) | n小時較好 |
交換 | O(n2) | O(n2) | 不穩定 | O(1) | n小時較好 |
選擇 | O(n2) | O(n2) | 不穩定 | O(1) | n小時較好 |
插入 | O(n2) | O(n2) | 穩定 | O(1) | 大部分已排序時較好 |
基數 | O(logRB) | O(logRB) | 穩定 | O(n) | B是真數(0-9), R是基數(個十百) |
Shell | O(nlogn) | O(ns) 1<s<2 | 不穩定 | O(1) | s是所選分組 |
快速 | O(nlogn) | O(n2) | 不穩定 | O(nlogn) | n大時較好 |
歸併 | O(nlogn) | O(nlogn) | 穩定 | O(1) | n大時較好 |
堆 | O(nlogn) | O(nlogn) | 不穩定 | O(1) | n大時較好 |
外部排序:若參加排序的記錄數量很大,整個排序過程不能一次在內存中完成,則稱此類排序問題爲外部排序;
以上博客,若有參考使用,請標明出處;
參考:
《算法與數據結構》 第二版 陳守孔 著
參考的網上內容:
http://blog.csdn.net/onedreamer/article/details/6745006
http://blog.csdn.net/zhangkaihang/article/details/7394806
http://blog.csdn.net/yincheng01/article/category/1269370/1
http://blog.csdn.net/yincheng01/article/details/8205037
http://www.chinadmd.com/file/wt63reerwwo6wcpxeiv3twx6_1.html
https://www.byvoid.com/blog/sort-radix
http://www.cnblogs.com/kkun/archive/2011/11/23/2260299.html
http://www.cnblogs.com/kkun/archive/2011/11/23/2260267.html
http://blog.csdn.net/hkx1n/article/details/3922249