CLRS第八章思考题

来源:互联网 发布:sql update 条件更新 编辑:程序博客网 时间:2024/06/13 23:32

思考题8-1

a) n 个不同元素一共有 n! 种输入,每种输入的概率 1n!,它们都对应一个可达的叶结点。

b) 当 k>1 时说明根结点 T 不是叶结点,意味着 T 的叶结点也是 LTRT 的叶结点。若 LTRT 的深度为 h,则 LTRT 的叶结点的深度为 h+1D(T) 必然是 D(LT)D(RT) 以及全部叶结点 k 的总和。
证明:设 dT(x) 是树 T 的结点 x 的深度。

D(T)=xleaves(T)dT(x)=xleaves(LT)dT(x)+xleaves(RT)dT(x)=xleaves(LT)(dLT(x)+1)+xleaves(RT)(dRT(x)+1)=xleaves(LT)dLT(x)+xleaves(RT)dRT(x)+xleaves(T)1=D(RT)+D(LT)+k


c) 要证明结论,只需分别证明 d(k)min1xk1{d(i)+d(ki)+k}d(k)min1xk1{d(i)+d(ki)+k} 即可。

证明 d(k)min1xk1{d(i)+d(ki)+k},只要证明对 i=1,2k1d(k)d(i)+d(ki)+k 。我们拿走 Tk 个叶子结点,则 D(T)=d(k),此时剩下 LTRT 。他们各自构成一棵树,设 RT 的叶结点为 iLT 的叶结点为 ki 有:

d(k)=D(T)=D(LT)+D(RT)+kd(i)+d(ki)+k(dD(T))

证明 d(k)min1xk1{d(i)+d(ki)+k},只要证明对 i=1,2k1d(k)d(i)+d(ki)+k 。对任意 i 属于 1k 可以找到有 i 个叶结点的 RTki 个叶结点的 LT,且D(RT)=d(i)D(LT)=d(ki)。分别构建树 T,LT,RT有:
d(k)D(T)=D(LT)+D(RT)+k=d(i)+d(ki)+k

因此得证。

d)

f(i)f(i)f(i)=0=ilgi+(ki)lg(ki)=lgi+1lg(ki)1=lgikilgiki=0i/(ki)=1i=k2

因而在 i=k2 得到最小值。用归纳法证明 d(k)=Ω(klgk)
d(1)0=c1lg1对任意 c 成立 。设对 i 时成立。
d(k)=min1xk1{d(i)+d(ki)+k}min1xk1{c(ilgi+(ki)lg(ki))+k}=min1xk1{cfk(i)+k}=c(k2lgk2(kk2)lg(kk2))+k=cklgk2+k=cklgk+(kck)cklgkc1

得证。

e) TAn! 个叶结点,所以 D(n)>d(k)=Ω(n!lg(n!))。每种排序的概率为 1/n!,因此期望运行时间:

Ω(n!lg(n!))n!=Ω(lg(n!))=Ω(nlgn)

f) 为了构建和 B 相对应确定算法 A,我们只需选一个孩子结点来替换那个随机的结点。新的子树以及这颗子树可选择的数将小于等于随机算法对应的子树,对任意子树有 Ω(nlgn) 选择方式。也就意味着 BΩ(nlgn)

思考题8-2

a) 计数排序。

b) 快排中的PARTITION即可实现。

c) 冒泡排序、插入排序都可。

d) 基数排序需要使用稳定的排序方法,因此必须满足条件2。所以只有计数排序可以。

e) 给一个简单的方法。我们先统计每个记录出现的次数,然后直接放入数组中,举例 1,3,2,3,1,1,2,COUNT 数组中分别是 COUNT[1]=3COUNT[2]=2COUNT[3]=2。然后数组 A[02]=1A[34]=2A[56]=3
显然这个是不稳定的。

#include <iostream>#include <cstring>using std::cout;using std::endl;void COUNT_SORT(int *array,int length,int max_int_k){    int *C = new int[max_int_k+1];    memset(C,0,sizeof(int)*(max_int_k+1));    for(int i = 0; i < length; ++i)        ++C[array[i]];    int cnt = 0;    for(int i = 1; i <= max_int_k; ++i)    {        while(C[i] > 0)        {            array[cnt++] = i;            --C[i];        }    }    delete []C;}int main(){    int ia[] = {1,3,2,3,1,1,2,7};    COUNT_SORT(ia,8,7);    for(int i = 0; i < 8; ++i)        cout << ia[i] << ' ';    cout << endl;    return 0;}

思考题8-3

a) 首先假设全部是整数并且数字没有前导 0,即没有 001 这种数字。先将相同数字位数的分成一组并用计数排序对每组数字排序,然后用基数排序对每个组排序。
设数字位数为 i 的有 mi 个,所以 i=1nimi=n。计数排序 O(n),基数排序 O(n),总的为 O(n)

