算法导论_第八章_线性时间排序
排序算法的下界
决策树模型
决策树是一颗完全二叉树
决策树的每个节点代表需要比较的两个数,叶节点为一个数的排序。所以任何正确的排
序,n个元
素的n!个排列情况都应该出现在叶节点上。
比较排序的最坏情况出现在比较次数为决策树的高度,而决策数的高度
h<=lg(n!)=Ω(n*lg(n))
堆排序和归并排序都是渐进最优的比较排序算法
其运行时间上界为O(n*lg(n))
计数排序:
基本思想:
对于每一个输入的元素x,确定小于x的元素个数。利用这一信息可以直接把x放到它在输出
数组中的位置上。
下面来看代码:
/*************************************************************************
> File Name: counting_sort.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年06月29日 星期三 08时42分35秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
void counting_sort(int a[],int b[],int n,int k);
int main(int argc,char *argv[])
{
int a[100],b[100];
int n;
int max=-INF;
printf("please input the number of array:");
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(max<a[i]) max=a[i];//确定输入元素的最大值,便于开辟c数组
}
counting_sort(a,b,n,max);
for(int i=1;i<=n;i++)
printf("%d ",b[i]);
return 0;
}
//a数组表示要排序的数组,
//b表示排序过后的数组,
//c表示a数组元素在b数组中的位置。
void counting_sort(int a[],int b[],int n,int k)
{
int c[1000];
for(int i=0;i<=k;i++)
c[i]=0;
for(int i=1;i<=n;i++)
c[a[i]]++;//初始化为1代表有1个元素与其相等,为其本身
/*******important**************/
for(int i=1;i<=k;i++)//确定比其小或等于的元素的个数
c[i]+=c[i-1];
for(int j=n;j>=1;j--)
{
b[c[a[j]]]=a[j];//确定位置
c[a[j]]--;//若有重复,则位置减1
//在这里,其保证了如果多个元素相同,其在b数组中的顺序与其
//在a数组中的顺序相同,就保证了其稳定性。
}
/*****************************/
}
据计算其时间复杂度为Θ(n)
计数排序不涉及到比较,所以其脱离比较模型,时间复杂度可以降低很多,再者其具有
稳定性,对于附带卫星数据来说,比较重要。
不过当k非常大时,可能导致内存空间利用过多。
基数排序:
排序思想:
假定要对一个有10位的十进制的数排序,那么就从最低有效位开始进行排序,就是先对个位数进行排序,
然后十位,百位……直到排序完成,在排序过程中卫星数据顺序不变,所以需要稳定的排序算法。
这里可以采用基数排序
下面上具体代码:
/*************************************************************************
> File Name: radix_sort.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年06月29日 星期三 09时46分44秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef struct node
{
int ge;
int shi;
int bai;
}NODE;
void counting_sort_ge(NODE a[],NODE b[],int n,int k);
void counting_sort_shi(NODE a[],NODE b[],int n,int k);
void counting_sort_bai(NODE a[],NODE b[],int n,int k);
int main(int argc,char *argv[])
{
NODE a[100];
NODE b[100];
a[1].ge=1;
a[1].shi=2;
a[1].bai=3;
a[2].ge=2;
a[2].shi=2;
a[2].bai=2;
a[3].ge=7;
a[3].shi=2;
a[3].bai=3;
a[4].ge=3;
a[4].shi=1;
a[4].bai=2;
a[5].ge=5;
a[5].shi=2;
a[5].bai=3;
int n=5,d=3;
int max_ge=7;
int max_shi=2;
int max_bai=3;
counting_sort_ge(a,b,n,max_ge);
counting_sort_shi(b,a,n,max_shi);
counting_sort_bai(a,b,n,max_bai);
for (int i = 1; i <=5; ++i)
{
printf("%d%d%d\n",b[i].bai,b[i].shi,b[i].ge);
}
return 0;
}
void counting_sort_ge(NODE a[],NODE b[],int n,int k)
{
int c[1000];
for(int i=0;i<=k;i++)
c[i]=0;
for(int i=1;i<=n;i++)
c[a[i].ge]++;//初始化为1代表有1个元素与其相等,为其本身
/*******important**************/
for(int i=1;i<=k;i++)//确定比其小或等于的元素的个数
c[i]+=c[i-1];
for(int j=n;j>=1;j--)
{
b[c[a[j].ge]]=a[j];//确定位置
c[a[j].ge]--;//若有重复,则位置减1
//在这里,其保证了如果多个元素相同,其在b数组中的顺序与其
//在a数组中的顺序相同,就保证了其稳定性。
}
/*****************************/
}
void counting_sort_shi(NODE a[],NODE b[],int n,int k)
{
int c[1000];
for(int i=0;i<=k;i++)
c[i]=0;
for(int i=1;i<=n;i++)
c[a[i].shi]++;//初始化为1代表有1个元素与其相等,为其本身
/*******important**************/
for(int i=1;i<=k;i++)//确定比其小或等于的元素的个数
c[i]+=c[i-1];
for(int j=n;j>=1;j--)
{
b[c[a[j].shi]]=a[j];//确定位置
c[a[j].shi]--;//若有重复,则位置减1
//在这里,其保证了如果多个元素相同,其在b数组中的顺序与其
//在a数组中的顺序相同,就保证了其稳定性。
}
/*****************************/
}
void counting_sort_bai(NODE a[],NODE b[],int n,int k)
{
int c[1000];
for(int i=0;i<=k;i++)
c[i]=0;
for(int i=1;i<=n;i++)
c[a[i].bai]++;//初始化为1代表有1个元素与其相等,为其本身
/*******important**************/
for(int i=1;i<=k;i++)//确定比其小或等于的元素的个数
c[i]+=c[i-1];
for(int j=n;j>=1;j--)
{
b[c[a[j].bai]]=a[j];//确定位置
c[a[j].bai]--;//若有重复,则位置减1
//在这里,其保证了如果多个元素相同,其在b数组中的顺序与其
//在a数组中的顺序相同,就保证了其稳定性。
}
/*****************************/
}
其利用了计数排序,对n个d位数来说
其时间复杂度为:Θ(d*(n+k))
d为常数时且k=O(n),基数排序具有线性的时间代价
由于其不是原址排序,所以其会耗费大量内存空间,当内存非常珍贵的情况下,利用快排
桶排序:
桶排序假设输入的数据服从均匀分布,平均情况下,其时间代价为O(n)
桶排序将[0,1)区间划分为n个相同大小的子区间上,或称为桶,然后将n个输入数分别放到各个桶中,先对
桶中的数进行排序,然后遍历每个桶,按照次序把各个桶中的元素列出来。
下面看具体代码:
/*************************************************************************
> File Name: bucket_sort.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年06月29日 星期三 10时50分26秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
void bucket_sort(float a[],float b[][100],int num[],int n,float max);
int main(int argc,char *argv[])
{
float a[100];//要排序的数组
float b[100][100];//桶
int num[100];//桶内的元素
int n;
float max=-INF;
memset(num,0,sizeof(num));//赋值清零
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%f",&a[i]);
if(a[i]>max) max=a[i];//找到最大值
}
bucket_sort(a,b,num,n,max);//桶排序
for(int i=1;i<=n;i++)
{
for(int j=1;j<=num[i];j++)
printf("%g ",b[i][j]);
}
return 0;
}
void bucket_sort(float a[],float b[][100],int num[],int n,float max)
{
int i,j,k;
for(i=1;i<=n;i++)
{
j=(int)(a[i]/max*n);//找到其应该放入桶的下标
if(!j) j++; //如果为0,则变为1
num[j]++; //计数器加1
/*************插入排序_部分功能********************/
for(k=num[j];k>1&&b[j][k-1]>a[i];k--)
b[j][k]=b[j][k-1];
b[j][k]=a[i];
}
}
桶排序效率分析:
桶排序的时间代价为
T(n)=Θ(n)+sum(O(n[i]^2))
最后求得其时间复杂度为:Θ(n)
即使输入数据不服从平均分布桶排序也仍然可以在线性时间内完成。只要输入数据满足所有桶的大小的平
方和与总的元素呈线性关系。