一、判断题
(1)若某堆栈的输入序列为1,2,3,4,则4,3,1,2不可能是堆栈的输出序列之一。( true )
(2)队列逻辑上是一个表头和表尾既能插入又能删除的线性表。(false)
(3)若一个叶子结点是某子树的中序遍历序列的最后一个结点,则它必是该子树的先序遍历中的最后一个结点。(true)
(4)在索引顺序表上实现分块查找,在等概率查找情况下,其查找长度只与表中元素个数有关,而与每块中元素个数无关。(false)
(5)二叉树为二叉排序树的充分必要条件是其任一结点的值均大于其左孩子的值,小于其右孩子的值。(false)
(6)在10万个随机排列的数据中,要选出5个最小的数,采用快速排序比采用Shell排序、堆排序及各种直接排序法都快。(false)
(7)用邻接矩阵法存储一个图时,在不考虑压缩存储的情况下,所占用的存储空间大小与图中的结点的个数有关,而与图的边数无关。(true)
(8)在一个有向图中,所有顶点的入度之和等于所有顶点的出度之和。 ( true )
(9)二分查找法不仅适用于顺序存储的有序表,而且适合于链接表。(false )
(10)按中序遍历一棵二叉排序树所得到的遍历序列是一个递增序列。( true )
二、填空题
1.数据结构有如下四种基本结构:
集合、线性结构、图结构、树结构
2.在一个算法中的语句频度之和为T(n)=3720n+4nlogn,则算法的时间复杂度为 O(nlogn)
3.已知某二叉树的叶子结点的个数为10个,度为1的结点个数为8个,则该二叉树的结点总数为 10+8+9=27
4. 已知一个有序表为(13,18,24,35,47,50,62,83,90,115,134),当二分检索值为90 的元素时,需__ 2___次比较可检索成功。
5.图的遍历方法主要有两个:深度优先搜索,广度优先搜索
6.在哈希造表的过程中,处理主要冲突的方法有
1、开放地址法 2、再哈希法(+1) 3、链地址法 4、建立一个公共的溢出区
7.在一个单链表中p所指结点(p所指不是最后结点)之后插入一个由指针s所指结点,应执行s->next=__ p->next ___;和p->next=___ s _____的操作。
8.已知一棵二叉树的先序序列为ABCD,中序序列为BCAD,则它的后序序列为___CBDA_
9.有N个顶点组成的无向连通图,最多可以_____N*(N-1)/2______条边。
10.若某序列的初始状态为{49,13,42,8,65,55,79},若以49为枢轴,则经过一趟快速排序之后的序列为:8 13 42 49 65 55 79。
三、单项选择题
1、从一个长度为n的顺序表中删除第i个元素(1≦i≦n)时,需向前移动( A )个元素。
A. n-i B. n-i+1 C. n-i-1 D. i
2、队列操作的原则是:( A )
A.先进先出 B.后进先出 C.只能进行插入 D.只能进行删除
3、下列算法中,( B)算法用来求图中每对顶点之间的最短路径。
A.Dijkstra B.Floyed C.Prim D.Kruskal
4、下面关于线性表的叙述中,错误的是 (B )
A)线性表采用顺序存储,必顺占用一片连续的存储单元。
B)线性表采用顺序存储,便于进行插入和删除操作。
C)线性表采用链接存储,不必占用一片连续的存储单元
D)线性表采用链接存储,便于插入和删除操作。
5.对下面的有向图进行拓扑排序,其排序结果不正确的是:(C)
A. 1、4、2、6、5、3 B .1、2、4、6、3、5 C. 1、4、2、5、6、3
6、带头结点的单链表head为空的判定条件是:( B )
A.head= =NULL B. head->next= =NULL
C.head->next= =head D.head!=NULL
7、在所有排序方法中,关键字的比较次数与记录的初始排列无关的是______D__。
A.Shell排序 B. 冒泡排序 C. 直接插入排序 D. 直接选择排序
8、设有两个串p和q,求q在p中首次出现的位置的运算叫_____A_____。
A. 模式匹配 B. 求子串 C. 定位 D. 查找
9、将长度为n的单链表链接到长度为m的单链表之后的算法的时间复杂度为(A )
A. O(m) B. O(1) C. O(n) D . O(m+n)
10、用线性探测法查找散列表,可能要探测多个散列地址,这些位置上的关键值( D )
A.一定都是同义词 B. 一定都不是同义词
C. 都相同 D.不一定都是同义词
四、解答题
1、对于给定的一组权值W={3,6,9,11,13,15,17,19}}画出Huffman树(要求结点左小右大),并给出每个结点的Huffman编码,计算该树的WPL。(6分)
19:00 9:010 3:0110 6:0111 11:100 13:101
15:110 17:111;
WPL=(19*2+9*3+3*4+6*4+11*3+13*3+15*3+17*3)=269
2、考虑下图:(10分)
- 从顶点A出发,求它的深度优先生成树。
- 从顶点E出发,求它的广度优先生成树。
- 根据普里姆(Prim)算法,求它的最小生成树。(从顶点A出发,要求给出计算过程)
1)(有多种答案)
2)(有多种答案)
3)(计算过程略)
3、已知待散列的线性表为(36,15,40,63,22),散列用的一维地址空间为[0..6],假定选用的散列函数是H(K)=K % 7,若发生冲突采用线性探查法处理,试:
计算出每一个元素的散列地址并在下图中填写出散列表;
求出在查找每一个元素概率相等情况下的平均查找长度。
- H(36)=36 % 7=1
H(15)=15 % 7=1 冲突
H1(15)=(15+1) % 7=2
H(40)=40 % 7=5
H(63)=63 %7=0
H(22)=22 % 7=1 冲突
H1(22)=(22+1)% 7=2 冲突
H2(22)=(22+2)%7=3
0 |
1 |
2 |
3 |
4 |
5 |
6 |
63 |
36 |
15 |
22 |
40 |
||
(冲突次数)0 |
1 |
0 |
0 |
2 |
(2)平均查找长度=(1+2+1+1+3)/5=1.6(冲突次数+1加起来除以个数)
4、对关键字序列(49,38,65,97,76,13,27,49)进行堆排序,使之按关键字非递增次序排列。请写出排序过程中得到的初始堆和前二趟的序列状态。 (8分)
初始堆:________
第1趟:_______ _
第2趟:______ __
所有父节点都比子节点小,称为最小堆 反之则为最大堆
初始堆:13,38,27,49,76,65,49,97
第一趟:27,38,49,49,76,65,97,13
第二趟;38,49,49,97,76,65,27,13
五、算法填空与设计
- 此为向以T为树根指针的二叉排序树上插入值为item的结点的递归算法。(6分)
- p->lchild=NULL;p->rchild=NULL;
2) Insert(T->lchild,item); 3) Insert(T->rchild,item)
void Insert(BiTree &T, ElemType item)
{
if(T==NULL)
{ p=( BiTree )malloc(sizeof(BiTNode));
p->data=item;
_______________________;
T=p;
}
else if(item.key<T->data.key) ___________________;
else ________________________;
}
其中:二叉树的存储结构为:
typedef struct BiTNode{
ElemType data;
Struct BiTNode *lchild, *rchild;
}BiTNode, *Bitree;
- 以下运算实现在循环队上的入队列,请在________________处用适当句子予以填充。(6分)
4 ) ==Q.front 5) Q.base[Q.rear]=e; 6) Q.rear=(Q.rear+1)%MAXQSIZE;
status EnQueue(SqQueue &Q, QElemType x)
//插入元素x为Q的新的队尾元素
{ if((Q.rear+1)%MAXQSIZE== ________________)
{error(“队满”); return(ERROR);}
else{ ________________;
______________________________;
return(OK);
}
}
其中循环队列存储结构为:
#define MAXQSIZE 100
typedef struct {
QelemType *base;
int front;
int rear;
} SqQueue;
- 已知一个线性表中的元素按元素值非递减有序排列,编写一个程序删除线性表中多余的值相同的元素,并返回删除后线性表的长度(8分)
方案2:
1、假设用带头结点的循环链表表示队列,并且只设一个指针rear指向队尾结点,但不设头指针,请写出相应的入队列算法EnQueue(用函数实现)(5分)。
链表结点结构定义如下:
typedef struct Lnode
{
ElemType data;
struct Lnode *next;
}Lnode, *LinkQueue;
void EnQueue(Lnode *rear, ElemType e)
{
}
2.已知顺序栈SqStack的基本操作函数如下:
int InitStack(SqStack &S); //构造空栈
int StackEmpty(SqStack &S);//判断栈空
int Push(SqStack &S,ElemType e);//入栈
int Pop(SqStack &S,ElemType &e);//出栈
请编写算法完成十进制数转换为八进制数。(5分)
答案
3.已知二叉树的存储结构为如下的二叉链表,请设计一个判断两个二叉树是否相同的算法。(5分)
typedef struct BiTNode
{
ElemType data;
Struct BiTNode *lchild,*rchild;
}BiTNode, *BiTree;
答案 :
int judgebitree(BiTree *bt1,BiTree *bt2)
{
if ( !bt1&& !bt2) return(1);
else if (!bt1 || !bt2 ||bt1->data!=bt2->data) return(0);
else return (judgebitree(bt1->lchild,bt2->lchild)
*judgebitree(bt1->rchild,bt2->rchild));
}
六、附
求哈夫曼树的方法如下:
假设五个权值是 1 3 7 8 14
(1) 从小到大排序 1 3 7 8 14 (这是有序序列)
(2) 每次提取最小的两个结点,取结点1和结点3,组成新结点N4,其权值=1+3=4,
取数值较小的结点作为左分支,1作为左分支,而3就作为右分支.
(3) 将新结点N4放入有序序列,保持从小到大排序:
N4 7 8 14
(4) 重复步骤(2),提取最小的两个结点,N4与结点7组成新结点N11,其权值=4+7=11,
N4数值较小,作为左分支,而结点7就作为右分支.
(5) 将新结点N11放入有序序列,保持从小到大排序:
8 N11 14
(6) 重复步骤(2),提取最小的两个结点,结点8与N11组成新结点N19,其权值=8+11=19,
结点8的数值较小,作为左分支,N11就作为右分支.
(7) 将新结点N19放入有序序列,保持从小到大排序:
14 N19
(8) 重复步骤(2),提取剩下的两个结点,结点14与N19组成新结点N33,其权值=14+19=33,
数值较小的结点14作为左分支,N19就作为右分支.
有序序列已经没有结点,最后得到"哈夫曼树":
N33
/ \
14 N19
/ \
8 N11
/ \
N4 7
/ \
1 3
带权路径长度(WPL):
根结点N33到结点14的路径长度是1,结点14的带权路径长度是14*1
根结点N33到结点8的路径长度是2,结点8的带权路径长度是8*2
根结点N33到结点7的路径长度是3,结点7的带权路径长度是7*3
根结点N33到结点3的路径长度是4,结点3的带权路径长度是3*4
根结点N33到结点1的路径长度是4,结点1的带权路径长度是1*4
所以,哈夫曼树的带权路径长度(WPL)等于
14*1 + 8*2 + 7*3 + 3*4 + 1*4 = 67
哈夫曼编码:
规定哈夫曼树的左分支代表0,右分支代表1.
从根结点N33到结点14,经历一次左分支,结点14的编码就是0
从根结点N33到结点8,先经历右分支,后经历左分支,结点8的编c码就是10
从根结点N33到结点7,先后经历三次右分支,结点7的编码就是111
从根结点N33到结点3,先经历两次右分支,再经历左分支,最后经历右分支,结点3的编码就是1101
从根结点N33到结点1,先经历两次右分支,再经历两次左分支,结点1的编码就是1100
得出所有结点的"哈夫曼编码":
权值14: 0
权值8 : 10
权值7 : 111
权值3 : 1101
权值1 : 1100
七、哈夫曼树测试程序
//C语言测试程序(来自其他网友)
//
//输入构造哈夫曼树中带权叶子结点数(n):5
//输入5个整数作为权值:1 3 7 8 14
//可以得出哈夫曼树的广义表形式,带权路径长度,以及哈夫曼编码.
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
struct BTreeNode
{
ElemType data;
struct BTreeNode* left;
struct BTreeNode* right;
};
//1、输出二叉树,可在前序遍历的基础上修改。
// 采用广义表格式,元素类型为int
void PrintBTree_int(struct BTreeNode* BT)
{
if (BT != NULL)
{
printf("%d", BT->data); //输出根结点的值
if (BT->left != NULL || BT->right != NULL)
{
printf("(");
PrintBTree_int(BT->left); //输出左子树
if (BT->right != NULL)
printf(",");
PrintBTree_int(BT->right); //输出右子树
printf(")");
}
}
}
//2、根据数组 a 中 n 个权值建立一棵哈夫曼树,返回树根指针
struct BTreeNode* CreateHuffman(ElemType a[], int n)
{
int i, j;
struct BTreeNode **b, *q;
b = malloc(n*sizeof(struct BTreeNode));
//初始化b指针数组,使每个指针元素指向a数组中对应的元素结点
for (i = 0; i < n; i++)
{
b[i] = malloc(sizeof(struct BTreeNode));
b[i]->data = a[i];
b[i]->left = b[i]->right = NULL;
}
for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树
{
//k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标
int k1 = -1, k2;
//让k1初始指向森林中第一棵树,k2指向第二棵
for (j = 0; j < n; j++)
{
if (b[j] != NULL && k1 == -1)
{
k1 = j;
continue;
}
if (b[j] != NULL)
{
k2 = j;
break;
}
}
//从当前森林中求出最小权值树和次最小
for (j = k2; j < n; j++)
{
if (b[j] != NULL)
{
if (b[j]->data < b[k1]->data)
{
k2 = k1;
k1 = j;
}
else if (b[j]->data < b[k2]->data)
k2 = j;
}
}
//由最小权值树和次最小权值树建立一棵新树,q指向树根结点
q = malloc(sizeof(struct BTreeNode));
q->data = b[k1]->data + b[k2]->data;
q->left = b[k1];
q->right = b[k2];
b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置
b[k2] = NULL;//k2位置为空
}
free(b); //删除动态建立的数组b
return q; //返回整个哈夫曼树的树根指针
}
//3、求哈夫曼树的带权路径长度
ElemType WeightPathLength(struct BTreeNode* FBT, int len)//len初始为0
{
if (FBT == NULL) //空树返回0
return 0;
else
{
if (FBT->left == NULL && FBT->right == NULL)//访问到叶子结点
{
printf("+ %d * %d ",FBT->data,len);
return FBT->data * len;
}
else //访问到非叶子结点,进行递归调用,
{ //返回左右子树的带权路径长度之和,len递增
return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);
}
}
}
//4、哈夫曼编码(可以根据哈夫曼树带权路径长度的算法基础上进行修改)
void HuffManCoding(struct BTreeNode* FBT, int len)//len初始值为0
{
//定义静态数组a,保存每个叶子的编码,数组长度至少是树深度减一
static int a[10];
int i;
//访问到叶子结点时输出其保存在数组a中的0和1序列编码
if (FBT != NULL)
{
if (FBT->left == NULL && FBT->right == NULL)
{
printf("权值为%d的编码:", FBT->data);
for (i = 0; i < len; i++)
printf("%d", a[i]);
printf("\n");
}
else //访问到非叶子结点时分别向左右子树递归调用,
{ //并把分支上的0、1编码保存到数组a的对应元素中,
//向下深入一层时len值增1
a[len] = 0;
HuffManCoding(FBT->left, len + 1);
a[len] = 1;
HuffManCoding(FBT->right, len + 1);
}
}
}
int main()
{
int n, i;
ElemType* a;
struct BTreeNode* fbt;
printf("输入构造哈夫曼树中带权叶子结点数(n):");
while(1)
{
scanf("%d", &n);
if (n > 1)
break;
else
printf("重输n值:");
}
a = malloc(n*sizeof(ElemType));
printf("输入%d个整数作为权值:", n);
for (i = 0; i < n; i++)
scanf(" %d", &a[i]);
fbt = CreateHuffman(a, n);
printf("广义表形式的哈夫曼树:");
PrintBTree_int(fbt);
printf("\n");
printf("哈夫曼树的带权路径长度:\n");
printf("=");
printf("\n=%d\n", WeightPathLength(fbt, 0));
printf("树中每个叶子结点的哈夫曼编码:\n");
HuffManCoding(fbt, 0);
return 0;
}