b) 将首字母相同的分成一组,使用计数排序第一个字母;若第一个字母相同,则去掉首字母再分组,递归排序直到只有一个字母。需要注意的是长度为 l 的字符串可能需要 l+1 次计数排序(如 aba,要排序必须把 b 和空字符比较)。运行时间是 O(n)

思考题8-4

a) 很简单,比较每个红色和蓝色水壶,即暴力破解,总时间 Θ(n2)

b) 类似决策树模型,结点内部是要比较的两个水壶,比较结果引出三条边,红色大于蓝色、等于蓝色、小于蓝色。叶结点就是匹配的水壶。n 个红色和蓝色水壶一共有 n! 种组合方式,若树高为 hl 个叶结点,类似 8.1 节有:n!l3hhΩ(nlgn)

c)
1.随机选一个红色水壶 O(1)
2.找出对应的蓝色水壶 O(n)
3.挑出的红色水壶在和蓝色组水壶对比时,蓝色水壶就可以分成两组(可能一组为空);
4.类似 3,步骤 2 选出的蓝色水壶对比红色组,红色水壶分成两组;
5.分别递归划分的两组水壶。显然期望比较次数 O(nlgn),最坏情况下是 O(n2)

下面的代码用两组数字代表两种颜色不同大小的水壶,最后排序两组数表示配对成功。

#include <iostream>#include <cstdlib>using std::cout;using std::endl;int partition(int *red,int *blue,int p,int r){    int index_red = rand() % (r - p + 1) + p;    int index_blue;    //找出红色对于的蓝色的下标    for(index_blue = p; index_blue <= r; ++index_blue)    {        if(red[index_red] == blue[index_blue])            break;    }    //交换blue的piovt    int temp = blue[index_blue];    blue[index_blue] =  blue[r];    blue[r] = temp;    //划分blue    int i = p - 1;    int blue_pivot = red[index_red];//蓝色不能和蓝色的水壶比较,所以此处用的红色    for(int j = p; j < r; ++j)    {        if(blue[j] <= blue_pivot)        {            ++i;            temp = blue[j];            blue[j] = blue[i];            blue[i] = temp;        }    }    temp = blue[r];    blue[r] = blue[i+1];    blue[i+1] = temp;    //交换red的piovt    temp = red[index_red];    red[index_red] = red[r];    red[r] = temp;    //划分red    int k = p - 1;    int red_piovt = blue[i+1];    for(int j = p; j < r; ++j)    {        if(red[j] <= red_piovt)        {            ++k;            temp = red[j];            red[j] = red[k];            red[k] = temp;        }    }    temp = red[r];    red[r] = red[k+1];    red[k+1] = temp;    //返回划分的下标    return k + 1;}void match_jugs(int *red,int *blue,int p,int r){    if(p < r)    {        int q = partition(red,blue,p,r);        match_jugs(red,blue,p,q-1);        match_jugs(red,blue,q+1,r);    }}int main(){    int ia[] = {13,-3,-25,20,-16,-23,18};    int ib[] = {18,20,-3,-23,13,-16,-25};    match_jugs(ia,ib,0,6);    for(int i = 0; i < 7; ++i)        cout << ia[i] << ' ';    cout << endl;    for(int i = 0; i < 7; ++i)        cout << ib[i] << ' ';    cout << endl;    return 0;}

思考题8-5

a) 就是普通的排序。

b) 2,1,4,3,6,5,8,7,10,9.

c)证明:

j=ii+k1A[j]kj=i+1i+kA[j]kA[i]+j=i+1i+k1A[j]kj=i+1i+k1A[j]+A[i+k]kA[i]kA[i+k]kA[i]A[i+k]

d) 由 c) 的证明,可以将数组分成 k 组,即 i,i+k,i+2k一组,每组 n/k 个,每组快速排序时间 O(nklgnk),总时间是 kO(nklgnk)=O(nlgnk)

e) 和练习 6.5-9 十分类似,建一个堆,运行时间是 O(nlgk)(参考6.5-9)。

f) 一共 k 组,每组 n/k 个,每组至少 Ω(nklgnk),总的就是 Ω(nlgnk)。当 k 是常数时有 Ω(nlgn)

思考题8-6

a) 2n 个数中选 n 个,即 (2nn)

b) 首先(2nn)=22nπn(1+O(1/n)),根据决策树模型,设有 l 个叶子结点,树高 h 得:

22nπn(1+O(1/n))l2h

化简有:
hlg(22nπn(1+O(1/n)))=lg22nlgπn+lg(1+O(1/n))=2no(n)

c) 很明显,不比较怎么知道哪个元素出列。

d) 对元素a1,b2,a2,b2,,an,bn 分成子表 a1,a2,,anb1,b2,,bn,一共有 2n1对元素需要比较,所以需要比较 2n1次。

思考题8-7

题目出奇的长,实在不想看,附一个链接,答案见此处这里写链接内容

0 0
原创粉丝点击