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;
}
Makefile
GCC := 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