《数据结构题集》答案 第9章 查找
发布时间:2024-11-17
发布时间:2024-11-17
C语言版 清华大学出版社出版内附两套答案
第九章 查找
9.25
int Search_Sq(SSTable ST,int key)//在有序表上顺序查找的算法,监视哨设在高下标端
{
ST.elem[ST.length+1].key=key;
for(i=1;ST.elem[i].key>key;i++);
if(i>ST.length||ST.elem[i].key<key) return ERROR;
return i;
}//Search_Sq
分析:本算法查找成功情况下的平均查找长度为ST.length/2,不成功情况下为ST.length.
9.26
int Search_Bin_Recursive(SSTable ST,int key,int low,int high)//折半查找的递归算法
{
if(low>high) return 0; //查找不到时返回0
mid=(low+high)/2;
if(ST.elem[mid].key==key) return mid;
else if(ST.elem[mid].key>key)
return Search_Bin_Recursive(ST,key,low,mid-1);
else return Search_Bin_Recursive(ST,key,mid+1,high);
}
}//Search_Bin_Recursive
9.27
int Locate_Bin(SSTable ST,int key)//折半查找,返回小于或等于待查元素的最后一个结点号
{
int *r;
r=ST.elem;
if(key<r .key) return 0;
else if(key>=r[ST.length].key) return ST.length;
low=1;high=ST.length;
while(low<=high)
{
mid=(low+high)/2;
if(key>=r[mid].key&&key<r[mid+1].key) //查找结束的条件
return mid;
C语言版 清华大学出版社出版内附两套答案
else if(key<r[mid].key) high=mid;
else low=mid;
} //本算法不存在查找失败的情况,不需要return 0;
}//Locate_Bin
9.28
typedef struct {
int maxkey;
int firstloc;
} Index;
typedef struct {
int *elem;
int length;
Index idx[MAXBLOCK]; //每块起始位置和最大元素,其中idx[0]不利用,其内容初始化为{0,0}以利于折半查找
int blknum; //块的数目
} IdxSqList; //索引顺序表类型
int Search_IdxSeq(IdxSqList L,int key)//分块查找,用折半查找法确定记录所在块,块内采用顺序查找法
{
if(key>L.idx[L.blknum].maxkey) return ERROR; //超过最大元素
low=1;high=L.blknum;
found=0;
while(low<=high&&!found) //折半查找记录所在块号mid
{
mid=(low+high)/2;
if(key<=L.idx[mid].maxkey&&key>L.idx[mid-1].maxkey)
found=1;
else if(key>L.idx[mid].maxkey)
low=mid+1;
else high=mid-1;
}
i=L.idx[mid].firstloc; //块的下界
j=i+blksize-1; //块的上界
temp=L.elem[i-1]; //保存相邻元素
L.elem[i-1]=key; //设置监视哨
for(k=j;L.elem[k]!=key;k--); //顺序查找
L.elem[i-1]=temp; //恢复元素
if(k<i) return ERROR; //未找到
return k;
}//Search_IdxSeq
C语言版 清华大学出版社出版内附两套答案
分析:在块内进行顺序查找时,如果需要设置监视哨,则必须先保存相邻块的相邻元素,以免数据丢失.
9.29
typedef struct {
LNode *h; //h指向最小元素
LNode *t; //t指向上次查找的结点
} CSList;
LNode *Search_CSList(CSList &L,int key)//在有序单循环链表存储结构上的查找算法,假定每次查找都成功
{
if(L.t->data==key) return L.t;
else if(L.t->data>key)
for(p=L.h,i=1;p->data!=key;p=p->next,i++);
else
for(p=L.t,i=L.tpos;p->data!=key;p=p->next,i++);
L.t=p; //更新t指针
return p;
}//Search_CSList
分析:由于题目中假定每次查找都是成功的,所以本算法中没有关于查找失败的处理.由微积分可得,在等概率情况下,平均查找长度约为n/3.
9.30
typedef struct {
DLNode *pre;
int data;
DLNode *next;
} DLNode;
typedef struct {
DLNode *sp;
int length;
} DSList; //供查找的双向循环链表类型
DLNode *Search_DSList(DSList &L,int key)//在有序双向循环链表存储结构上的查找算法,假定每次查找都成功
{
p=L.sp;
if(p->data>key)
{
while(p->data>key) p=p->pre;
L.sp=p;
C语言版 清华大学出版社出版内附两套答案
}
else if(p->data<key)
{
while(p->data<key) p=p->next;
L.sp=p;
}
return p;
}//Search_DSList
分析:本题的平均查找长度与上一题相同,也是n/3.
9.31
int last=0,flag=1;
int Is_BSTree(Bitree T)//判断二叉树T是否二叉排序树,是则返回1,否则返回0 {
if(T->lchild&&flag) Is_BSTree(T->lchild);
if(T->data<last) flag=0; //与其中序前驱相比较
last=T->data;
if(T->rchild&&flag) Is_BSTree(T->rchild);
return flag;
}//Is_BSTree
9.32
int last=0;
void MaxLT_MinGT(BiTree T,int x)//找到二叉排序树T中小于x的最大元素和大于x的最小元素
{
if(T->lchild) MaxLT_MinGT(T->lchild,x); //本算法仍是借助中序遍历来实现 if(last<x&&T->data>=x) //找到了小于x的最大元素
printf("a=%d\n",last);
if(last<=x&&T->data>x) //找到了大于x的最小元素
printf("b=%d\n",T->data);
last=T->data;
if(T->rchild) MaxLT_MinGT(T->rchild,x);
}//MaxLT_MinGT
9.33
void Print_NLT(BiTree T,int x)//从大到小输出二叉排序树T中所有不小于x的元素
{
if(T->rchild) Print_NLT(T->rchild,x);
C语言版 清华大学出版社出版内附两套答案
if(T->data<x) exit(); //当遇到小于x的元素时立即结束运行
printf("%d\n",T->data);
if(T->lchild) Print_NLT(T->lchild,x); //先右后左的中序遍历
}//Print_NLT
9.34
void Delete_NLT(BiTree &T,int x)//删除二叉排序树T中所有不小于x元素结点,并释放空间
{
if(T->rchild) Delete_NLT(T->rchild,x);
if(T->data<x) exit(); //当遇到小于x的元素时立即结束运行
q=T;
T=T->lchild;
free(q); //如果树根不小于x,则删除树根,并以左子树的根作为新的树根 if(T) Delete_NLT(T,x); //继续在左子树中执行算法
}//Delete_NLT
9.35
void Print_Between(BiThrTree T,int a,int b)//打印输出后继线索二叉排序树T中所有大于a且小于b的元素
{
p=T;
while(!p->ltag) p=p->lchild; //找到最小元素
while(p&&p->data<b)
{
if(p->data>a) printf("%d\n",p->data); //输出符合条件的元素
if(p->rtag) p=p->rtag;
else
{
p=p->rchild;
while(!p->ltag) p=p->lchild;
} //转到中序后继
}//while
}//Print_Between
9.36
void BSTree_Insert_Key(BiThrTree &T,int x)//在后继线索二叉排序树T中插入元素x
{
if(T->data<x) //插入到右侧
{
if(T->rtag) //T没有右子树时,作为右孩子插入
C语言版 清华大学出版社出版内附两套答案
{
p=T->rchild;
q=(BiThrNode*)malloc(sizeof(BiThrNode));
q->data=x;
T->rchild=q;T->rtag=0;
q->rtag=1;q->rchild=p; //修改原线索
}
else BSTree_Insert_Key(T->rchild,x);//T有右子树时,插入右子树中
}//if
else if(T->data>x) //插入到左子树中
{
if(!T->lchild) //T没有左子树时,作为左孩子插入
{
q=(BiThrNode*)malloc(sizeof(BiThrNode));
q->data=x;
T->lchild=q;
q->rtag=1;q->rchild=T; //修改自身的线索
}
else BSTree_Insert_Key(T->lchild,x);//T有左子树时,插入左子树中
}//if
}//BSTree_Insert_Key
9.37
Status BSTree_Delete_key(BiThrTree &T,int x)//在后继线索二叉排序树T中删除元素x
{
BTNode *pre,*ptr,*suc;//ptr为x所在结点,pre和suc分别指向ptr的前驱和后继 p=T;last=NULL; //last始终指向当前结点p的前一个(前驱)
while(!p->ltag) p=p->lchild; //找到中序起始元素
while(p)
{
if(p->data==x) //找到了元素x结点
{
pre=last;
ptr=p;
}
else if(last&&last->data==x) suc=p; //找到了x的后继
if(p->rtag) p=p->rtag;
else
{
p=p->rchild;
while(!p->ltag) p=p->lchild;
} //转到中序后继
last=p;
C语言版 清华大学出版社出版内附两套答案
}//while //借助中序遍历找到元素x及其前驱和后继结点
if(!ptr) return ERROR; //未找到待删结点
Delete_BSTree(ptr); //删除x结点
if(pre&&pre->rtag)
pre->rchild=suc; //修改线索
return OK;
}//BSTree_Delete_key
void Delete_BSTree(BiThrTree &T)//课本上给出的删除二叉排序树的子树T的算法,按照线索二叉树的结构作了一些改动
{
q=T;
if(!T->ltag&&T->rtag) //结点无右子树,此时只需重接其左子树
T=T->lchild;
else if(T->ltag&&!T->rtag) //结点无左子树,此时只需重接其右子树
T=T->rchild;
else if(!T->ltag&&!T->rtag) //结点既有左子树又有右子树
{
p=T;r=T->lchild;
while(!r->rtag)
{
s=r;
r=r->rchild; //找到结点的前驱r和r的双亲s
}
T->data=r->data; //用r代替T结点
if(s!=T)
s->rchild=r->lchild;
else s->lchild=r->lchild; //重接r的左子树到其双亲结点上
q=r;
}//else
free(q); //删除结点
}//Delete_BSTree
分析:本算法采用了先求出x结点的前驱和后继,再删除x结点的办法,这样修改线索时会比较简单,直接让前驱的线索指向后继就行了.如果试图在删除x结点的同时修改线索,则问题反而复杂化了.
9.38
void BSTree_Merge(BiTree &T,BiTree &S)//把二叉排序树S合并到T中 {
if(S->lchild) BSTree_Merge(T,S->lchild);
if(S->rchild) BSTree_Merge(T,S->rchild); //合并子树
Insert_Key(T,S); //插入元素
}//BSTree_Merge
C语言版 清华大学出版社出版内附两套答案
void Insert_Node(Bitree &T,BTNode *S)//把树结点S插入到T的合适位置上 {
if(S->data>T->data)
{
if(!T->rchild) T->rchild=S;
else Insert_Node(T->rchild,S);
}
else if(S->data<T->data)
{
if(!T->lchild) T->lchild=S;
else Insert_Node(T->lchild,S);
}
S->lchild=NULL; //插入的新结点必须和原来的左右子树断绝关系
S->rchild=NULL; //否则会导致树结构的混乱
}//Insert_Node
分析:这是一个与课本上不同的插入算法.在合并过程中,并不释放或新建任何结点,而是采取修改指针的方式来完成合并.这样,就必须按照后序序列把一棵树中的元素逐个连接到另一棵树上,否则将会导致树的结构的混乱.
9.39
void BSTree_Split(BiTree &T,BiTree &A,BiTree &B,int x)//把二叉排序树T分裂为两棵二叉排序树A和B,其中A的元素全部小于等于x,B的元素全部大于x {
if(T->lchild) BSTree_Split(T->lchild,A,B,x);
if(T->rchild) BSTree_Split(T->rchild,A,B,x); //分裂左右子树
if(T->data<=x) Insert_Node(A,T);
else Insert_Node(B,T); //将元素结点插入合适的树中
}//BSTree_Split
void Insert_Node(Bitree &T,BTNode *S)//把树结点S插入到T的合适位置上 {
if(!T) T=S; //考虑到刚开始分裂时树A和树B为空的情况
else if(S->data>T->data) //其余部分与上一题同
{
if(!T->rchild) T->rchild=S;
else Insert_Node(T->rchild,S);
}
else if(S->data<T->data)
{
if(!T->lchild) T->lchild=S;
else Insert_Node(T->lchild,S);
}
S->lchild=NULL;
C语言版 清华大学出版社出版内附两套答案
S->rchild=NULL;
}//Insert_Key
9.40
typedef struct {
int data;
int bf;
int lsize; //lsize域表示该结点的左子树的结点总数加1
BlcNode *lchild,*rchild;
} BlcNode,*BlcTree; //含lsize域的平衡二叉排序树类型
BTNode *Locate_BlcTree(BlcTree T,int k)//在含lsize域的平衡二叉排序树T中确定第k小的结点指针
{
if(!T) return NULL; //k小于1或大于树结点总数
if(T->lsize==k) return T; //就是这个结点
else if(T->lsize>k)
return Locate_BlcTree(T->lchild,k); //在左子树中寻找
else return Locate_BlcTree(T->rchild,k-T->lsize); //在右子树中寻找,注意要修改k的值
}//Locate_BlcTree
9.41
typedef struct {
enum {LEAF,BRANCH} tag; //结点类型标识
int keynum;
BPLink parent; //双亲指针
int key[MAXCHILD]; //关键字
union {
BPLink child[MAXCHILD];//非叶结点的孩子指针
struct {
rectype *info[MAXCHILD];//叶子结点的信息指针 BPNode *next; //指向下一个叶子结点的链接
} leaf;
}
} BPNode,*BPLink,*BPTree;//B+树及其结点类型
Status BPTree_Search(BPTree T,int key,BPNode *ptr,int pos)//B+树中按关键字随机查找的算法,返回包含关键字的叶子结点的指针ptr以及关键字在叶子结点中的位置pos
{
p=T;
while(p.tag==BRANCH) //沿分支向下查找
C语言版 清华大学出版社出版内附两套答案
{
for(i=0;i<p->keynum&&key>p->key[i];i++); //确定关键字所在子树
if(i==p->keynum) return ERROR; //关键字太大
p=p->child[i];
}
for(i=0;i<p->keynum&&key!=p->key[i];i++); //在叶子结点中查找
if(i==p->keynum) return ERROR; //找不到关键字
ptr=p;pos=i;
return OK;
}//BPTree_Search
9.42
void TrieTree_Insert_Key(TrieTree &T,StringType key)//在Trie树T中插入字符串key,StringType的结构见第四章
{
q=(TrieNode*)malloc(sizeof(TrieNode));
q->kind=LEAF;
q->lf.k=key; //建叶子结点
klen=key[0];
p=T;i=1;
while(p&&i<=klen&&p->bh.ptr[ord(key[i])])
{
last=p;
p=p->bh.ptr[ord(key[i])];
i++;
} //自上而下查找
if(p->kind==BRANCH) //如果最后落到分支结点(无同义词):
{
p->bh.ptr[ord(key[i])]=q; //直接连上叶子
p->bh.num++;
}
else //如果最后落到叶子结点(有同义词):
{
r=(TrieNode*)malloc(sizeof(TrieNode)); //建立新的分支结点
last->bh.ptr[ord(key[i-1])]=r; //用新分支结点取代老叶子结点和上一层的联系 r->kind=BRANCH;r->bh.num=2;
r->bh.ptr[ord(key[i])]=q;
r->bh.ptr[ord(p->lf.k[i])]=p; //新分支结点与新老两个叶子结点相连
}
}//TrieTree_Insert_Key
分析:当自上而下的查找结束时,存在两种情况.一种情况,树中没有待插入关键字的同义词,此时只要新建一个叶子结点并连到分支结点上即可.另一种情况,有同
C语言版 清华大学出版社出版内附两套答案
义词,此时要把同义词的叶子结点与树断开,在断开的部位新建一个下一层的分支结点,再把同义词和新关键字的叶子结点连到新分支结点的下一层.
9.43
Status TrieTree_Delete_Key(TrieTree &T,StringType key)//在Trie树T中删除字符串key
{
p=T;i=1;
while(p&&p->kind==BRANCH&&i<=key[0]) //查找待删除元素
{
last=p;
p=p->bh.ptr[ord(key[i])];
i++;
}
if(p&&p->kind==LEAF&&p->lf.k=key) //找到了待删除元素
{
last->bh.ptr[ord(key[i-1])]=NULL;
free(p);
return OK;
}
else return ERROR; //没找到待删除元素
}//TrieTree_Delete_Key
9.44
void Print_Hash(HashTable H)//按第一个字母顺序输出Hash表中的所有关键字,其中处理冲突采用线性探测开放定址法
{
for(i=1;i<=26;i++)
for(j=i;H.elem[j].key;j=(j+1)%hashsize[sizeindex]) //线性探测
if(H(H.elem[j].key)==i) printf("%s\n",H.elem[j]);
}//Print_Hash
int H(char *s)//求Hash函数
{
if(s) return s[0]-96; //求关键字第一个字母的字母序号(小写)
else return 0;
}//H
9.45
typedef *LNode[MAXSIZE] CHashTable; //链地址Hash表类型
C语言版 清华大学出版社出版内附两套答案
Status Build_Hash(CHashTable &T,int m)//输入一组关键字,建立Hash表,表长为m,用链地址法处理冲突.
{
if(m<1) return ERROR;
T=malloc(m*sizeof(WORD)); //建立表头指针向量
for(i=0;i<m;i++) T[i]=NULL;
while((key=Inputkey())!=NULL) //假定Inputkey函数用于从键盘输入关键字 {
q=(LNode*)malloc(sizeof(LNode));
q->data=key;q->next=NULL;
n=H(key);
if(!T[n]) T[n]=q; //作为链表的第一个结点
else
{
for(p=T[n];p->next;p=p->next);
p->next=q; //插入链表尾部.本算法不考虑排序问题.
}
}//while
return OK;
}//Build_Hash
9.46
Status Locate_Hash(HashTable H,int row,int col,KeyType key,int &k)//根据行列值在Hash表表示的稀疏矩阵中确定元素key的位置k
{
h=2*(100*(row/10)+col/10); //作者设计的Hash函数
while(H.elem[h].key&&!EQ(H.elem[h].key,key))
h=(h+1)%20000;
if(EQ(H.elem[h].key,key)) k=h;
else k=NULL;
}//Locate_Hash
分析:本算法所使用的Hash表长20000,装填因子为50%,Hash函数为行数前两位和列数前两位所组成的四位数再乘以二,用线性探测法处理冲突.当矩阵的元素是随机分布时,查找的时间复杂度为O(1).
另解:
第九章 查找
习题及答案
题号:1 2 3 4 5 6 7 8 9 10 11 12 13 14
15 16 17 18
C语言版 清华大学出版社出版内附两套答案
19 20 21 22 23
一、基础知识题
1.对含有n个互不相同元素的集合,同时找最大元和最小元至少需进行多少次比较?
答:我们可以设立两个变量max和min用于存放最大元和最小元(的位置),第一次取两个元素进行比较,大的放入max,小的放入min,从第2次开始,每次取一个元素先和max比较,如果大于max则以它替换max,并结束本次比较;若小于max则再与min相比较,在最好的情况下,一路比较下去都不用和min相比较,所以这种情况下,至少要进行n-1次比较就能找到最大元和最小元。(顺便说一下,最坏情况下,要进行2n-3次比较
才能得到结果)
2.若对具有n个元素的有序的顺序表和无序的顺序表分别进行顺序查找,试在下述两种情况下分别讨论两者在等概率时的平均查找长度:(1)查找
不成功,即表中无关键字等于给定值K的记录;(2)查找成功,即表中有关键字等于给定值K的记录。
答:查找不成功时,需进行n+1次比较才能确定查找失败。因此平均查找长度为n+1,这时有序表和无序表是一样的。
查找成功时,平均查找长度为(n+1)/2,有序表和无序表也是一样的。因为顺序查找对表的原始序列的有序性不感兴趣。
3.画出对长度为18的有序的顺序表进行二分查找的判定树,并指出在等概率时查找成功的平均查找长度,以及查找失败时所需的最多的关键字比
较次数。
答:请看题图。
等概率情况下,查找成功的平均查找长度为:
ASL=(1+2*2+3*4+4*8+5*3)/18=3.556
也可以用公式代,大约为:ASL=(18+1)lg(18+1)/18-1=3.346
查找失败时,最多的关键字比较次树不超过判定树的深度,此处为5.如图:
4.为什么有序的单链表不能进行折半查找?
答:因为链表无法进行随机访问,如果要访问链表的中间结点,就必须先从头结点开始进行依次访问,这就要浪费很多时间,还不如进行顺序查找,
而且,用链存储结构将无法判定二分的过程是否结束,因此无法用链表实现二分查找。
5.设有序表为(a,b,c,e,f,g,i,j,k,p,q),请分别画出对给定值b,g和n进行折半查找的过程。
解: b的查找过程如下(其中括号表示当前查找区间,圆括号表示当前比较的关键字)
下标: 1 2 3 4 5 6 7 8 9 10 11 12 13
第一次比较: [a b c d e f (g) h i j k p q]
第二次比较: [a b (c)d e f] g h i j k p q
第三次比较: [a(b)]c d e f g h i j k p q
经过三次比较,查找成功。
g的查找过程如下:
[a b c d e f (g) h i j k p q]
一次比较成功。
n的查找过程如下:
下标: 1 2 3 4 5 6 7 8 9 10 11 12 13
第一次比较: [a b c d e f (g) h i j k p q]
第二次比较: a b c d e f g [h i (j) k p q]
C语言版 清华大学出版社出版内附两套答案
第三次比较: a b c d e f g h i j [k (p) q]
第四次比较: a b c d e f g h i j [k] p q]
经过四次比较,查找失败。
6.将(for, case, while, class, protected, virtual, public,
private, do, template, const ,if,
int)中的关键字依次插入初态为空的二叉排序树中,请画出所得到的树T。然后画出删去for之后的二叉排序树T',若再将for
插入T'中得到的二叉排序树T''是否与T相同?最后给出T"的先序、中序和后序序列。
答:见题图:
T"的先序序列是:do case class const while protected private if
for int virtual public template
T"的中序序列是:case class const do for if int private
protected public template virtual while
T"的后序序列是:const class case for int if private template
public virtual protected while do
二叉排序树T如下图:
删去for后的二叉排序树如下图:圈内的for表示再插入后的结点:
7.对给定的关键字集合,以不同的次序插入初始为空的树中,是否有可能得到同一棵二叉排序树?
答:有可能。如有两个序列:3,1,2,4 和 3,4,1,2,它们插入空树所得的二叉排序树是相同的。
8.将二叉排序树T的先序序列中的关键字依次插入一空树中,所得和二叉排序树T'与T"是否相同?为什么?
答:这两棵二叉树完全相同。
9.设二叉排序树中关键字由1至1000的整数构成,现要查找关键字为363的结点,下述关键字序列哪一个不可能是在二叉排序树上查找到的序列?
(a) 2,252,401,398,330, 344,397,363;
(b) 924, 220, 911, 244, 898, 258, 362, 363;
(c) 925, 202, 911, 240, 912, 245, 363;
(d) 2, 399, 387, 219, 266, 382, 381, 278, 363.
答:(c)是不可能查找到的序列。我们可以把这四个序列各插入到一个初始为空的二叉排序树中,结果可以发现,(c)序列所形成的不是一条路径,
而是有分支的,可见它是不可能在查找过程中访问到的序列。
10.设二叉排序树中关键字互不相同,则其中最小元必无左孩子,最大元必无右孩子。此命题是否正确?最小元和最大元一定是叶子吗?一个新结点
总是插在二叉排序树的某叶子上吗?
答:此命题正确。假设最小元有左孩子,则根据二叉排序树性质,此左孩子应比最小元更小,如此一来就产生矛盾了,因此最小元不可能有左孩子,
对于最大元也是这个道理。
但最大元和最小元不一定是叶子,它也可以是根、内部结点(分支结点)等,这得根据插入结点时的次序而定。如3,1,2,4
这个集合,根据不同的插入次序可以得到不同的二叉排序树:
○3
C语言版 清华大学出版社出版内附两套答案
/ \
○1 ○4
\
○2
○2
/ \
○1 ○3
\
○4
○4
\
○3
\
○2
\
○1
○2
/ \
○1 ○4
/
○3
新结点总是插入在二叉排序树的某个叶子上的。
11.在一棵m阶的B-树中,当将一关键字插入某结点而引起该结点的分裂时,此结点原有多少个关键字?若删去某结点中的一个关键字,而导致结点
合并时,该结点中原有几个关键字?
答:在此树中,若由于一关键字的插入某结点而引起该结点的分裂时,则该结点原有m-1个关键字。
若删去某结点中一个关键字而导致结点合并时,该结点中原有┌m/2┐-1个关键字。
12.在一棵B-树中,空指针数总是比关键字数多一个,此说法是否正确?请问包含8个关键字的3阶B-树(即2-3树)最多有几个结点?最少有几个结
C语言版 清华大学出版社出版内附两套答案
点?画出这两种情况的B-树。
答:这个说法是正确的。
包含8个关键字的3阶B-树最多有7个结点,最少有4个结点。
见题图。:图如下:
13.从空树开始,依次输入20,30,50,52,60,68,70,画出建立2-3树的过程。并画出删除50和68后的B-树状态。
答:过程如下:
(1) 插入20,30: (2) 插入50:
(3) 插入52: (4) 插入60:
(5) 插入68: (6) 插入70:
(7)删去50: (8) 删去68
14。画出依次插入z,v,o,p,w,y到图9.12(h)所示的5阶B-树的过程。
答:如图:第一步,插入z:
第二、三步,插入v,o:
第四五六步,插入p,w,y:
15. 在含有n个关键字的m阶B-树中进行查找,至多读盘多少次?完全平衡的二叉排序树的读盘次数大约比它大多少倍?
答:在含有n个关键字的m阶B-树中进行查找至多读盘次数不超过B-树高h,即logt((n+1)/2)+1
,(注,此处t为底,值是除根外的每个内部结点的最小度数┌m/2┐).
完全平衡的二叉树高为lgn,所以它的读盘次数至多也是lgn,它与上述B-树的读盘次数的比值约为lgt倍(此处底是2).
16.为什么在内存中使用的B-树通常是3阶的,而不使用更高阶的B-树?
答:因为查找等操作的cpu时间在B-树上是O(lgn (m/lgt)),而m/lgt>1,所以m较大时它所费时间比平衡的二叉排序树上相应操作时间大得多,因
此,仅在内存中使用的B-树通常取最小值3.
17.为什么二叉排序树长高时,新结点总是一个叶子,而B-树长高时,新结点总是根?哪一种长高能保证树平衡?
答:因为在二叉排序树中,关键字总是作为一个叶子结点插入以原来的树中,所以当树增高时,新结点总是一个叶子;而B-树中关键字插入总是插入到叶子结点内部,在叶结点中的关键字数目尚未超过它能够容纳的数目之前是不会增加结点的,当关键字数超过结点可容纳的数目时,叶结点就会发生分裂,产生一个新结点(但不一定引起树增高),并且将其中的中间结点传至上一层,只有当这种分裂操作传递至根结点并引起根结点的分裂
时,才能引起树高增加,此时产生一个新的根结点。所以说B树长高时,新结点总是根。
显然,后一种长高总能保证树的平衡。
18.已知关键字序列为(PAL,LAP,PAM,MAP,PAT,PET,SET,SAT,TAT,BAT)试为它们设计一个散列函数,将其映射到区间[0..n-1]上,要求碰撞尽可能的
少。这里n=11,13,17,19.
解:我的设计的散列函数是这样的,把关键字串中的每一个字符按其所在位置分别将其ASCII值乘以一个不同的数,然后把这些值相加的和去对n
求余,余数即为散列表中的位置。函数如下:
int Hash (char key[])
{
return (int)(key[0]+key[1]*0.618+key[2]*10)%n;
}