| 设计 任务书 文档 开题 答辩 说明书 格式 模板 外文 翻译 范文 资料 作品 文献 课程 实习 指导 调研 下载 网络教育 计算机 网站 网页 小程序 商城 购物 订餐 电影 安卓 Android Html Html5 SSM SSH Python 爬虫 大数据 管理系统 图书 校园网 考试 选题 网络安全 推荐系统 机械 模具 夹具 自动化 数控 车床 汽车 故障 诊断 电机 建模 机械手 去壳机 千斤顶 变速器 减速器 图纸 电气 变电站 电子 Stm32 单片机 物联网 监控 密码锁 Plc 组态 控制 智能 Matlab 土木 建筑 结构 框架 教学楼 住宅楼 造价 施工 办公楼 给水 排水 桥梁 刚构桥 水利 重力坝 水库 采矿 环境 化工 固废 工厂 视觉传达 室内设计 产品设计 电子商务 物流 盈利 案例 分析 评估 报告 营销 报销 会计 |
|
|
| 首 页 | 机械毕业设计 | 电子电气毕业设计 | 计算机毕业设计 | 土木工程毕业设计 | 视觉传达毕业设计 | 理工论文 | 文科论文 | 毕设资料 | 帮助中心 | 设计流程 |
您现在所在的位置:首页 >>文科论文 >> 文章内容 |
4.【严题集6.21②】画出和下列二叉树相应的森林。
答:注意根右边的子树肯定是森林,
而孩子结点的右子树均为兄弟。
![]() 六、算法设计题(前5题中任选2题,第6题必做,每题8分,共24分)
1.【严题集6.42③】编写递归算法,计算二叉树中叶子结点的数目。
解:思路:输出叶子结点比较简单,用任何一种遍历递归算法,凡是左右指针均空者,则为叶子,将其打印出来。
法一:核心部分为:
DLR(liuyu *root) /*中序遍历 递归函数*/
{if(root!=NULL)
{if((root->lchild==NULL)&&(root->rchild==NULL)){sum++; printf("%d\n",root->data);}
DLR(root->lchild);
DLR(root->rchild); }
return(0);
}
法二:
int LeafCount_BiTree(Bitree T)//求二叉树中叶子结点的数目
{
if(!T) return 0; //空树没有叶子
else if(!T->lchild&&!T->rchild) return 1; //叶子结点
else return Leaf_Count(T->lchild)+Leaf_Count(T->rchild);//左子树的叶子数加
上右子树的叶子数
}//LeafCount_BiTree
注:上机时要先建树!例如实验二的方案一。
① 打印叶子结点值(并求总数)
思路:先建树,再从遍历过程中打印结点值并统计。
步骤1 键盘输入序列12,8,17,11,16,2,13,9,21,4,构成一棵二叉排序树。叶子结点值应该是4,9, 13, 21, 总数应该是4.
127 17
2 11 16 21
4 9 13
编程: 生成二叉树排序树之后,再中序遍历排序查找结点的完整程序如下:
说明部分为:
#include <stdio.h>
#include <stdlib.h>
typedef struct liuyu{int data;struct liuyu *lchild,*rchild;}test;
liuyu *root;
int sum=0;int m=sizeof(test);
void insert_data(int x) /*如何生成二叉排序树?参见教材P43C程序*/
{ liuyu *p,*q,*s;
s=(test*)malloc(m);
s->data=x;
s->lchild=NULL;
s->rchild=NULL;
if(!root){root=s; return;}
p=root;
while(p) /*如何接入二叉排序树的适当位置*/
{q=p;
if(p->data==x){printf("data already exist! \n");return;}
else if(x<p->data)p=p->lchild; else p=p->rchild;
}
if(x<q->data)q->lchild=s;
else q->rchild=s;
}
DLR(liuyu *root) /*中序遍历 递归函数*/
{if(root!=NULL)
{if((root->lchild==NULL)&&(root->rchild==NULL)){sum++; printf("%d\n",root->data);}
DLR(root->lchild);
DLR(root->rchild); }
return(0);
}
main() /*先生成二叉排序树,再调用中序遍历递归函数进行排序输出*/
{int i,x;
i=1;
root=NULL; /*千万别忘了赋初值给root!*/
do{printf("please input data%d:",i);
i++;
scanf("%d",&x); /*从键盘采集数据,以-9999表示输入结束*/
if(x==-9999){
DLR(root);
printf("\nNow output count value:%d\n",sum);
return(0); }
else insert_data(x);} /*调用插入数据元素的函数*/
while(x!=-9999);
return(0);}
若一开始运行就输入-9999,则无叶子输出,sum=0。
2.【全国专升本统考题】写出求二叉树深度的算法,先定义二叉树的抽象数据类型。 (10分)
或【严题集6.44④】编写递归算法,求二叉树中以元素值为x的结点为根的子树的深度。
答;设计思路:只查后继链表指针,若左或右孩子的左或右指针非空,则层次数加1;否则函数返回。
但注意,递归时应当从叶子开始向上计数,否则不易确定层数。
int depth(liuyu*root) /*统计层数*/
{int d,p; /*注意每一层的局部变量d,p都是各自独立的*/
p=0;
if(root==NULL)return(p); /*找到叶子之后才开始统计*/
else{
d=depth(root->lchild);
if(d>p) p=d; /*向上回朔时,要挑出左右子树中的相对大的那个深度值*/
d=depth(root->rchild);
if(d>p)p=d;
}
p=p+1;
return(p);
}
法二:
int Get_Sub_Depth(Bitree T,int x)//求二叉树中以值为x的结点为根的子树深度
{
if(T->data==x)
{
printf("%d\n",Get_Depth(T)); //找到了值为x的结点,求其深度
exit 1;
}
}
else
{
if(T->lchild) Get_Sub_Depth(T->lchild,x);
if(T->rchild) Get_Sub_Depth(T->rchild,x); //在左右子树中继续寻找
}
}//Get_Sub_Depth
int Get_Depth(Bitree T)//求子树深度的递归算法
{
if(!T) return 0;
else
{
m=Get_Depth(T->lchild);
n=Get_Depth(T->rchild);
return (m>n?m:n)+1;
}
}//Get_Depth
附:上机调试过程
步骤1 键盘输入序列12,8,17,11,16,2,13,9,21,4,构成一棵二叉排序树。层数应当为4
步骤2: 执行求深度的函数,并打印统计出来的深度值。完整程序如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct liuyu{int data;struct liuyu *lchild,*rchild;}test;
liuyu *root;
int sum=0;int m=sizeof(test);
void insert_data(int x) /*如何生成二叉排序树?参见教材P43C程序*/
{ liuyu *p,*q,*s;
s=(test*)malloc(m);
s->data=x;
s->lchild=NULL;
s->rchild=NULL;
if(!root){root=s; return;}
p=root;
while(p) /*如何接入二叉排序树的适当位置*/
{q=p;
if(p->data==x){printf("data already exist! \n");return;}
else if(x<p->data)p=p->lchild; else p=p->rchild;
}
if(x<q->data)q->lchild=s;
else q->rchild=s;
}
int depth(liuyu*root) /*统计层数*/
{int d,p; /*注意每一层的局部变量d,p都是各自独立的*/
p=0;
if(root==NULL)return(p); /*找到叶子之后才开始统计*/
else{
d=depth(root->lchild);
if(d>p) p=d; /*向上回朔时,要挑出左右子树中的相对大的那个深度值*/
d=depth(root->rchild);
if(d>p)p=d;
}
p=p+1;
return(p);
}
void main() /*先生成二叉排序树,再调用深度遍历递归函数进行统计并输出*/
{int i,x;
i=1;
root=NULL; /*千万别忘了赋初值给root!*/
do{printf("please input data%d:",i);
i++;
scanf("%d",&x); /*从键盘采集数据,以-9999表示输入结束*/
if(x==-9999){
printf("\nNow output depth value=%d\n", depth (root)); return; }
else insert_data(x);} /*调用插入数据元素的函数*/
while(x!=-9999);
return;}
执行结果:
3. 【严题集6.47④】编写按层次顺序(同一层自左至右)遍历二叉树的算法。
或:按层次输出二叉树中所有结点;
解:思路:既然要求从上到下,从左到右,则利用队列存放各子树结点的指针是个好办法。
这是一个循环算法,用while语句不断循环,直到队空之后自然退出该函数。
技巧之处:当根结点入队后,会自然使得左、右孩子结点入队,而左孩子出队时又会立即使得它的左右孩子结点入队,……以此产生了按层次输出的效果。
level(liuyu*T)
/* liuyu *T,*p,*q[100]; 假设max已知*/
{int f,r;
f=0; r=0; /*置空队*/
r=(r+1)%max;
q[r]=T; /*根结点进队*/
while(f!=r) /*队列不空*/
{f=(f+1%max);
p=q[f]; /*出队*/
printf("%d",p->data); /*打印根结点*/
if(p->lchild){r=(r+1)%max; q[r]=p->lchild;} /*若左子树不空,则左子树进队*/
if(p->rchild){r=(r+1)%max; q[r]=p->rchild;} /*若右子树不空,则右子树进队*/
}
return(0);
}
法二:
void LayerOrder(Bitree T)//层序遍历二叉树
{
InitQueue(Q); //建立工作队列
EnQueue(Q,T);
while(!QueueEmpty(Q))
{
DeQueue(Q,p);
visit(p);
if(p->lchild) EnQueue(Q,p->lchild);
if(p->rchild) EnQueue(Q,p->rchild);
}
}//LayerOrder
可以用前面的函数建树,然后调用这个函数来输出。
完整程序如下(已上机通过)
#include <stdio.h>
#include <stdlib.h>
#define max 50
typedef struct liuyu{int data;struct liuyu *lchild,*rchild;}test;
liuyu *root,*p,*q[max];
int sum=0;int m=sizeof(test);
void insert_data(int x) /*如何生成二叉排序树?参见教材P43C程序*/
{ liuyu *p,*q,*s;
s=(test*)malloc(m);
s->data=x;
s->lchild=NULL;
s->rchild=NULL;
if(!root){root=s; return;}
p=root;
while(p) /*如何接入二叉排序树的适当位置*/
{q=p;
if(p->data==x){printf("data already exist! \n");return;}
else if(x<p->data)p=p->lchild; else p=p->rchild;
}
if(x<q->data)q->lchild=s;
else q->rchild=s;
}
level(liuyu*T)
/* liuyu *T,*p,*q[100]; 假设max已知*/
{int f,r;
f=0; r=0; /*置空队*/
r=(r+1)%max;
q[r]=T; /*根结点进队*/
while(f!=r) /*队列不空*/
{f=(f+1%max);
p=q[f]; /*出队*/
printf("%d",p->data); /*打印根结点*/
if(p->lchild){r=(r+1)%max; q[r]=p->lchild;} /*若左子树不空,则左子树进队*/
if(p->rchild){r=(r+1)%max; q[r]=p->rchild;} /*若右子树不空,则右子树进队*/
}
return(0);
}
void main() /*先生成二叉排序树,再调用深度遍历递归函数进行统计并输出*/
{int i,x;
i=1;
root=NULL; /*千万别忘了赋初值给root!*/
do{printf("please input data%d:",i);
i++;
scanf("%d",&x); /*从键盘采集数据,以-9999表示输入结束*/
if(x==-9999){
printf("\nNow output data value:\n", level(root)); return; }
else insert_data(x);} /*调用插入数据元素的函数*/
while(x!=-9999);
return;}
4. 已知一棵具有n个结点的完全二叉树被顺序存储于一维数组A中,试编写一个算法打印出编号为i的结点的双亲和所有的孩子。
答:首先,由于是完全二叉树,不必担心中途会出现孩子为null的情况。
其次分析:结点i的左孩子为2i,右孩子为2i+1;直接打印即可。
Printf(“Left_child=”, %d, v[2*i].data; “Right_child=”, %d, v[2*i+1].data;);
但其双亲是i/2,需先判断i为奇数还是偶数。若i为奇数,则应当先i-- ,然后再除以2。
If(i/2!=0)i--;
Printf(“Parents=”, %d, v[i/2].data;);
5.【严题集6.49④】编写算法判别给定二叉树是否为完全二叉树。
答:int IsFull_Bitree(Bitree T)//判断二叉树是否完全二叉树,是则返回1,否则返回0
{
InitQueue(Q);
flag=0;
EnQueue(Q,T); //建立工作队列
while(!QueueEmpty(Q))
{
{
DeQueue(Q,p);
if(!p) flag=1;
else if(flag) return 0;
else
{
EnQueue(Q,p->lchild);
EnQueue(Q,p->rchild); //不管孩子是否为空,都入队列
}
}//while
return 1;
}//IsFull_Bitree
分析:该问题可以通过层序遍历的方法来解决.与6.47相比,作了一个修改,不管当前结点
是否有左右孩子,都入队列.这样当树为完全二叉树时,遍历时得到是一个连续的不包含空
指针的序列.反之,则序列中会含有空指针.
6. 【严题集6.26③】假设用于通信的电文仅由8个字母组成,字母在电文中出现的频率分别为0.07,0.19,0.02,0.06,0.32,0.03,0.21,0.10。试为这8个字母设计哈夫曼编码。使用0~7的二进制表示形式是另一种编码方案。对于上述实例,比较两种方案的优缺点。
解:方案1;哈夫曼编码
先将概率放大100倍,以方便构造哈夫曼树。
w={7,19,2,6,32,3,21,10},按哈夫曼规则:【[(2,3),6], (7,10)】, ……19, 21, 32
![]() ![]() (100)(40) (60)
19 21 32 (28)
(17) (11)
7 10 6 (5)
2 3
方案比较:
![]() 方案1的WPL=2(0.19+0.32+0.21)+4(0.07+0.06+0.10)+5(0.02+0.03)=1.44+0.92+0.25=2.61
方案2的WPL=3(0.19+0.32+0.21+0.07+0.06+0.10+0.02+0.03)=3
结论:哈夫曼编码优于等长二进制编码
第7章 图 自测卷解答
一、单选题(每题1分,共16分) 前两大题全部来自于全国自考参考书!
( C )1. 在一个图中,所有顶点的度数之和等于图的边数的 倍。
A.1/2 B. 1 C. 2 D. 4
( B )2. 在一个有向图中,所有顶点的入度之和等于所有顶点的出度之和的 倍。
A.1/2 B. 1 C. 2 D. 4
( B )3. 有8个结点的无向图最多有 条边。
A.14 B. 28 C. 56 D. 112
( C )4. 有8个结点的无向连通图最少有 条边。
A.5 B. 6 C. 7 D. 8
( C )5. 有8个结点的有向完全图有 条边。
A.14 B. 28 C. 56 D. 112
( B )6. 用邻接表表示图进行广度优先遍历时,通常是采用 来实现算法的。
A.栈 B. 队列 C. 树 D. 图
( A )7. 用邻接表表示图进行深度优先遍历时,通常是采用 来实现算法的。
A.栈 B. 队列 C. 树 D. 图
( D )9. 已知图的邻接矩阵同上题8,根据算法,则从顶点0出发,按深度优先遍历的结点序列是
A. 0 2 4 3 1 5 6 B. 0 1 3 5 6 4 2 C. 0 4 2 3 1 6 5 D. 0 1 3 4 2 5 6
( B )10. 已知图的邻接矩阵同上题8,根据算法,则从顶点0出发,按广度优先遍历的结点序列是
A. 0 2 4 3 6 5 1 B. 0 1 3 6 4 2 5 C. 0 4 2 3 1 5 6 D. 0 1 3 4 2 5 6
(建议:0 1 2 3 4 5 6)
( C )11. 已知图的邻接矩阵同上题8,根据算法,则从顶点0出发,按广度优先遍历的结点序列是
A. 0 2 4 3 1 6 5 B. 0 1 3 5 6 4 2 C. 0 1 2 3 4 6 5 D. 0 1 2 3 4 5 6
( D )12. 已知图的邻接表如下所示,根据算法,则从顶点0出发按深度优先遍历的结点序列是
![]() ( A )13. 已知图的邻接表如下所示,根据算法,则从顶点0出发按广度优先遍历的结点序列是
![]() ( A )14. 深度优先遍历类似于二叉树的
A.先序遍历 B. 中序遍历 C. 后序遍历 D. 层次遍历
( D )15. 广度优先遍历类似于二叉树的
A.先序遍历 B. 中序遍历 C. 后序遍历 D. 层次遍历
( A )16. 任何一个无向连通图的最小生成树
A.只有一棵 B. 一棵或多棵 C. 一定有多棵 D. 可能不存在
(注,生成树不唯一,但最小生成树唯一,即边权之和或树权最小的情况唯一)
二、填空题(每空1分,共20分)
1. 图有 邻接矩阵 、 邻接表 等存储结构,遍历图有 深度优先遍历 、 广度优先遍历 等方法。
2. 有向图G用邻接表矩阵存储,其第i行的所有元素之和等于顶点i的 出度 。
3. 如果n个顶点的图是一个环,则它有 n 棵生成树。 (以任意一顶点为起点,得到n-1条边)
4. n个顶点e条边的图,若采用邻接矩阵存储,则空间复杂度为 O(n2) 。
5. n个顶点e条边的图,若采用邻接表存储,则空间复杂度为 O(n+e) 。
6. 设有一稀疏图G,则G采用 邻接表 存储较省空间。
7. 设有一稠密图G,则G采用 邻接矩阵 存储较省空间。
8. 图的逆邻接表存储结构只适用于 有向 图。
9. 已知一个图的邻接矩阵表示,删除所有从第i个顶点出发的方法是 将邻接矩阵的第i行全部置0 。
10. 图的深度优先遍历序列 不是 惟一的。
11. n个顶点e条边的图采用邻接矩阵存储,深度优先遍历算法的时间复杂度为 O(n2) ;若采用邻接表存储时,该算法的时间复杂度为 O(n+e) 。
12. n个顶点e条边的图采用邻接矩阵存储,广度优先遍历算法的时间复杂度为 O(n2) ;若采用邻接表存储,该算法的时间复杂度为 O(n+e)
13. 图的BFS生成树的树高比DFS生成树的树高 小或相等 。
14. 用普里姆(Prim)算法求具有n个顶点e条边的图的最小生成树的时间复杂度为 O(n2) ;用克鲁斯卡尔(Kruskal)算法的时间复杂度是 O(elog2e) 。
15. 若要求一个稀疏图G的最小生成树,最好用 克鲁斯卡尔(Kruskal) 算法来求解。
16. 若要求一个稠密图G的最小生成树,最好用 普里姆(Prim) 算法来求解。
17. 用Dijkstra算法求某一顶点到其余各顶点间的最短路径是按路径长度 递增 的次序来得到最短路径的。
18. 拓扑排序算法是通过重复选择具有 0 个前驱顶点的过程来完成的。
三、简答题(每题6分,共24分)
1. 【严题集7.1①】已知如图所示的有向图,请给出该图的:
(1)
![]() 每个顶点的入/出度;(2) 邻接矩阵;
(3) 邻接表;
(4) 逆邻接表。
答案:
2. 【严题集7.7②】请对下图的无向带权图:(1) 写出它的邻接矩阵,并按普里姆算法求其最小生成树;
(2) 写出它的邻接表,并按克鲁斯卡尔算法求其最小生成树。
解:设起点为a。可以直接由原始图画出最小生成树,而且最小生成树只有一种(类)!
邻接矩阵为:
PRIM算法(横向变化):
邻接表为:
先罗列:f---2---g a—3--c f—3—e a—4---b d—4—h
(a,b,c) (e,f,g) (d,h) 取b—5—d, g—5--d 就把三个连通分量连接起来了。
3. 【严题集7.5②】已知二维数组表示的图的邻接矩阵如下图所示。试分别画出自顶点1出发进行遍历所得的深度优先生成树和广度优先生成树。
![]() 4. 【严题集7.11②】试利用Dijkstra算法求图中从顶点a到其他各顶点间的最短路径,写出执行算法过程中各步的状态。
解:最短路径为:(a,c,f,e,d,g,b)
![]() 四、 【2001年计考研题】给定下列网G: (10分)1 试着找出网G的最小生成树,画出其逻辑结构图;
2 用两种不同的表示法画出网G的存储结构图;
3 用C语言(或其他算法语言)定义其中一种表示法(存储结构)的数据类型。
解:1. 最小生成树可直接画出,如右图所示。2. 可用邻接矩阵和邻接表来描述:
![]() ![]() 邻接表为:
五、算法设计题(每题10分,共30分)
1. 【严题集7.14③】编写算法,由依次输入的顶点数目、弧的数目、各顶点的信息和各条弧的信息建立有向图的邻接表。
解:Status Build_AdjList(ALGraph &G) //输入有向图的顶点数,边数,顶点信息和边的信息建立邻接表
{
InitALGraph(G);
scanf("%d",&v);
if(v<0) return ERROR; //顶点数不能为负
G.vexnum=v;
scanf("%d",&a);
if(a<0) return ERROR; //边数不能为负
G.arcnum=a;
for(m=0;m<v;m++)
G.vertices[m].data=getchar(); //输入各顶点的符号
for(m=1;m<=a;m++)
{
t=getchar();h=getchar(); //t为弧尾,h为弧头
if((i=LocateVex(G,t))<0) return ERROR;
if((j=LocateVex(G,h))<0) return ERROR; //顶点未找到
p=(ArcNode*)malloc(sizeof(ArcNode));
if(!G.vertices.[i].firstarc) G.vertices[i].firstarc=p;
else
{
for(q=G.vertices[i].firstarc;q->nextarc;q=q->nextarc);
q->nextarc=p;
}
p->adjvex=j;p->nextarc=NULL;
}//while
return OK;
}//Build_AdjList
2. 【严题集7.15③】试在邻接矩阵存储结构上实现图的基本操作:DeleteArc(G,v,w) ,即删除一条边的操作。(如果要删除所有从第i个顶点出发的边呢? 提示: 将邻接矩阵的第i行全部置0 )
解://本题中的图G均为有向无权图。
Status Delete_Arc(MGraph &G,char v,char w)//在邻接矩阵表示的图G上删除边(v,w)
{
if((i=LocateVex(G,v))<0) return ERROR;
if((j=LocateVex(G,w))<0) return ERROR;
if(G.arcs[i][j].adj)
{
G.arcs[i][j].adj=0;
G.arcnum--;
}
return OK;
}//Delete_Arc
3. 【严题集7.22③】试基于图的深度优先搜索策略写一算法,判别以邻接表方式存储的有向图中是否存在由顶点vi到顶点vj的路径(i≠j)。注意:算法中涉及的图的基本操作必须在此存储结构上实现。
int visited[MAXSIZE]; //指示顶点是否在当前路径上
int exist_path_DFS(ALGraph G,int i,int j)//深度优先判断有向图G中顶点i到顶点j
是否有路径,是则返回1,否则返回0
{
if(i==j) return 1; //i就是j
else
{
visited[i]=1;
for(p=G.vertices[i].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
if(!visited[k]&&exist_path(k,j)) return 1;//i下游的顶点到j有路径
}//for
}//else
}//exist_path_DFS
解2:(以上算法似乎有问题:如果不存在路径,则原程序不能返回0。我的解决方式是在原程序的中引入一变量level来控制递归进行的层数。具体的方法我在程序中用红色标记出来了。)
int visited[MAXSIZE]; //指示顶点是否在当前路径上
int level=1;//递归进行的层数
int exist_path_DFS(ALGraph G,int i,int j)//深度优先判断有向图G中顶点i到顶点j
是否有路径,是则返回1,否则返回0
{
if(i==j) return 1; //i就是j
else
{
visited[i]=1;
for(p=G.vertices[i].firstarc;p;p=p->nextarc,level--)
{ level++;
k=p->adjvex;
if(!visited[k]&&exist_path(k,j)) return 1;//i下游的顶点到j有路径
}//for
}//else
if (level==1) return 0;
}//exist_path_DFS
附加题:【严题集7.27④】采用邻接表存储结构,编写一个判别无向图中任意给定的两个顶点之间是否存在一条长度为k的简单路径的算法。
(注1:一条路径为简单路径指的是其顶点序列中不含有重现的顶点。
注2:此题可参见严题集P207-208中有关按“路径”遍历的算法基本框架。)
int visited[MAXSIZE];
int exist_path_len(ALGraph G,int i,int j,int k)//判断邻接表方式存储的有向图G
的顶点i到j是否存在长度为k的简单路径
{
{
if(i==j&&k==0) return 1; //找到了一条路径,且长度符合要求
else if(k>0)
{
visited[i]=1;
for(p=G.vertices[i].firstarc;p;p=p->nextarc)
{
l=p->adjvex;
if(!visited[l])
if(exist_path_len(G,l,j,k-1)) return 1; //剩余路径长度减一
}//for
visited[i]=0; //本题允许曾经被访问过的结点出现在另一条路径中
}//else
return 0; //没找到
}//exist_path_len
第8章 查找 自测卷答案 A
一、填空题(每空1分,共10分)
1. 在数据的存放无规律而言的线性表中进行检索的最佳方法是 顺序查找(线性查找) 。
2. 线性有序表(a1,a2,a3,…,a256)是从小到大排列的,对一个给定的值k,用二分法检索表中与k相等的元素,在查找不成功的情况下,最多需要检索 8 次。设有100个结点,用二分法查找时,最大比较次数是 7 。
3. 假设在有序线性表a[20]上进行折半查找,则比较一次查找成功的结点数为1;比较两次查找成功的结点数为 2 ;比较四次查找成功的结点数为 8 ;平均查找长度为 3.7 。
解:显然,平均查找长度=O(log2n)<5次(25)。但具体是多少次,则不应当按照公式
全部元素的查找次数为=(1+2×2+4×3+8×4+5×5)=74; ASL=74/20=3.7 !!!
4.【计研题2000】折半查找有序表(4,6,12,20,28,38,50,70,88,100),若查找表中元素20,它将依次与表中元素 28,6,12,20 比较大小。
5. 在各种查找方法中,平均查找长度与结点个数n无关的查找方法是 散列查找 。
6. 散列法存储的基本思想是由 关键字的值 决定数据的存储地址。
7. 有一个表长为m的散列表,初始状态为空,现将n(n<m)个不同的关键码插入到散列表中,解决冲突的方法是用线性探测法。如果这n个关键码的散列地址都相同,则探测的总次数是 n(n-1)/2=( 1+2+…+n-1) 。(而任一元素查找次数 ≤n-1)
二、单项选择题(每小题1分,共27分)
( B )1.在表长为n的链表中进行线性查找,它的平均查找长度为
A. ASL=n; B. ASL=(n+1)/2;
C. ASL=
( A )2.【计研题2001】折半查找有序表(4,6,10,12,20,30,50,70,88,100)。若查找表中元素58,则它将依次与表中 比较大小,查找结果是失败。
A.20,70,30,50 B.30,88,70,50 C.20,50 D.30,88,50
( C )3.【计研题2001】对22个记录的有序表作折半查找,当查找失败时,至少需要比较 次关键字。
A.3 B.4 C.5 D. 6
( A )4. 链表适用于 查找
A.顺序 B.二分法 C.顺序,也能二分法 D.随机
( C )5. 折半搜索与二叉搜索树的时间性能
A. 相同 B. 完全不同 C. 有时不相同 D. 数量级都是O(log2n)
6.【91程P3】从供选择的答案中,选出应填入下面叙述 ? 内的最确切的解答,把相应编号写在答卷的对应栏内。
要进行线性查找,则线性表 A ;要进行二分查找,则线性表 B ;要进行散列查找,则线性表 C 。
某顺序存储的表格,其中有90000个元素,已按关键项的值的上升顺序排列。现假定对各个元素进行查找的概率是相同的,并且各个元素的关键项的值皆不相同。当用顺序查找法查找时,平均比较次数约为 D ,最大比较次数为 E 。
供选择的答案:
A~C:① 必须以顺序方式存储 ② 必须以链表方式存储 ③ 必须以散列方式存储
④ 既可以以顺序方式,也可以以链表方式存储
⑤ 必须以顺序方式存储且数据元素已按值递增或递减的次序排好
⑥ 必须以链表方式存储且数据元素已按值递增或递减的次序排好
D,E: ① 25000 ② 30000 ③ 45000 ④ 90000
答案: A= ④ B= ⑤ C= ③ D= ③ E= ④
7. (96初程P73)从供选择的答案中,选出应填入下面叙述 ? 内的最确切的解答,把相应编号写在答卷的对应栏内。
数据结构反映了数据元素之间的结构关系。链表是一种 A ,它对于数据元素的插入和删除 B 。通常查找线性表数据元素的方法有 C 和 D 两种方法,其中 C 是一种只适合于顺序存储结构但 E 的方法;而 D 是一种对顺序和链式存储结构均适用的方法。
供选择的答案:
A:①顺序存储线性表 ②非顺序存储非线性表 ③顺序存储非线性表 ④非顺序存储线性表
B: ① 不需要移动结点,不需改变结点指针 ②不需要移动结点,只需改变结点指针
③只需移动结点,不需改变结点指针 ④既需移动结点,又需改变结点指针
C:① 顺序查找 ②循环查找 ③条件查找 ④二分法查找
D:① 顺序查找 ②随机查找 ③二分法查找 ④分块查找
E:① 效率较低的线性查找 ②效率较低的非线性查找
③ 效率较高的非线性查找 ④效率较高的线性查找
答案:A= ④ B= ② C= ④ D= ① E= ③
8. 【97程P18】 从供选择的答案中,选出应填入下面叙述 ? 内的最确切的解答,把相应编号写在答卷的对应栏内。
在二叉排序树中,每个结点的关键码值 A , B 一棵二叉排序,即可得到排序序列。同一个结点集合,可用不同的二叉排序树表示,人们把平均检索长度最短的二叉排序树称作最佳二叉排序,最佳二叉排序树在结构上的特点是 C 。
供选择的答案
A: ①比左子树所有结点的关键码值大,比右子树所有结点的关键码值小
②比左子树所有结点的关键码值小,比右子树所有结点的关键码值大
③比左右子树的所有结点的关键码值都大
④与左子树所有结点的关键码值和右子树所有结点的关键码值无必然的大小关系
B: ①前序遍历 ② 中序(对称)遍历 ③ 后序遍历 ④ 层次遍历
C:① 除最下二层可以不满外,其余都是充满的 ②除最下一层可以不满外,其余都是充满的
③ 每个结点的左右子树的高度之差的绝对值不大于1 ④ 最下层的叶子必须在最左边
答案:A= ① B= ② C= ②
9. 【92程 P6】 从供选择的答案中,选出应填入下面叙述 ? 内的最确切的解答,把相应编号写在答卷的对应栏内。
散列法存储的基本思想是根据 A 来决定 B ,碰撞(冲突)指的是 C ,处理碰撞的两类主要方法是 D 。
供选择的答案
A,B: ①存储地址 ② 元素的符号 ③ 元素个数 ④ 关键码值
⑤ 非码属性 ⑥ 平均检索长度 ⑦ 负载因子 ⑧ 散列表空间
C: ①两个元素具有相同序号 ② 两个元素的关键码值不同,而非码属性相同
③ 不同关键码值对应到相同的存储地址 ④ 负载因子过大 ⑤ 数据元素过多
D: ① 线性探查法和双散列函数法 ② 建溢出区法和不建溢出区法
③ 除余法和折叠法 ④ 拉链法和开地址法
答案:A= ④ B= ① C= ③ D= ④
10.【91程P4】考虑具有如下性质的二叉树:除叶子结点外,每个结点的值都大于其左子树上的一切结点的值。并小于等于其右子树上的一切结点的值。现把9个数1,2,3,…,8,9填入右图所示的二叉树的9个结点中,并使之具有上述性质。此时,n1的值是 A ,n2的值是 B ,n9的值是 C 。现欲把
供选择的答案
A~C: ①1 ② 2 ③ 3 ④ 4 ⑤ 5 ⑥ 6 ⑦ 7 ⑧ 8 ⑨ 9
D~E: ① n7下面 ② n8下面 ③ n9下面 ④ n6下面
⑤ n1与n2之间 ⑥ n2与n4之间 ⑦ n6与n9之间 ⑧ n3与n6之间
答案:A= ⑦ B= ④ C= ⑥ D= ② E= ⑥
![]() 三、简答题(每小题4分,共16分)
1.【全国专升本题】对分(折半)查找适不适合链表结构的序列,为什么?用二分查找的查找速度必然比线性查找的速度快,这种说法对吗?
答:不适合!虽然有序的单链表的结点是按从小到大(或从大到小)顺序排列,但因其存储结构为单链表,查找结点时只能从头指针开始逐步搜索,故不能进行折半查找。
二分查找的速度在一般情况下是快些,但在特殊情况下未必快。例如所查数据位于首位时,则线性查找快;而二分查找则慢得多。
2.【计研题1999】假定对有序表:(3,4,5,7,24,30,42,54,63,72,87,95)进行折半查找,试回答下列问题:
(1) 画出描述折半查找过程的判定树;
(2) 若查找元素54,需依次与哪些元素比较?
(3) 若查找元素90,需依次与哪些元素比较?
(4) 假定每个元素的查找概率相等,求查找成功时的平均查找长度。
解:
(1) 先画出判定树如下(注:mid=ë(1+12)/2û=6):
305 63
3 7 42 87
4 24 54 72 95
(2) 查找元素54,需依次与30, 63, 42, 54 等元素比较;
(3) 查找元素90,需依次与30, 63,87, 95, 72等元素比较;
(4) 求ASL之前,需要统计每个元素的查找次数。判定树的前3层共查找1+2×2+4×3=17次;
但最后一层未满,不能用8×4,只能用5×4=20次,
所以ASL=1/12(17+20)=37/12≈3.08
3. 【全国专升本题】用比较两个元素大小的方法在一个给定的序列中查找某个元素的时间复杂度下限是什么? 如果要求时间复杂度更小,你采用什么方法?此方法的时间复杂度是多少?
答:查找某个元素的时间复杂度下限,如果理解为最短查找时间,则当关键字值与表头元素相同时,比较1次即可。要想降低时间复杂度,可以改用Hash查找法。此方法对表内每个元素的比较次数都是O(1)。
4. 【计研题1999】设哈希(Hash)表的地址范围为0~17,哈希函数为:H(K)=K MOD 16。
K为关键字,用线性探测法再散列法处理冲突,输入关键字序列:
(10,24,32,17,31,30,46,47,40,63,49)
造出Hash表,试回答下列问题:
(1) 画出哈希表的示意图;
(2) 若查找关键字63,需要依次与哪些关键字进行比较?
(3) 若查找关键字60,需要依次与哪些关键字比较?
(4) 假定每个关键字的查找概率相等,求查找成功时的平均查找长度。
解: (1)画表如下:
(2) 查找63,首先要与H(63)=63%16=15号单元内容比较,即63 vs 31 ,no;
然后顺移,与46,47,32,17,63相比,一共比较了6次!
(3)查找60,首先要与H(60)=60%16=12号单元内容比较,但因为12号单元为空(应当有空标记),所以应当只比较这一次即可。
(4) 对于黑色数据元素,各比较1次;共6次;
对红色元素则各不相同,要统计移位的位数。“63”需要6次,“49”需要3次,“40”需要2次,“46”需要3次,“47”需要3次,
所以ASL=1/11(6+2+3×3)=17/11=1.5454545454≈1.55
四、分析题(每小题6分,共24分)
1. 【严题集9.3②】画出对长度为10的有序表进行折半查找的判定树,并求其等概率时查找成功的平均查找长度。
解:判定树应当描述每次查找的位置: 52 8
1 3 6 9
4 7 10
2. 【全国专升本考题】在一棵空的二叉查找树中依次插入关键字序列为12,7,17,11,16,2,13,9,21,4,请画出所得到的二叉查找树。
答:
128 17
2 11 16 21
4 9 13
验算方法: 用中序遍历应得到排序结果: 2,4,7,9,11,12,13,16,17,21
3. 【严题集9.9③】已知如下所示长度为12的表:
(Jan, Feb, Mar, Apr, May, June, July, Aug, Sep, Oct, Nov, Dec)
(1) 试按表中元素的顺序依次插入一棵初始为空的二叉排序树,画出插入完成之后的二叉排序树,并求其在等概率的情况下查找成功的平均查找长度。
(2) 若对表中元素先进行排序构成有序表,求在等概率的情况下对此有序表进行折半查找时查找成功的平均查找长度。
(3)
解:
![]() 4. 选取散列函数H(key)=(3*key)%11,用线性探测法处理冲突,对下列关键码序列构造一个散列地址空间为0~10,表长为11的散列表,{22,41,53,08,46,30,01,31,66}。
解:由题意知,m=11(刚好为素数)
则(22*3)%11=6……0 (41*3)%11=11……2
(53*3)%11=14……5
(08*3)%11=2……2
(46*3)%11=12……6
(30*3)%11=8……2
(01*3)%11=0……3
(31*3)%11=8……5
(66*3)%11=9……0
五、算法设计题(4中选3,第1题7分必选,其余每题8分,共23分)
1. 已知11个元素的有序表为(05 13 19 21 37 56 64 75 80 88 92), 请写出折半查找的算法程序,查找关键字为key的数据元素 (建议上机调试)。
解:折半查找的C程序有很多参考资料,注意此题要求是整型量。
折半查找的一个递归算法如下,形式非常简洁!
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
2. 【严题集9.31④】试写一个判别给定二叉树是否为二叉排序树的算法,设此二叉树以二叉链表作存储结构。且树中结点的关键字均不同。
解:注意仔细研究二叉排序树的定义。易犯的典型错误是按下述思路进行判别:“若一棵非空的二叉树其左、右子树均为二叉排序树,且左子树的根的值小于根结点的值,又根结点的值不大于右子树的根的值,则是二叉排序树”
(刘注:即不能只判断左右孩子的情况,还要判断左右孩子与双亲甚至根结点的比值也要遵循(左小右大)原则)。
若要采用递归算法,建议您采用如下的函数首部:
bool BisortTree(BiTree T, BiTree&PRE),其中PRE为指向当前访问结点的前驱的指针。
(或者直接存储前驱的数值,随时与当前根结点比较)
一个漂亮的算法设计如下:
int last=0, flag=1; // last是全局变量,用来记录前驱结点值,只要每个结点都比前驱大就行。
int Is_BSTree(Bitree T) //判断二叉树T是否二叉排序树,是则返回1,否则返回0
{
if(T->lchild&&flag) Is_BSTree(T->lchild);
if(T->data<last) flag=0; //与其中序前驱相比较, flag=0表示当前结点比直接前驱小,则立即返回
last=T->data;
if(T->rchild&&flag) Is_BSTree(T->rchild);
return flag;
}//Is_BSTree
3. 【严题集9.22④】已知一个含有1000个记录的表,关键字为中国人姓氏的拼音,请给出此表的一个哈希表设计方案,要求它在等概率情况下查找成功的平均查找长度不超过3。
解:设计哈希表的步骤为:
a) 根据所选择的处理冲突的方法求出装载因子a的上界;
b) 由a值设计哈希表的长度m;
c) 根据关键字的特性和表长m选定合适的哈希函数。
刘注:要求ASL≤3,则m必然要尽量长,以减少冲突;
4. 【严题集9.44④】已知某哈希表的装载因子小于1,哈希函数H(key)为关键字(标识符)的第一个字母在字母表中的序号,处理冲突的方法为线性探测开放定址法。试编写一个按第一个字母的顺序输出哈希表中所有关键字的算法。
解:注意此题给出的条件:装载因子a〈1, 则哈希表未填满。由此可写出下列形式简明的算法:
void PrintWord(Hash Table ht)
{//按第一个字母的顺序输出哈希表ht中的标识符。哈希函数为表示符的第一个字母在字母表中的序号,处理冲突的方法是线性探测开放定址法。
for(i=1; i<=26; i++){
j=i;
While(ht.elem[j].key){
if(Hash(ht.elem[j].key==i)printf(ht.elem[j].key);
j=(j+1)%m;
}
}
}//PrintWord
第9章 排序 自测卷
一、填空题(每空1分,共24分)
1. 大多数排序算法都有两个基本的操作: 比较 和 移动 。
2. 在对一组记录(54,38,96,23,15,72,60,45,83)进行直接插入排序时,当把第7个记录60插入到有序表时,为寻找插入位置至少需比较 6 次。
3. 在插入和选择排序中,若初始数据基本正序,则选用 插入 ;若初始数据基本反序,则选用 选择 。
4. 在堆排序和快速排序中,若初始记录接近正序或反序,则选用 堆排序 ;若初始记录基本无序,则最好选用 快速排序 。
5. 对于n个记录的集合进行冒泡排序,在最坏的情况下所需要的时间是 O(n2) 。若对其进行快速排序,在最坏的情况下所需要的时间是 O(n2) 。
6. 对于n个记录的集合进行归并排序,所需要的平均时间是 O(nlog2n)
,所需要的附加空间是 O(n) 。
7. 对于n个记录的表进行2路归并排序,整个归并排序需进行 ┌log2n┐ 趟(遍)。
8. 设要将序列(Q, H, C, Y, P, A, M, S, R, D, F, X)中的关键码按字母序的升序重新排列,则:
冒泡排序一趟扫描的结果是 H C Q P A M S R D F X Y ;
初始步长为4的希尔(shell)排序一趟的结果是 P A C S Q H F X R D M Y ;
二路归并排序一趟扫描的结果是 H Q C Y A P M S D R F X;
快速排序一趟扫描的结果是 F H C D P A M Q R S Y X ;
堆排序初始建堆的结果是 A D C R F Q M S Y P H X 。
9. 在堆排序、快速排序和归并排序中,
若只从存储空间考虑,则应首先选取 方法,其次选取 快速排序方法,最后选取归并排序方法;
若只从排序结果的稳定性考虑,则应 选取 归并排序 方法;
若只从平均情况下最快考虑,则应选取 堆排序、快速排序和归并排序 方法;
若只从最坏情况下最快并且要节省内存考虑,则应选取 堆排序 方法。
二、单项选择题(每小题1分,共18分)
( C )1.将5个不同的数据进行排序,至多需要比较 次。
A. 8 B. 9 C. 10 D. 25
( C )2. 排序方法中,从未排序序列中依次取出元素与已排序序列(初始时为空)中的元素进行比较,将其放入已排序序列的正确位置上的方法,称为
A. 希尔排序 B. 冒泡排序 C. 插入排序 D. 选择排序
( D )3.从未排序序列中挑选元素,并将其依次插入已排序序列(初始时为空)的一端的方法,称为
A. 希尔排序 B. 归并排序 C. 插入排序 D. 选择排序
( B )4.对n个不同的排序码进行冒泡排序,在下列哪种情况下比较的次数最多。
A. 从小到大排列好的 B. 从大到小排列好的 C. 元素无序 D. 元素基本有序
( D )5.对n个不同的排序码进行冒泡排序,在元素无序的情况下比较的次数为
A. n+1 B. n C. n-1 D. n(n-1)/2
( C )6.快速排序在下列哪种情况下最易发挥其长处。
A. 被排序的数据中含有多个相同排序码 B. 被排序的数据已基本有序
C. 被排序的数据完全无序 D. 被排序的数据中的最大值和最小值相差悬殊
( B )7. 对有n个记录的表作快速排序,在最坏情况下,算法的时间复杂度是
A.O(n) B.O(n2) C.O(nlog2n) D.O(n3)
( C )8.若一组记录的排序码为(46, 79, 56, 38, 40, 84),则利用快速排序的方法,以第一个记录为基准得到的一次划分结果为
A. 38, 40, 46, 56, 79, 84 B. 40, 38, 46 , 79, 56, 84
C. 40, 38,46, 56, 79, 84 D. 40, 38, 46, 84, 56, 79
( D )9.下列关键字序列中, 是堆。
A. 16, 72, 31, 23, 94, 53 B. 94, 23, 31, 72, 16, 53
C. 16, 53, 23, 94,31, 72 D. 16, 23, 53, 31, 94, 72
( B )10.堆是一种 排序。
A. 插入 B.选择 C. 交换 D. 归并
( C )11.堆的形状是一棵
A. 二叉排序树 B.满二叉树 C. 完全二叉树 D. 平衡二叉树
( B )12.若一组记录的排序码为(46, 79, 56, 38, 40, 84),则利用堆排序的方法建立的初始堆为
A. 79, 46, 56, 38, 40, 84 B. 84, 79, 56, 38, 40, 46
C. 84, 79, 56, 46, 40, 38 D. 84, 56, 79, 40, 46, 38
( C )17. 下述几种排序方法中,要求内存最大的是
A. 插入排序 B.快速排序 C. 归并排序 D. 选择排序
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||