1. 冒泡排序
最差時間複雜度爲O(n2),最優時間複雜度爲O(n)。
比較相鄰的元素,若第一個比第二個大,則交換它們的位置;
依次進行比較直到到達末尾,此時最大元素在末尾;
從第一個到倒數第二個元素重複進行上一步;
不斷重複,直到有一次沒有任何元素交換或者只剩一個元素時終止,完成排序。
2. 快速排序
最差時間複雜度爲O(n2),最優時間複雜度爲O(nlogn)。算法採用分治思想。
首先取一個數爲基準(在本次實現中,取數組左邊的第一個數);
剩下數組的所有元素與之對比,若小於它則放在它左邊(與之交換位置),否則放在它右邊;
然後遞歸地對基準數左邊子序列和右邊子序列分別作相同的排序,直到子序列大小爲0或1,完成排序。
3. 堆排序
最差/最優時間複雜度都爲O(nlogn)。
最大堆化是對完全二叉樹中第i個位置爲根的子樹進行調整,使得子結點永遠小於父結點。
首先將數組建成最大堆(對葉子結點以外的其它結點最大堆化),使堆中的根結點爲最大的數;
將根結點與數組中最後一個元素交換位置,循環地將除最後一位之外的數組建成最大堆;
每次都將根結點放到當前的最後位置,直到只剩1個元素時,完成排序。
4. 選擇排序
最差/最優時間複雜度都爲O(n2)。
數組的前半部分爲有序的,後半部分爲無序;
每次選取無序部分的最小元素,與無序部分的第一個元素交換位置;
直到執行到數組中倒數第二個數,完成排序。
5. 插入排序
最差時間複雜度爲O(n2),最優時間複雜度爲O(n)。
數組前半部分爲有序,後半部分爲無序;
每次將無序部分的第一個數a在有序部分中從後向前掃描;
當找到小於等於a的數時將a插入它的下一位,否則將掃描到的元素後移一個位置;
直到最後一個元素,完成排序。
6. 基數排序
設待排序列爲n個記錄,d個關鍵碼,關鍵碼的取值範圍爲radix,則進行鏈式基數排序的時間複雜度爲O(d(n+radix)),其中,一趟分配時間複雜度爲O(n),一趟收集時間複雜度爲O(radix),共進行d趟分配和收集。
從右到左的每位數(個位/十位/百位/…)進行排序,每次只按照每個元素1個位上的數進行比較,直到循環次數爲最大數的位數值,完成排序。
7. 計數排序
最優/最差時間複雜度均爲O(n+k),此時有n個從0到k的待排序元素。
申請一個額外的數組A,大小爲待排序數組中最大值與最小值之差加1,用於存放各個數值的個數;
統計待排序數組中值爲i的元素個數,將它存放在數組A的第i項;
完成統計後,對A中元素值從左到右累加,從第一項開始,新值爲當前項與前一項的和;
按照待排序元素的值(從右到左)和數組A中的計數值,反向填充有序數組,沒填充一個相應的C中計數減1;
直到進行到待排序數組的第一個元素,完成排序。
8. 桶排序
最差時間複雜度爲O(n2),最優時間複雜度爲O(n+k)。
首先找出待排序數組中的最大數,進而求出取餘基數radix,例如:最大數爲238,位數爲3,取餘基數爲pow(10,3-1)=100;
將每個元素對radix取餘,並根據餘數的值將其分配到相應的桶中(0-9);
分配結束後,對桶中的元素進行插入排序,最後將有序桶中的元素依次存入有序數組中,完成排序。
9. 歸併排序
最差時間複雜度爲O(nlogn),最優時間複雜度爲O(n)。
申請空間大小爲兩個已排序序列之和,用來存放合併後的序列。它的速度僅次於快速排序,但它是穩定的,快速排序不穩定。
設定兩個指針,分別指向兩個已排序好的序列起始位置;
選擇所指向較小的數放到合併序列,並將指針向後移一位;
重複上一步直到某個序列指針到達尾部;
將另一個序列剩下的元素全部添加到合併序列尾部,完成。
10. 希爾排序
希爾排序也稱爲遞減增量排序,是插入算法一種高速而穩定的改進版本,插入算法複雜度爲O(n2),希爾排序複雜度爲O(n log2n)。
與插入算法相比,希爾排序僅增加了索引的步長,從i++變爲i+stepSize。排序時假設將數組分爲多行排列,每行stepSize個元素,然後對排好的每列採用插入排序;列有序後減小步長stepSize,重複上一步;直到步長爲1時,每行只有1個元素,再對列排序,即爲對所有元素進行插入排序,此時已是幾乎排序好的,插入排序效率高(可達到線性排序的效率O(n))。
以下爲練習時寫的代碼,如有耐心看,歡迎指正點評。
sorting.h
#ifndef SORTING_H_INCLUDED
#define SORTING_H_INCLUDED
using namespace std;
class basico
{
public:
int inputNums(int *&);
bool outputNums(int *,int);
bool swap(int &,int &);
int inputDouble(double *&);
int maxBit(int *,int);
};
//冒泡排序
class bubbleSort:public basico
{
public:
bool bubble(int *,int);
};
//快速排序
class quickSort:public basico
{
public:
bool quicksort(int *,int,int);
bool quick(int *,int);
};
//堆排序
class heapSort:public basico
{
public:
int parent(int);
int left(int);
int right(int);
bool maxHeapify(int *,int,int);
bool buildMaxHeap(int *,int);
bool heap(int *,int);
};
//選擇排序
class selectSort:public basico
{
public:
int minNum(int *,int,int);
bool select(int *,int);
};
//插入排序
class insertSort:public basico
{
public:
bool insert(int *,int);
};
//基數排序
class radixSort:public basico
{
public:
bool radix(int *,int);
};
//計數排序
class countSort:public basico
{
public:
int maxNum(int *,int);
bool count(int *,int);
};
//桶排序
class bucketSort:public insertSort
{
public:
bool bucket(int *,int);
};
//歸併排序
class mergeSort:public basico
{
public:
bool mergeArray(int *,int *,int,int,int);
bool subMerge(int *,int *,int,int);
bool merge(int *,int);
};
//希爾排序
class shellSort:public insertSort
{
public:
bool shell(int *,int);
};
#endif
sorting.cpp#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <new>
#include <time.h>
#include <iomanip>
#include <math.h>
#include "sorting.h"
using namespace std;
/*************************************輸入數組*************************************/
//輸入待排序數組
void noMemory()
{
cout<<"no adequate memory left"<<endl;
abort();
}
int basico::inputNums(int *&nums)
{
int n;
cout<<"please input the number of numbers: ";
cin>>n;
//增加空間分配失敗的判斷
set_new_handler(noMemory);
nums = (int*) malloc(sizeof(int)*n);
if(!nums)
return false;
int m;
srand((unsigned)time(0));
for(int s = 0;s < n;s++)
{
//cin>>m;
m = rand()%1000;
nums[s] = m;
}
cout<<"before sorting: ";
for(int l = 0;l < n;l++)
cout<<nums[l]<<" ";
cout<<endl;
//若直接返回int *nums容易出現詭異問題,換成引用比較安全
return n;
}
//交換數據
bool basico::swap(int &i,int &j)
{
int temp;
temp = i;
i = j;
j = temp;
return true;
}
//隨機生成兩位小數的浮點數
int basico::inputDouble(double *&nums)
{
int n,i;
double k;
cout<<"please input the number of numbers: ";
cin>>n;
set_new_handler(noMemory);
nums = (double *)malloc(sizeof(double)*n);
srand((unsigned)time(NULL));
for(i = 0;i < n;)
{
k = (1.0*rand())/RAND_MAX;
//需要 #include<iomanip>
//cout<<setprecision(2)<<k<<endl; //輸出時取兩位小數(結果被四捨五入)
k = (double)(int)(k*100)/100; //直接截取k的小數點後兩位,不四捨五入
if(k < 1.0) //保證k [0.0,1.0)
{
nums[i] = k;
i++;
}
else
continue;
}
cout<<"before sorting: ";
for(i = 0;i < n;i++)
cout<<nums[i]<<" ";
cout<<endl;
return n;
}
//返回數組中最大數的位數
int basico::maxBit(int *nums,int n)
{
int max = 1;
int radix = 10;
for(int i = 0;i < n;i++)
{
while(nums[i] > radix || nums[i] == radix)
{
radix = radix*10;
max++;
}
}
return max;
}
/*************************************冒泡排序*************************************/
//第一遍對數組裏的每個數從第一個開始與後一個數比較,若前面的數比較大則交換位置,將大數往後面排,第
//二遍做相同的操作,一直循環,直到數組有序,最壞情況下比較n*n次,時間複雜度爲O(n*n)
bool bubbleSort::bubble(int *nums,int n)
{
int i;
for(i = 0;i < n;i++)
{
int flag = 0;
for(int j = 0;j < n-1;j++)
{
if(nums[j] > nums[j+1])
{
swap(nums[j],nums[j+1]);
flag++;
}
}
if(flag == 0)
break;
}
cout<<"after bubbleSort: ";
outputNums(nums,n);
cout<<"compare "<<i+1<<" rounds"<<endl;
return true;
}
/*************************************快速排序*************************************/
//取數組中第一個數爲key,初始pos爲第一個數的位置,從第二個數開始到最後一個數,與key值比較,若小於
//等於key則pos的位置加1,且當前位置的數與pos位置上的數交換,結束時第一個位置上的key值與pos位置上
//的數交換位置,第一遍結束。然後運用分治思想,對pos左邊的數組和右邊的數組再做相同的排序。
bool quickSort::quicksort(int *nums,int i,int j)
{
if(i < j)
{
int key = nums[i],pos = i;
int temp;
for(int k = i+1;k < j+1;k++)
{
if(nums[k] < key || nums[k] == key)
{
pos++;
swap(nums[k],nums[pos]);
}
}
swap(nums[i],nums[pos]);
quicksort(nums,i,pos-1);
quicksort(nums,pos+1,j);
}
return true;
}
bool quickSort::quick(int *nums,int n)
{
int i = 0,j = n-1;
quicksort(nums,i,j);
cout<<"after quickSort: ";
outputNums(nums,n);
return true;
}
/*************************************堆排序*************************************/
int heapSort::parent(int i)
{
i = i/2;
return i;
}
int heapSort::left(int i)
{
i = 2*i;
return i;
}
int heapSort::right(int i)
{
i = 2*i+1;
return i;
}
//對數組nums的完全二叉樹中在第i個位置爲根的子樹進行最大堆化
bool heapSort::maxHeapify(int *nums,int i,int heapSize)
{
int l = left(i);
int r = right(i);
int largest;
//i爲在堆中位置,大小爲所在的數組下標+1
if(l < heapSize+1 && nums[l-1] > nums[i-1])
largest = l-1;
else
largest = i-1;
if(r < heapSize+1 && nums[r-1] > nums[largest])
largest = r-1;
if(largest != i-1)
{
swap(nums[i-1],nums[largest]);
maxHeapify(nums,largest+1,heapSize);
}
return true;
}
//可以通過自底向上地調用maxHeapify,葉子結點爲一個元素的堆,所以只對葉子以外的其他結點調用max函數
bool heapSort::buildMaxHeap(int *nums,int heapSize)
{
for(int k = heapSize/2;k > 0;k--)
maxHeapify(nums,k,heapSize);
return true;
}
//先將數組建成最大堆,取最大堆的根結點(即數組中的第一個數)與第heapSize個數組元素交換位置(即將最大
//數放到數組最後),heapSize-1,循環地將剩下數組中的數建成最大堆,將根結點放到當前數組最後
bool heapSort::heap(int *nums,int n)
{
int heapSize = n;
buildMaxHeap(nums,heapSize);
for(int k = heapSize;k > 1;k--)
{
swap(nums[0],nums[k-1]);
heapSize = heapSize-1;
maxHeapify(nums,1,heapSize);
}
cout<<"after heapSort: ";
outputNums(nums,n);
return true;
}
/*************************************選擇排序*************************************/
int selectSort::minNum(int *nums,int i,int n)
{
int pos = i;
for(int j = i;j < n-1;j++)
{
//保證當數組中有幾個相同最小數時,pos返回第一個的位置而不是最後一個
if(nums[pos] > nums[j+1])
{
pos = j+1;
}
}
return pos;
}
//每次選取最小的數,與當前循環起始位置上的數交換位置,前部分是有序數組,後部分爲無序數組,直到數
//組中的倒數第二個數結束循環,整個數組爲有序
bool selectSort::select(int *nums,int n)
{
int pos;
for(int i = 0;i < n-1;i++)
{
pos = minNum(nums,i,n);
if(pos != i)
swap(nums[i],nums[pos]);
}
cout<<"after selectSort: ";
outputNums(nums,n);
return true;
}
/*************************************插入排序*************************************/
//數組前部分爲有序後部分無序,無序數組第一位a與有序數組從後向前掃描比較,若數組元素大於a則將該元素
//移到下一個位置,直到找到小於等於a的數,將a插入它的後一位
bool insertSort::insert(int *nums,int n)
{
int a;
for(int i = 1;i < n;i++)
{
a = nums[i];
for(int j = i-1;j > -1;j--)
{
if(nums[j] > a)
{
nums[j+1] = nums[j];
if(j == 0) //當a爲有序數組中最小元素時
nums[j] = a;
}
else
{
nums[j+1] = a;
break;
}
}
}
//cout<<"after insertSort: ";
//outputNums(nums,n);
}
/*************************************基數排序*************************************/
//首先獲取數組中最大的位數max,然後進行max次排序從最右位開始排,創建count[10]數組記錄當前排序的位
//數上0-9每個數的個數,創建temp[n]數組,用於存放原數組中的數經過當前位數排序後的結果,結束一輪排序
bool radixSort::radix(int *nums,int n)
{
int max;
max = maxBit(nums,n);
int radix = 1;
int *count;
int *temp;
//count = new int[10]; //釋放 delete []count;
//temp = new int[n]; //釋放 delete []temp;
set_new_handler(noMemory);
count = (int *)malloc(sizeof(int)*10);
temp = (int *)malloc(sizeof(int)*n);
for(int i = 0;i < max;i++)
{
int j,k,re;
//爲count數組賦初始值
for(j = 0;j < 10;j++)
count[j] = 0;
//統計當前位數上0-9各個數的個數
for(k = 0;k < n;k++)
{
//radix=1取個位,radix=10取十位...
re = (nums[k]/radix)%10;
count[re]++;
}
//獲得0-9各個數所在temp數組的最後一個位置
//例:當前位數上有3個0,2個1,則count[1]=5,最後一個1在temp中下標爲5-1=4
for(j = 1;j < 10;j++)
count[j] = count[j-1] + count[j];
//獲得按當前位數排好序的數組
for(k = n-1;k > -1;k--)
{
re = (nums[k]/radix)%10;
temp[count[re]-1] = nums[k];
count[re]--;
}
for(k = 0;k < n;k++)
nums[k] = temp[k];
radix = radix*10;
}
free(count);
free(temp);
cout<<"after radixSort: ";
outputNums(nums,n);
return true;
}
/*************************************計數排序*************************************/
int countSort::maxNum(int *nums,int n)
{
int max = nums[0];
int pos = 0;
for(int i = 1;i < n;i++)
{
if(nums[pos] < nums[i])
{
pos = i;
}
}
max = nums[pos];
return max;
}
//當數組中的數比較密集且最大數
//計數排序爲基數排序的子程序(將radix作爲參數傳入?)
bool countSort::count(int *nums,int n)
{
int max,i,j;
max = maxNum(nums,n);
//count數組個數爲0,1,2...max = max+1
max = max+1;
int *count;
int *temp;
set_new_handler(noMemory);
count = (int *)malloc(sizeof(int)*max);
temp = (int *)malloc(sizeof(int)*n);
for(i = 0;i < max;i++)
count[i] = 0;
for(j = 0;j < n;j++)
{
count[nums[j]]++;
}
for(i = 1;i < max;i++)
count[i] = count[i-1] + count[i];
for(j = n-1;j > -1;j--)
{
temp[count[nums[j]]-1] = nums[j];
count[nums[j]]--;
}
for(j = 0;j < n;j++)
nums[j] = temp[j];
free(count);
free(temp);
cout<<"after countSort: ";
outputNums(nums,n);
return true;
}
/*************************************桶排序*************************************/
//首先求出數組中最大數的的位數(例如:238,位數爲3),進而求出取餘基數(此例中爲10的3-1次方:100)
//將數組每個數對基數(radix)取餘,結果爲0-9,分別放入對應的桶中(數組),再對桶中元素插入排序
//最後將0-9有序桶中的元素依次存入數組中即完成排序
bool bucketSort::bucket(int *nums,int n)
{
int i,j,maxbit,radix,re,pos=0;
int **buck;
int *temp;
set_new_handler(noMemory);
//buck = new int *[10];
buck = (int **)malloc(sizeof(int *)*10);
//for(i = 0;i < 10;i++)
// buck[i] = new int[n];
temp = new int[10];
for(i = 0;i < 10;i++)
{
temp[i] = 0;
}
maxbit = maxBit(nums,n);
radix = pow(10,maxbit-1);
//temp存放數組中取餘結果爲0-9的元素個數統計
for(j = 0;j < n;j++)
{
re = nums[j]/radix;
temp[re]++;
//pos = 0;
//while(buck[re][pos] != '\0')
// pos++;
}
for(i = 0;i < 10;i++)
{
//爲10個桶申請對應的空間存放數組元素
//buck[i] = new int[temp[i]];
set_new_handler(noMemory);
//要+1否則在釋放buck[i]時會報錯【free(): invalid next size (fast)】
//若+1則不必在釋放時進行NULL判斷,但是浪費空間
//buck[i] = (int *)malloc(sizeof(int)*temp[i]+1);
buck[i] = (int *)malloc(sizeof(int)*temp[i]);
buck[i][0] = temp[i];
}
for(j = n-1;j > -1;j--)
{
re = nums[j]/radix;
buck[re][temp[re]] = nums[j];
temp[re]--;
}
delete []temp;
for(i = 0;i < 10;i++)
{
if(buck[i][0] == 1)
{
nums[pos] = buck[i][1];
pos++;
}
else if(buck[i][0] > 1)
{
temp = new int[buck[i][0]];
for(j = 1;j < buck[i][0]+1;j++)
{
temp[j-1] = buck[i][j];
}
insert(temp,buck[i][0]); //對桶中元素插入排序
for(j = 0;j < buck[i][0];j++)
{
nums[pos] = temp[j];
pos++;
}
delete []temp;
}
//pos += buck[i][0];
}
cout<<"after bucketSort: ";
outputNums(nums,n);
//釋放空間
for(i = 0;i < 10;i++)
{ //若爲NULL仍然釋放,會報錯【free(): invalid next size (fast)】
if(buck[i] == NULL)
continue;
else
free(buck[i]); //delete []buck[i];
}
free(buck); //delete []buck;
return true;
}
/*************************************歸併排序*************************************/
//將數組nums中從begin到end分爲兩個子序列,將其中的元素有序地存放到temp中
//其中begin到mid爲第一個有序子序列,mid+1到end爲第二個有序子序列
bool mergeSort::mergeArray(int *nums,int *temp,int begin,int mid,int end)
{
int pos = begin;
int begin1 = begin;
int end1 = mid;
int begin2 = mid+1;
int end2 = end;
while(pos < end || pos == end)
{
//當第二個子序列長於第一個子序列時,將第二個子序列剩下的元素添加到合併序列中
if(begin1 > end1)
{
//這三行代碼等價於:temp[pos++] = nums[begin2++];
//先由原先的值進行賦值運算,後自增
temp[pos] = nums[begin2];
begin2++;
pos++;
}
//當第一個子序列長於第二個子序列時,將第一個子序列剩下的元素添加到合併序列中
else if(begin2 > end2)
{
temp[pos] = nums[begin1];
begin1++;
pos++;
}
else
{
if(nums[begin1] < nums[begin2] || nums[begin1] == nums[begin2])
{
temp[pos] = nums[begin1];
begin1++;
pos++;
}
else if(nums[begin1] > nums[begin2])
{
temp[pos] = nums[begin2];
begin2++;
pos++;
}
}
}
}
//當子序列元素個數爲segLen時,對每一組子序列進行二路歸併排序
bool mergeSort::subMerge(int *nums,int *temp,int segLen,int n)
{
int begin_index = 0;
while(begin_index < (n-2*segLen) || begin_index == (n-2*segLen))
{
mergeArray(nums,temp,begin_index,begin_index+segLen-1,begin_index+2*segLen-1);
begin_index += 2*segLen;
}
//當剩下的兩個子序列不構成完整的長度均爲segLen的子序列時,end == n-1(數組末端)
if(begin_index+segLen < n || begin_index+segLen == n)
{
mergeArray(nums,temp,begin_index,begin_index+segLen-1,n-1);
}
//最終合併到只剩少於segLen個元素不能構成兩個子序列時,將剩下的全部元素添加到合併序列中
else
{
while(begin_index < n ||begin_index == n)
{
temp[begin_index] = nums[begin_index];
begin_index++;
}
}
}
bool mergeSort::merge(int *nums,int n)
{
set_new_handler(noMemory);
int *temp = new int[n];
int segLen = 1; //segLen爲子序列中元素的個數1,2,4,8...
while(segLen < n || segLen == n)
{
subMerge(nums,temp,segLen,n); //初始時nums爲待排序數組,temp爲合併序列
segLen += segLen;
subMerge(temp,nums,segLen,n); //一次排序後temp爲待排序,nums爲合併序列
segLen += segLen;
}
cout<<"after mergeSort: ";
outputNums(nums,n);
}
/*************************************希爾排序*************************************/
//首先根據待排序數組元素個數獲得初始步長大小,然後相當於將數組分爲若干行,每行元素個數即爲步長
//然後依次對每列進行插入排序;結束後減小步長,重複上述步驟,直到步長爲1,即對整個相對有序的數列
//進行插入排序,完成排序
bool shellSort::shell(int *nums,int n)
{
int stepSize = 0;
int *temp;
//獲取初始的stepSize【關鍵!】
while(stepSize < n || stepSize == n)
{
stepSize = stepSize*3+1;
}
stepSize = (stepSize-1)/3;
while(stepSize > 0)
{
for(int i = 0;i < stepSize;i++)
{
int len = 0,j,k=0;
//獲取當前列的元素個數
for(j = i;j < n;j+=stepSize)
{
len++;
}
set_new_handler(noMemory);
temp = new int[len];
//將當前列的元素存放到臨時數組temp中再對其插入排序
for(j = i;j < n;j+=stepSize)
{
temp[k++] = nums[j];
}
insert(temp,len);
k = 0;
//將列元素按有序的次序存放回原數組中
for(j = i;j < n;j+=stepSize)
{
nums[j] = temp[k++];
}
delete []temp;
}
stepSize = (stepSize-1)/3;
}
cout<<"after shellSort: ";
outputNums(nums,n);
}
/*************************************輸出數組*************************************/
//輸出排序後的數組
bool basico::outputNums(int *nums,int n)
{
for(int k = 0;k < n;k++)
{
cout<<nums[k]<<" ";
}
cout<<endl;
return true;
}
main.cpp#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include"sorting.h"
using namespace std;
int main()
{
int *nums;
int n;
basico test;
n = test.inputNums(nums);
/* bubbleSort btest; //冒泡排序
btest.bubble(nums,n);
quickSort qtest; //快速排序
qtest.quick(nums,n);
heapSort htest; //堆排序
htest.heap(nums,n);
selectSort stest; //選擇排序
stest.select(nums,n);
insertSort itest; //插入排序
itest.insert(nums,n);
radixSort rtest; //基數排序
rtest.radix(nums,n);
countSort ctest; //計數排序
ctest.count(nums,n);
bucketSort bktest; //桶排序
bktest.bucket(nums,n);
mergeSort mtest; //歸併排序
mtest.merge(nums,n); */
shellSort shtest; //希爾排序
shtest.shell(nums,n); /**/
//釋放內存
free(nums);
return 0;
}
MakefileGCC := g++
all:
$(GCC) -c main.cpp -o main.o
$(GCC) -c sorting.cpp -o sorting.o
$(GCC) -o main main.o sorting.o
clean:
rm *.o main *~ -rf