冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、桶排序的实现
在待排序的记录序列中,可能存在两个或两个以上关键字相等的记录,若在排序后这些相等的关键字的前后顺序仍与之前的顺序保持不变,则称该排序方法是稳定的;反之,若排序后相等关键字的前后顺序可能发生变化,则称所用的排序方法是不稳定的。
冒泡排序
思路:从第一个元素开始,对数组中两两相邻的元素比较,将值较小的元素放在前面,值较大的元素放在后面,一轮比较完毕,一个最大的数沉底成为数组中的最后一个元素,一些较小的数如同气泡一样上浮一个位置。n个数,经过n-1轮比较后完成排序。
//冒泡排序
//平均时间复杂度为O(n²),最坏时间复杂度为O(n²),最好时间复杂度为O(n)
//空间复杂度为O(1)
//稳定排序
void bubbleSort( int *iarr, int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
int tmp;
bool flags;
for( int i = 1; i < length; i++ ){
flags = true;
for( int j = 0; j < length - i; j++ ){
if( iarr[j] > iarr[j+1] ){
flags = false;
tmp = iarr[j];
iarr[j] = iarr[j+1];
iarr[j+1] = tmp;
}
}
if( flags )
break;
}
}
选择排序
思路:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
//选择排序
//平均时间复杂度为O(n²),最坏时间复杂度为O(n²),最好时间复杂度为O(n²)
//空间复杂度为O(1)
//不稳定排序
void selectSort( int *iarr , int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
int tmp;
int min_index;
for( int i = 0; i < length; i++ ){
min_index = i;
for( int j = i+1; j < length; j++ ){
if( iarr[min_index] > iarr[j] ){
min_index = j;
}
}
if( min_index != i ){
tmp = iarr[i];
iarr[i] = iarr[min_index];
iarr[min_index] = tmp;
}
}
}
插入排序
思路:将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录增1的有序表。
//插入排序
//平均时间复杂度为O(n²),最坏时间复杂度为O(n²),最好时间复杂度为O(n)
//空间复杂度为O(1)
//稳定排序
void insertSort( int *iarr , int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
int tmp;
int i, j;
for( i = 1; i < length; i++ ){
tmp =iarr[i];
j = i-1;
while( j >= 0 && iarr[j] > tmp ){
iarr[j+1] = iarr[j];
j--;
}
iarr[j+1] = tmp;
}
}
希尔排序
思路:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。即先取一个小于 n 的整数 d1 作为第一个增量,把文件的全部记录分成 d1 个组。所有距离为 d1 的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量 d2<d1 重复上述的分组和排序,直至所取的增量 dt = 1 (dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。【若待排记录序列为正序,其时间复杂度可提高到O(n),所以希尔排序的时间复杂度为O(nlogn)。
void shellInsert( int *iarr , int length , int step ){
int tmp;
int i, j;
for( i = step; i < length; i++ ){
tmp = iarr[i];
j = i-step;
while( j >= 0 && iarr[j] > tmp ){
iarr[j+step] = iarr[j];
j -= step;
}
iarr[j+step] = tmp;
}
}
//希尔排序
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(nlogn),最好时间复杂度为O(nlogn)
//空间复杂度为O(1)
//稳定排序
void shellSort( int *iarr, int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
int step = length/2 + 1;
for( ; step > 2; step = step/2 + 1 ){
shellInsert( iarr, length, step );
}
shellInsert( iarr, length, 1 );
}
快速排序
思路:通过一躺排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一不部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
void qSort( int *iarr , int begin , int end ){
if( begin > end )
return;
int i = begin, j = end;
int pivot = iarr[i]; //枢轴
while( i < j ){
while( i < j && pivot <= iarr[j] ){
j--;
}
if( i < j ){
iarr[i] = iarr[j];
i++;
}
while( i < j && pivot >= iarr[i] ){
i++;
}
if( i < j ){
iarr[j] = iarr[i];
j--;
}
}
iarr[i] = pivot;
qSort( iarr, begin, i-1 );
qSort( iarr, i+1, end );
}
//快速排序
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(n²),最好时间复杂度为O(nlogn)
//空间复杂度为O(logn)
//不稳定排序
void quickSort( int *iarr , int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
qSort( iarr, 0, length-1 );
}
归并排序
思路:将两个或两个以上的有序表组合成一个新的有序表。
void merge( int *iarrA , int *iarrB , int begin , int mid , int end ){
int i = begin, j = mid+1, k = begin;
while( i <= mid && j <= end ){
if( iarrA[i] < iarrA[j] ){
iarrB[k++] = iarrA[i++];
}else{
iarrB[k++] = iarrA[j++];
}
}
while( i <= mid ){
iarrB[k++] = iarrA[i++];
}
while( j <= end ){
iarrB[k++] = iarrA[j++];
}
for( i = begin; i <= end; i++ ){
iarrA[i] = iarrB[i];
}
print( iarrA , end+1 );
print( iarrB , end+1 );
}
void mSort( int *iarrA, int *iarrB, int begin, int end ){
if( begin == end ){
iarrB[begin] = iarrA[begin];
}else{
int mid = ( begin + end )/2;
mSort( iarrA, iarrB, begin, mid );
mSort( iarrA, iarrB, mid+1, end );
merge( iarrB, iarrA, begin, mid, end );
}
}
//归并排序
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(nlogn),最好时间复杂度为O(nlogn)
//空间复杂度为O(n)
//稳定排序
void mergeSort( int *iarr, int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
int *iarrB = new int[length];
mSort( iarr, iarrB, 0, length-1 );
delete [] iarrB; //申请之后一定要释放
}
堆排序
思路:将数组 A 创建为一个最大堆,然后交换堆的根(最大元素)和最后一个叶节点 x,将x从堆中去掉形成新的堆 A1,调整 A1堆。重复以上动作,直到堆中只有一个节点。
void heapAdjust( int *iarr , int s , int m ){
int i = 2*s +1, tmp = iarr[s];
while( i <= m ){
if( i+1 <= m && iarr[i] < iarr[i+1] ){
i++;
}
if( tmp > iarr[i] ){
break;
}else{
iarr[s] = iarr[i];
s = i;
i = 2*s +1;
}
}
iarr[s] = tmp;
}
//堆排序
//平均时间复杂度为O(nlogn),最坏时间复杂度为O(nlogn),最好时间复杂度为O(nlogn)
//空间复杂度为O(n);也可以做到O(1),将调整堆程序写入堆排序中,可以节省空间。
//不稳定排序
void heapSort( int *iarr , int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
int i, tmp;
//调整堆,时间复杂度为O(n)
for( i = length/2 -1; i >=0; i-- ){
heapAdjust( iarr, i, length-1 );
}
for( i = length-1; i >= 0 ; i-- ){
tmp = iarr[0];
iarr[0] = iarr[i];
iarr[i] = tmp;
heapAdjust( iarr , 0 , i-1 );
}
}
桶排序
思路:假设有一组长度为N的待排关键字序列 K[1....n]。首先将这个序列划分成 M 个的子区间(桶) 。然后基于某种映射函数 ,将待排序列的关键字 key 映射到第 i 个桶中,那么该关键字 key 就作为第 i 个桶的元素。接着对每个桶中的所有元素进行比较排序(可以使用快排、插排等)。然后依次从桶中倒出所有元素。
int divide10( int &num ){
return num/10;
}
//桶排序
//平均时间复杂度为O(n),最坏时间复杂度为O(nlogn),最好时间复杂度为O(n)
//空间复杂度为O(n)
//稳定性取决于桶内的排序算法
//下面的实现主要针对 0~99 之间的数字的,入桶用hash,除以10取商,桶内快排,
void bucketSort( int *iarr , int length ){
//非法情况:iarr为空或者数组长度小于1
if( !iarr || length < 1 )
return;
vector< vector<int> > ivec;
int i, j, k, bucket_num = 10; // 桶的数量设置为10
for( i = 0; i < bucket_num; i++ ){
vector<int> vec;
ivec.push_back( vec );
}
//对每个数字做除以10的处理后入桶
for( i = 0; i < length; i++ ){
ivec[ divide10(iarr[i]) ].push_back( iarr[i] );
}
//对每个桶做快速排序(或插入排序)
for( i = 0; i < bucket_num; i++ ){
if( ivec[i].size() )
quickSort( &ivec[i][0], ivec[i].size() );
}
for( i = 0, j = 0; i < bucket_num; i++ ){
for( k = 0; k < ivec[i].size(); k++ ){
iarr[j++] = ivec[i][k];
}
}
}