8.1排序算法的下界
8.1-1 在一颗比较排序算法的决策树中,一个叶结点可能的最小深度是多少?
最少进行n-1次比较,所以深度最小是n-1
8.1-2不用斯特林近似公式,给出lg(n!)的渐近紧确界,利用A.2节介绍的技术来求累加和∑lgk.
∫(lgk)dk=klgk-∫kd(lgk)=klgk-(1/ln2)k 所以∑lgk=(nlgn-1lg1)-(1/ln2)(n-1)=nlgn-(1/ln2)(n-1)-cnlgn=(1-c)nlgn-(1/ln2)(n-1)
如果1-c>0,那么对于足够大的n来说nlgn增长速度比(n-1)快,所以(1-c)nlgn-(1/ln2)(n-1)≥0 所以lg(n!)=Ω(nlgn)
如果1-c<0,对于足够大的n显然有(1-c)nlgn-(1/ln2)(n-1)≤0。所以lg(n!)=Ο(nlgn) 所以lg(n!)=Θ(nlgn)
8.1-3证明:对于n!种长度为n的输入中至少一半,不存在能达到线性运行时间的比较排序算法。
如果只要求对1/n的输入达到线性时间呢?1/2^n呢?
假设x种输入达到h1=Θ(n).x<2^h1=>h1>lgx. 如果存在x>n!/2输入达到线性时间。那么就应该有 h1>lgx>lg(n!/2)>Ω(nlgn)与假设矛盾。
假设x种输入达到h1=Θ(n).x<2^h1=>h1>lgx.如果存在x>n!/n输入达到线性时间。那么就应该有 h1>lgx>lg(n!/n)>Ω(nlgn)与假设矛盾。
同理1/2^n的输入也与假设矛盾。所以以上这三种输入都不行。
8.1-4 假设现有一个包含n个元素的待排序序列。该序列由n/k个子序列组成,每个子序列包含k个元素一个给定子序列中的每个元素都小于其后继子序列中的所有元素,且大于其前驱子序列中的每个元素。因此,对于这个长度为n的序列的排序转化为对n/k个子序列中的k个元素的排序。试证明,这个排序问题中所需比较次数的下界Ω(nlgk).
因为每个子序列有k!种排列方式,那么n/k个子序列就有(k!)^(n/k)种排列方式,所以(k!)^(n/k)≤2^h h≥(n/k)lgk! 因为由(公式3.19)lgk!=Θ(klgk) lgk!≥klgk 所以h≥(n/k)klgk=nlgk.得证。
8.2计数排序
计数排序代码:
//计数排序
//附带8.2-4代码
/*#include <iostream>
using namespace std;
const n=8;
void COUNTING_SORT(int A[n],int B[n],int k)
{
int *C=new int[k+1];
for (int i=0;i<=k;i++)
{
C[i]=0;
}
for (int j=0;j<n;j++)
{
C[A[j]]=C[A[j]]+1;
}
for (i=0;i<=k;i++)
{
C[i+1]=C[i+1]+C[i];
}
for (j=n-1;j>=0;j--)
{
B[C[A[j]]-1]=A[j];
C[A[j]]=C[A[j]]-1;
}
}
int counting_SORT(int A[n],int a,int b,int k)
{
int *C=new int[k+1];
for (int i=0;i<=k;i++)
{
C[i]=0;
}
for (int j=0;j<n;j++)
{
C[A[j]]=C[A[j]]+1;
}
for (i=0;i<=k;i++)
{
C[i+1]=C[i+1]+C[i];
}
int x=C[b]-C[a-1];
return x;
}
void main()
{
//int A[n]={6,0,2,0,1,3,4,6,1,3},B[n]={0},k=0;
int A[n]={2,5,3,0,2,3,0,3},B[n]={0},k=0;
for (int i=0;i<n;i++)
{
if (A[i]>k)
{
k=A[i];
}
}
COUNTING_SORT(A,B,k);
for (int j=0;j<n;j++)
{
cout<<B[j]<<" ";
}
int a=0,b=0;
while (1)
{
cout<<"请输入需要查找的区间"<<endl;
cin>>a;
cin>>b;
if (a<B[0]||b>B[n-1])
{
cout<<"输入错误"<<endl;
}
else
{
break;
}
}
cout<<"落在["<<a<<","<<b<<"]区间内的个数"<<counting_SORT(A,a,b,k)<<endl;
}
8.2-1 参照图8-2的方法,说明COUNTING-SORT 在数组 A={6,0,2,0,1,3,4,6,1,3,2}上的操作过程。
(1)C[1..k]被初始化为0。