一、各种排序方法的时间、空间复杂度情况
1、 直接插入排序:比较次数 最少n-1次;最多(n-1)(n+2)/2 ;移动次数 最少0; 最多(n-1)(n+4)/2 使用一个辅助存储空间,是稳定的排序;
2 、 折半插入排序:比较次数 最少与最多同,都是n*log2n(其中2为底,下边表示同), 移动次数 最少0,最多时间复杂度为O(n2);(n的平方,以下也如此表示); 使用一个辅助
存储空间,是稳定的排序;
3 、冒泡排序: 比较最少为:n-1次,最多时间复杂度表示为o(n2); 移动次数最少为0,最多时间复杂度表示为O(n2); 使用一个辅存空间,是稳定的排序;
4 、简单选择排序: 比较次数没有多少之分,均是n(n-1)/2; 移动次数最少为0,最多为3(n-1); 使用一个辅存空间,是稳定的排序;
5 、快速排序:比较和移动次数最少时间复杂度表示为O(n*log2n); 比较和移动次数最多的时间复杂度表示为O(n2); 使用的辅助存储空间最少为log2n,最多为n的平方;是不稳定
的排序;
6 、堆排序: 比较和移动次数没有好坏之分,都是O(n*log2n); 使用一个辅存空间,是不稳定的排序;
7、 2-路归并排序:比较和移动次数没有好坏之分,都是O(n*log2n); 需要n个辅助存储空间,是稳定的排序;
二、各种排序的c/c++实现
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <ctime>
using namespace std;
const int SIZE = 100000;
//打印数组元素
void print(const int * const t);
//交换两元素
void swap(int *p,int *q);
/*
插入排序
基本思想:
1、从第一个元素开始,该元素可以认为已经被排序
2、取出下一个元素,在已经排序的元素序列中从后向前扫描
3、如果该元素(已排序)大于新元素,将该元素移到下一位置
4、重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5、将新元素插入到该位置中
6、重复步骤2~5
*/
void InsertSort(int* const t);
/*
折半插入排序
基本思想: 利用前i个元素为已经排序的特点;进行折半查找插入位置
1、取序列的t[midd]中间位置元素与目标元素t[i]比较,如果
t[i]>t[midd],则在[midd+1,high]查找,否则在[low,midd-1]
2、重复1,直至找到该位置
3、将high到i的元素右移
4、将t[i]元素插入high+1位置
5、重复1-4,直到i=size-1
*/
void BInsertSort(int* const t);
/*
希尔排序
基本思想:
1、先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。
所有距离为dl的倍数的记录放在同一个组中
2、先在各组内进行直接插入排序;
3、然后,取第二个增量d2<d1重复上述的分组和排序,
4、重复1-3,直至所取的增量dt=1(dt<dt-l<…<d2<d1)
5、最后的进行dt=1的插入排序,结果即为顺序的
*/
void ShellSort(int * const t,int dk=1);
//一次希尔插入排序
void ShellInsert(int * const t,int dk=1);
/*
冒泡排序
基本思想:对相邻的两个数进行比较交换,将比较大的那个数右移。
1、第一轮将t[0]、t[1]进行比较,若t[i]>t[i+1],则交换,
直到第n-1个记录和第n个记录比较过为止
2、重复1,直至第n-i-1个记录和第n-i个记录比较过为止
*/
void BuddleSort(int * const t);
/*
快速排序
基本思想:
1、分治法的基本思想:
将原问题分解为若干个规模更小但结构与原问题相似的子问题。
递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
2、快速排序的基本思想
设当前待排序的无序区为R[low..high],利用分治法可将快速排序的基本思想描述为:
①分解:
在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右
两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有
记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,
右边的子区间中所有记录的关键字均大于等于pivot.key,
而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。
注意:
划分的关键是要求出基准记录所在的位置pivotpos。
划分的结果可以简单地表示为(注意pivot=R[pivotpos]):
R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys
其中low≤pivotpos≤high。
②求解:
通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。
③组合:
因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。
对快速排序而言,"组合"步骤无须做什么,可看作是空操作。
*/
void QuickSort(int *const t,int low,int high);
/*
选择排序
基本思想:
每一趟在n-i+1 (i=0,1,2,...,n-1)个记录中选取最小的记录作为有序序列中的第i个记录
*/
void SelectSort(int *const t);
/*
堆排序
基本思想:
堆的定义:满足如下约束的 n 个关键字序列 Kl,K2,…,Kn 称为堆,1 ≤ i ≤ n/2,
(1) ki ≤ K2i 且 ki ≤ K2i+1 (小根堆) 或
(2) Ki ≥ K2i 且 ki ≥ K2i+1 (大根堆)
*/
void HeapSort(int *const t);
//将序列建为一个堆
void HeapAdjust(int *const t,int s,int m);
/*
归并排序
基本思想:
假设初始序列为n个记录,则可将序列看成n个有序的序列,每个序列的长度为1,
然后进行两两归并,得到n/2个长度为2或者1的序列;再两两归并。。。,直到
得到一个长度为n的序列为止
*/
void MergeSort(int * const sr,int left,int right);
//将有序的sr[i..m]和sr[m+1..n]归并为有序的tr[i..n]
void Merge(int * const sr,int left,int midd,int right);
int main()
{
int a[SIZE];
srand((unsigned) time(0));
for(int i=1;i<SIZE;i++){
a[i] = rand()%SIZE;
}
a[0] = SIZE-1;
/*
cout<<"排序前,SIZE:"<<SIZE<<endl;
print(a);
InsertSort(a);
cout<<endl<<"选择排序后:";
cout<<"比较次数:"<<count1<<endl;
print(a);
BInsertSort(a);
cout<<endl<<"则半插入排序后:";
cout<<"比较次数:"<<count2<<endl;
print(a);
ShellSort(a);
cout<<endl<<"希尔排序排序后:";
print(a);
cout<<"比较次数:"<<count3<<endl;
BuddleSort(a);
cout<<endl<<"冒泡排序后:"<<endl;
QuickSort(a,0,SIZE-1);
cout<<endl<<"快速排序后:"<<endl;
print(a);
cout<<"比较次数:"<<count5<<endl;
*/
clock_t start,end;
start = clock();
//QuickSort(a,0,SIZE-1);
//BuddleSort(a);
// ShellSort(a);
//InsertSort(a);
//BInsertSort(a);
// SelectSort(a);
//HeapSort(a);
MergeSort(a,0,SIZE-1);
end = clock();
//cout<<endl<<"快速排序后:"<<endl;
cout<<"程序运行时间:"<<end-start<<endl;
//print(a);
system("pause");
return 0;
}
//插入排序
void InsertSort(int * const t){
//要插入的元素
int temp = 0;
for(int i=1;i<SIZE;i++)
{
temp = t[i];
int j=i-1;
//寻址插入位置,同时将元素后移
for(;j>=0&&temp<t[j];j--)
{
t[j+1]=t[j];
}
//插入
t[j+1]=temp;
}
}
//则半插入排序
void BInsertSort(int* const t)
{
int temp = 0;//要插入的元素
int low = 0;//查找范围的开端
int high = 0; //末端
int midd = 0;//中部
for(int i=1;i<SIZE;i++)
{
high = i-1;
temp = t[i];
while(low<=high)
{
midd = (high+low)/2;
if(t[i]<=t[midd])
{
high = midd-1;
}
else{
low = midd+1;
}
}
for(int j=i-1;j>high+1;j--)
{
t[j+1] = t[j];
}
t[high+1] = temp;
}
}
//希尔排序,也称增量递减排序
void ShellSort(int * const t,int dk)
{
//进行不同增量的的希尔插入排序
for(dk=SIZE/2;dk>2;dk/=2)
{
ShellInsert(t,dk);
}
//最后进行增量为1的排序
ShellInsert(t,1);
}
//一趟希尔插入排序
void ShellInsert(int * const t,int dk)
{
int temp = 0;//目标值
for(int i = dk;i<SIZE;i++)
{
temp = t[i];
int j = i-dk;
//找到要插入的位置,并将序列右移
for(;j>=0&&temp<t[j];j-=dk)
{
t[j+dk]=t[j];
}
//插入
t[j+dk] = temp;
}
}
//冒泡排序
void BuddleSort(int * const t)
{
for(int i=0;i<SIZE;i++)
{
//设置一个标志,当这一轮没有元素交互时,代表序列已经排序好,不用继续比较交换了
bool flag = false;
for(int j=0;j<SIZE-i-1;j++)
{
if(t[j]>t[j+1])
{
//将两个元素进行交换
swap(&t[j],&t[j+1]);
flag = true;
}
}
//已经排好序
if(!flag){
break;
}
}
}
//快速排序
void QuickSort(int *const t,int low,int high)
{
int i=low;
int j=high;
int pivotkey=t[low];//将第一个值作为枢纽
if(i<j)
{
while(i<j)
{
//查找高端比枢纽小的值位置j
while(i<j&&t[j]>=pivotkey)
{
--j;
}
//将小于枢纽的值与低端交换
swap(&t[i],&t[j]);
//查找低端比枢纽大的值位置i
while(i<j&&t[i]<=pivotkey)
{
++i;
}
//将小于枢纽的值与低端交换
swap(&t[i],&t[j]);
}
//对低子表进行递归排序
QuickSort(t,low,i-1);
//对高子表进行递归排序
QuickSort(t,i+1,high);
}
}
//简单选择排序
void SelectSort(int *const t)
{
//最小值索引
int minIndex = 0;
for(int i=0;i<SIZE;i++)
{
//将目标值索引设置为最小
minIndex = i;
//查找剩下序列的最小值索引
for(int j=i;j<SIZE;j++)
{
if(t[j]<t[minIndex])
{
//找到比原来最小值小的,则重新改变最小值索引
minIndex = j;
}
}
//交换两个记录
swap(&t[i],&t[minIndex]);
}
}
//堆排序
void HeapSort(int *const t)
{
int LEN = t[0];
//从最后一个不是叶子节点的节点开始构造
for(int i=LEN/2;i>0;i--)
{
HeapAdjust(t,i,LEN);
}
for(int j=LEN;j>1;j--)
{
//将根节点与最后一个节点交换
swap(&t[1],&t[j]);
//重新调整
HeapAdjust(t,1,j-1);
}
}
//将序列建为一个堆
void HeapAdjust(int *const t,int s,int m)
{
//子树根节点值
int temp = t[s];
//将子树进行调整
for(int j=2*s;j<m;j*=2)
{
if(j<m&&t[j]<t[j+1])
{
j++;
}
//子树已经排序,则跳出循环
if(t[j]<temp)
{
break;
}
//交换子节点与根节点的值
swap(&t[s],&t[j]);
//将该子节点作为根节点,再次进行调整
s=j;
}
}
//将sr[left..right]归并排序为tr[left..right]
void MergeSort(int * const sr,int left,int right)
{
int midd = 0;
if(left<right)
{
//将序列从中点分割为两个子序列
midd = (left+right)/2;
//归并排序左序列
MergeSort(sr,left,midd);
//归并排序右序列
MergeSort(sr,midd+1,right);
//将两个序列合并
Merge(sr,left,midd,right);
}
}
//将有序的sr[i..m]和sr[m+1..n]归并为有序的tr[i..n]
void Merge(int * const sr,int left,int midd,int right)
{
int len = right-left+1;
int *tr = new int[len];
int k = 0;
int j = midd+1;
int i = left;
while(i<=midd&&j<=right)
{
tr[k++]=(sr[j]>sr[i])?sr[i++]:sr[j++];
}
//将剩余的sr[i..m]复制到tr
while(i<=midd)
{
tr[k++]=sr[i++];
}
//将剩余的sr[j..m]复制到tr
while(j<=right)
{
tr[k++]=sr[j++];
}
//将合并好的序列值拷贝回原序列中
for(int p=0,i=left;p<len;p++,i++)
{
sr[i] = tr[p];
}
delete[] tr;
}
//打印
void print(const int* const t)
{
int i=0;
while(i<SIZE)
{
cout<<" "<<t[i++];
if(i%15==0)
{
cout<<endl;
}
}
cout<<endl;
}
//交换两元素
void swap(int *p,int *q)
{
int tem = 0;
tem = *p;
*p = *q;
*q = tem;
}