目錄
一、各排序算法的時間複雜度與空間複雜度比較
類別 | 排序方法 | 時間複雜度 | 空間複雜度 | 穩定性 | ||
平均情況 | 最好情況 | 最壞情況 | 輔助存儲 | |||
插入類排序 | 直接插入 | O(n^2) | O(n) | O(n^2) | O(1) | 穩定 |
希爾排序 | O(n^1.5) | O(n) | O(n^2) | O(1) | 不穩定 | |
交換類排序 | 起泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 穩定 |
快速排序 | O(nlog2n) | O(nlog2n) | O(n^2) | O(log2n) | 不穩定 | |
選擇類排序 | 簡單選擇排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不穩定 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不穩定 | |
歸併排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 穩定 | |
基數排序 | O(d(n+rd)) | O(d(n+rd)) | O(d(n+rd)) | O(rd) | 穩定 | |
二、直接插入排序
1、算法思想
每趟將一個待排序的關鍵字按照其值的大小插入到已經排好的部分有序序列的適當位置上,直到所有待排關鍵字都被插入到有序序列中爲止。
2、代碼實現
#include <iostream>
using namespace std;
void InsertSort(int R[],int n);
void outPutArr(int arr[],int n);
void main()
{
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
InsertSort(arr,len);
}
/**
* 直接插入排序算法
*/
void InsertSort(int R[],int n){
int i,j;
int temp;
for(i=1;i<n;i++){
temp = R[i]; //將待插入關鍵字暫存在temp中
j = i-1;
/*下面這個循環完成了從待排關鍵字之前的關鍵字開始掃描,如果大於待排關鍵字,則後移一位*/
while(j>=0&&temp<R[j]){
R[j+1] = R[j];
j--;
}
R[j+1] = temp; //找到插入位置,將temp中暫存的待排關鍵字插入
}
//對排序了的數組輸出
outPutArr(R,n);
}
/**
* 數組的輸出函數
*/
void outPutArr(int arr[],int n){
cout <<"<直接插入排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<";\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)最好情況時間複雜度:O(n);
(2)最壞情況時間複雜度:O(n2);
(3)平均時間複雜度:O(n2);
(4)空間複雜度:O(1);
三、希爾排序
1、算法思想
先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分組。所有距離爲d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有記錄放在同一組中進行直接插入排序爲止。
2、代碼實現
#include<iostream>
using namespace std;
void shellSort(int R[],int n);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
shellSort(arr,len);
}
/**
希爾排序算法
*/
void shellSort(int R[],int n){
int temp;
for(int gap = n/2;gap>0;gap/=2){
for(int i = gap;i<n;i++){
temp = R[i];
int j;
for(j = i;j>=gap&&R[j-gap]>temp;j-=gap){
R[j] = R[j-gap];
}
R[j] = temp;
}
}
outPutArr(R,n);
}
/**
*數組的輸出方法
*/
void outPutArr(int arr[],int n){
cout <<"<希爾排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)希爾自己提出的時間複雜度:O(n2);
(2)帕佩爾諾夫和斯塔舍維奇提出的時間複雜度:O(n1.5);
(3)空間複雜度:O(1);
四、起泡排序
1、算法思想
起泡排序屬於比較簡單的排序,以非遞減爲例,依次遍歷數組,發現R[j-1]>R[j]的情況,則交換R[j-1]和R[j]的順序,直到沒有逆序的數據,即完成排序。
2、代碼實現
#include <iostream>
using namespace std;
void bubbleSort(int R[],int n);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
bubbleSort(arr,len);
}
/**
起泡排序算法
*/
void bubbleSort(int R[],int n){
int i,j;
bool flag;
int temp;
for(i = n-1;i>0;i--){
flag = false; //flag用來標記此趟排序是否發生了交換
for(j = 1;j<=i;j++){
if(R[j-1]>R[j]){
temp = R[j];
R[j] = R[j-1];
R[j-1] = temp;
flag = true; //如果沒發生交換,則flag爲0
}
}
if(!flag){ //一趟排序過程中沒有發生排序,則證明剩餘序列有序,不在冒泡
outPutArr(R,n);
return;
}
}
}
/**
*數組的輸出方法
*/
void outPutArr(int arr[],int n){
cout <<"<起泡排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<";\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)最好情況時間複雜度:O(n);
(2)最壞情況時間複雜度:O(n2);
(3)平均時間複雜度:O(n2);
(4)空間複雜度:O(1);
五、快速排序
1、算法思想
選取一個樞軸元素(通常爲待排序序列的第一個數),然後通過一趟排序將比樞軸數大的放在右邊,比樞軸小的放在左邊,接着對劃分好的兩個子序列再進行上述的排序。
2、代碼實現
#include<iostream>
using namespace std;
void outPutArr(int arr[],int n);
void quickSort(int R[] ,int low,int high);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int length = 9;
quickSort(arr,0,length-1);
outPutArr(arr,length);
}
void quickSort(int R[] ,int low,int high){
int temp;
int i = low, j = high;
if(low<high){
temp = R[low];
while(i<j){ //將數組中小於temp的放在左邊,大於temp的放在右邊
while(j>i && R[j]>=temp){ //從右往左掃描,找到一個小於temp的關鍵字
j--;
}
if(i<j){
R[i] = R[j]; //放在temp左邊
i++; //右移一位
}
while(i<j && R[i]<temp){ //從左往右掃描,找到一個大於temp的關鍵字
i++;
}
if(i<j){
R[j] = R[i]; //放在temp右邊
j--; //左移一位
}
}
R[i] = temp; //將temp放在最終位置
quickSort(R,low,i-1); //遞歸的對temp左邊的關鍵字排序
quickSort(R,i+1,high); //遞歸的對temp右邊的關鍵字排序
}
}
/**
*數組的輸出方法
*/
void outPutArr(int arr[],int n){
cout <<"<快速排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)最好情況時間複雜度:O(nlog2n);
(2)最壞情況時間複雜度:O(n2);
(3)平均時間複雜度:O(nlog2n);
(4)空間複雜度:O(log2n);
六、簡單選擇排序
1、算法思想
選擇類排序的主要動作是“選擇”,簡單選擇排序採用最簡單的選擇方式,從頭至尾順序掃描序列,找出最小的一個關鍵字,和第一個關鍵字交換,接着從剩下的關鍵字中繼續這種選擇和交換,最終使序列有序。
2、代碼實現
#include <iostream>
using namespace std;
void selectSort(int R[],int n);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int len = 9;
selectSort(arr,len);
}
/**
簡單選擇排序算法
*/
void selectSort(int R[],int n){
int i,j,k;
int temp;
for(i = 0;i<n;i++){
k = i;
/*下面這個循環是算法的關鍵,它從無序序列中挑出一個最小的關鍵字*/
for(j = i+1;j<n;j++){
if(R[k]>R[j]){
k = j;
}
}
/*下面這三句完成最小關鍵字與無序序列第一個關鍵字的交換*/
temp = R[i];
R[i] = R[k];
R[k] = temp;
}
outPutArr(R,n);
}
/**
*數組的輸出
*/
void outPutArr(int arr[],int n){
cout <<"<簡單選擇排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<";\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)時間複雜度:O(n2);
(2)空間複雜度:O(1);
七、堆排序
1、算法思想
堆排序是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
2、代碼實現
#include<iostream>
using namespace std;
void outPutArr(int arr[],int n);
void sift(int R[] ,int low,int high);
void heapSort(int R[], int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int length = 9;
heapSort(arr,length);
}
/**
堆排序算法主方法
*/
void heapSort(int R[], int n){
int i;
int temp;
for(i = n/2-1;i>=0;i--){ //建立初始堆
sift(R,i,n-1);
}
for(i = n-1;i>0;i--){ //進行n-1次循環,完成堆排序
temp = R[0]; //一下3句換出根節點的關鍵字,將其放入最終位置
R[0] = R[i];
R[i] = temp;
sift(R,0,i-1); //在減少了一個關鍵字的無序序列中調整
}
outPutArr(R,n);
}
/**
堆的局部調整方法
*/
void sift(int R[] ,int low,int high){ //關鍵字設定下表從0開始
int i = low,j = 2*i+1; //R[j]是R[i]的左孩子節點
int temp = R[i];
while(j<=high){
if(j<high && R[j]<R[j+1]){ //若右孩子較大,則j指向右孩子
j++; //j變爲2*i+2
}
if(temp<R[j]){
R[i] = R[j]; //將R[j]調整到雙親節點的位置
i = j; //修改i和j的值,繼續向下調整
j = 2*i+1;
}else{
break; //調整結束
}
}
R[i] = temp; //被調整節點放入最終位置
}
/**
*數組的輸出方法
*/
void outPutArr(int arr[],int n){
cout <<"<堆排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)時間複雜度:O(nlog2n);
(2)空間複雜度:O(1);
八、二路歸併排序
1、算法思想
二路歸併排序是採用的分而治之的思想。將一個待排序的序列分成兩個序列,分別對這兩個序列排序。而對於這兩個序列排序的方式也是和之前一樣,將這兩個序列分別分成兩個序列分別排序。一直這樣分割下去,知道序列中沒有元素或者只有一個元素爲止。因爲沒有元素的序列和只有一個元素的序列定是一個有序的序列,所以相當於將這個序列排序完畢,向上返回。返回的過程中做的最重要的一件事就是將兩個有序的序列合併成一個有序的序列。所以歸併排序最重要的兩步是分割和合並。
2、代碼實現
#include<iostream>
using namespace std;
void mergeSort(int R[],int low,int high);
void merge(int R[],int low,int mid, int high);
void outPutArr(int arr[],int n);
void main(){
int arr[] = {68,23,34,45,-33,41,999,-1,68};
int length = 9;
mergeSort(arr,0,length-1);
outPutArr(arr,length);
}
/**
歸併排序
*/
void mergeSort(int R[],int low,int high){
if(low<high){
int mid = (low+high)/2;
mergeSort(R,low,mid); //歸併排序前半段
mergeSort(R,mid+1,high); //歸併排序後半段
merge(R,low,mid,high); //將R數組中low~mid,mid~high兩段序列歸併爲一個序列
}
}
/**
將兩個序列歸併爲一個有序序列
*/
void merge(int R[],int low,int mid, int high){
int i,j,k;
int n1 = mid - low +1;
int n2 = high - mid;
int left[n1],right[n2];
for(i = 0;i<n1;i++){
left[i] = R[low+i];
}
for(j = 0;j<n2;j++){
right[j] = R[mid + 1 + j];
}
i = 0;j = 0;k = low;
while(i<n1 && j<n2){
if(left[i]<=right[j]){
R[k] = left[i++];
}else{
R[k] = right[j++];
}
k++;
}
while(i<n1){
R[k++] = left[i++];
}
while(j<n2){
R[k++] = right[j++];
}
}
/**
*數組的輸出方法
*/
void outPutArr(int arr[],int n){
cout <<"<歸併排序算法>\r\n"<<endl;
cout <<"數組長度:"<<n<<"\r\n"<<endl;
cout <<"升序序列:{";
for(int i=0;i<n;i++){
if(i<n-1){
cout << arr[i] << " , ";
}else{
cout << arr[i] <<"};\r\n"<<endl;
}
}
}
3、輸出結果
4、時間複雜度與空間複雜度
(1)時間複雜度:O(nlog2n);
(2)空間複雜度:O(n);