一、试给出下列有关并查集(mfsets)的操作序列的运算结果:
union(1, 2), union(3, 4), union(3, 5),union(1, 7), union(3, 6),
union(8, 9), union(1, 8), union(3, 10), union(3, 11), union(3, 12),
union(3, 13), union(14, 15), union(16, 0), union(14, 16), union(1, 3),
union(1, 14)。(union是合并运算,在以前的书中命名为merge)
要求
(1) 对于union(i, j),以i作为j的双亲; (5分)
(2) 按i和j为根的树的高度实现union(i, j),高度大者为高度小者的双亲; (5分)
(3) 按i和j为根的树的结点个数实现union(i, j),结点个数大者为结点个数小者的双亲。 (5分)
二、设在4地(A, B, C, 0D)之间架设有6座桥,如图所示:
要求从某一地出发,经过每座桥恰巧一次,最后仍回到原地。
(1) 试就以上图形说明:此问题有解的条件什么? (5分)
(2) 设图中的顶点数为n,试用C或Pascal描述与求解此问题有关的数据结构并编写一个算法,找出满足要求的一条回路。 (10分)
三、针对以下情况确定非递归的归并排序的运行时间(数据比较次数与移动次数):
(1) 输入的n个数据全部有序; (5分)
(2) 输入的n个数据全部逆向有序; (5分)
(3) 随机地输入n个数据。 (5分)
四、简单回答有关AVL树的问题。
(1) 在有N个结点的AVL树中,为结点增加一个存放结点高度的数据成员,那么每一个结点需要增加多少个字位(bit)? (5分)
(2) 若每一个结点中的高度计数器有8bits,那么这样的AVL树可以有多少层?最少有多少个关键码? (5分)
五、设一个散列表包含hashSize = 13个表项,其下标从0到12,采用线性探查法解决冲突。请按以下要求,将下列关键码散列到表中。
10 100 32 45 58 126 3 29 200 400 0
(1) 散列函数采用除留余数法,用%hashSize(取模运算)将各关键码映像到表中。请指出每一个产生冲突的关键码可能产生多少次冲突。
(7分)
(2) 散列函数采用先将关键码各位数字折叠相加,再用 %hashSize将相加的结果映像到表中的办法。请指出每一个产生冲突的关键码可能产生多少次冲突。 (8分)
六、设一棵二叉树的结点定义为
struct BinTreeNode {
ElemType data;
BinTreeNode *leftChild, *rightChild;
}
现采用输入广义表表示建立二叉树。具体规定如下:
树的根结点作为由子树构成的表的表名放在表的最前面;
(2) 每个结点的左子树和右子树用逗号隔开。若仅有右子树没有左子树,逗号不能省略。
(3) 在整个广义表表示输入的结尾加上一个特殊的符号(例如“#”)表示输入结束。
例如,对于如右图所示的二叉树,其广
义表表示为 A(B(D, E(G, ) ), C( , F) )。
此算法的基本思路是:依次从保存广义
表的字符串ls中输入每个字符。若遇到的是
字母 (假定以字母作为结点的值),则表示是
结点的值,应为它建立一个新的结点,并把
该结点作为作为左子女(当k=0)或右子女(当k=1)链接到其双亲结点上。若遇到的是左括号”(”,则表明子表的开始,将k置为0;若遇到的是右括号”)”,则表明子表结束。若遇到的是逗号”,”,则表示以左子女为根的子树处理完毕,应接着处理以右子女为根的子树,将k置为1。
在算法中使用了一个栈s,在进入子表之前,将根结点指针进栈,以便括号内的子女链接之用。在子表处理结束时退栈。相关的栈操作如下:
MakeEmpty(s) 置空栈
Push(s, p) 元素p进栈
Pop(s) 退栈
Top(s) 存取栈顶元素的函数
下面给出了建立二叉树的算法,其中有5个语句缺失。请阅读此算法并把缺失的语句补上。 (每空3分)
void CreateBinTree(BinTreeNode *& BT, char ls) {
Stack<BinTreeNode *> s; MakeEmpty(s) ;
BT = NULL; //置空二叉树
BinTreeNode *p;
int k;
instream ins(ls); //把串ls定义为输入字符串流对象ins
char ch;
ins >> ch; //从ins顺序读入一个字符
while (ch != ‘#’) { //逐个字符处理,直到遇到‘#’为止
switch (ch) {
case ‘(’ : push(s, p);
k=1;
break;
case ‘)’ : pop(s);
break;
case ‘,’ : k=2;
break;
default : p = new BinTreeNode;
p->data = ch;
p->leftChild = NULL;
p->rightChild = NULL;
if ( BT == NULL )
Bt = p;
else if (k==1) top(s)->leftChild = p;
else top(s)->rightChild = p;
}
ins >> ch;
}
}
七、下面是一个用C编写的快速排序算法。为了避免最坏情况,取基准记录pivot采用从left, right和mid = (( left + right ) / 2( 中取中间值,并交换到right位置的办法。数组a存放待排序的一组记录,数据类型为Type,left和right是待排序子区间的最左端点和最右端点。
void quicksort (Type a[ ], int left, int right) {
Type temp;
if (left < right) {
Type pivot = median3 (a, left, right); //三者取中子程序
int i = left, j = right-1;
for ( ; ; ) {
while (i < j && a[i] < pivot) i++;
while (i < j && pivot < a[j]) j--;
if (i < j) {
temp = a[i]; a[j] = a[i]; a[i] = temp;
i++; j--;
}
else break;
}
if ( a[i] > pivot )
{ temp = a[i]; a[i] = a[right]; a[right] = temp; }
quicksort (a, left, i-1); //递归排序左子区间
quicksort (a, i+1, right); //递归排序右子区间
}
}
(1) 用C或Pascal实现三者取中子程序median3(a, left, right);
(5分)
改写quicksort算法,不用栈消去第二个递归调用
quicksort (a, i+1, right); (5分)
(3) 继续改写quicksort算法,用栈消去剩下的递归调用。 (5分)
一、解答
(1) 对于union(i, j),以i作为j的双亲 (5分)
(2) 按i和j为根的树的高度实现union(i, j),高度大者为高度小者的双亲; (5分)
(3) 按i和j为根的树的结点个数实现union(i, j),结点个数大者为结点个数小者的双亲。 (5分)
二、解答:将下图改为无向图,城市为结点,桥为边:
(1) 此为欧拉问题。有解的充要条件是每一个结点的度为偶数。 (5分)
(2) 数据结构采用邻接表,每个边结点有3个域,分别记录相关边号、邻接顶点号和链指针。
另外,设置一个边的访问标志数组visited[ ],当visited[i] == 0表示第i条边未访问过,一旦访问了第i条边,即将visited[i]改为1。
struct edgenode { //顶点结点
int edgeno; //相关边号
int adjvertex; //邻接顶点号
edgenode * link; //链接指针
};
struct vertex { //顶点
int city; //顶点数据
edgenode * first; //出边表指针
};
Typedef vertex * nodelist; //邻接表数组
下面给出按深度优先搜索方法求解欧拉问题的递归算法。 (5分)
void dfs ( nodelist euler, int start, int n, int visited[ ] ) {
cout << euler[start].city << ‘ -> ’; //输出顶点数据
edgenode * p = euler[start].first; //找第一条关联的边
while ( p != NULL ) { //有
int j = p->edgeno; //边号
if ( visited[j] == 0 ) { //未走过
visited[j] = 1; //作边访问标记
dfs ( euler, p->adjvertex, n, visited ); //按邻接顶点递归下去
}
p = p->link; //查下一条关联的边
}
}
主程序 (5分)
void main {
int count, n, i, j, k;
cout << “输入顶点数 :” ; cin >> n;
nodelist euler= new vertex[n]; //创建邻接表数组
for ( int i = 0; i < n; i++) { //输入顶点数据, 指针置空
cin >> euler[i].city; euler[i].first = NULL;
}
cout << “输入各条边!” << endl; //输入各边数据
count = 0; //边计数
cin >> i >> j >> k >>; //i是边号码,j, k是顶点
while ( i != 0 ) { //i为0, 表示输入结束
edgenode * p = new edgenode; //链入第j号链表
p->edgeno = i; p->adjvertex = k;
p->link = euler[j].first; euler[j].first = p;
edgenode * p = new edgenode; //链入第k号链表
p->edgeno = i; p->adjvertex = j;
p->link = euler[k].first; euler[k].first = p;
cin >> i >> j >> k >>;
count++;
}
int *visited = new int[count]; //创建访问标志数组
for ( i = 0; i < count; i++) visited[i] = 0;
for ( i = 0; i < n; i++ ) { //判断各个顶点的度是否偶数
count = 0;
p = euler[start].first; //找第一条关联的边
while ( p != NULL ) { //有
count++; p = p->link; //查下一条关联的边;
} //计算该顶点的度
if ( count % 2 != 0 )
{ cout << “顶点的度不为偶数, 此题无解!” << endl; exit(1); }
}
dfs ( euler, 0, n, visited ); //从0号顶点开始
delete [ ] visited;
}
三、解答
输入的n个数据全部有序: (5分)
n个数据全部有序时,如
{10 20 30 40 50 60 70 80}
{10} {20} {30} {40} {50} {60} {70} {80} 第一趟比较4次,移动8次
{10 20} {30 40} {50 60} {70 80} 第二趟比较4次,移动8次
{10 20 30 40} {50 60 70 80} 第三趟比较4次,移动8次
20 30 40 50 60 70 80}
一般地,n个数据全部有序时,每一趟数据比较 (n/2( 次,移动n次,共 (log2n( 趟,总数据比较次数与移动次数为O(nlog2n)次。
(2) 输入的n个数据全部逆向有序; (5分)
n个数据全部逆向有序时,如
{80 70 60 50 40 30 20 10}
{80} {70} {60} {50} {40} {30} {20} {10} 第一趟比较4次,移动8次
{70 80} {50 60} {30 40} {10 20} 第二趟比较4次,移动8次
{50 60 70 80} {10 20 30 40} 第三趟比较4次,移动8次
20 30 40 50 60 70 80}
一般地,n个数据全部逆序时,每一趟数据比较 (n/2( 次,移动n次,共 (log2n( 趟,总数据比较次数与移动次数为O(nlog2n)次。
(3) 随机的输入n个数据。 (5分)
随机输入n个数据时,如
{30 70 50 80 40 10 60 20}
{30} {70} {50} {80} {40} {10} {60} {20} 第一趟比较4次,移动8次
{30 70} {50 80} {10 40} {20 60} 第二趟比较6次,移动8次
{30 50 70 80} {10 20 40 60} 第三趟比较7次,移动8次
20 30 40 50 60 70 80}
一般地,n个数据随机输入时,每一趟数据比较n-1次,移动n次,共 (log2n( 趟,总数据比较次数与移动次数为O(nlog2n)次。
四、解答
有N个结点的AVL树的高度不超过
假设为表示k需要m位,则有
20 + 21 + … + 2m < k
2m+1 –1 < k ( m < log2(k+1) – 1 (5分)
(2) 若每一个结点中的高度计数器有m = 8,有 9 < log2(k+1),那么这样的AVL树可以有h = 29 -1层,最少有Fh+3-1个关键码? (5分)
五、解答:
设散列表大小hashSize = 13,其下标从0到12,采用线性探查法解决冲突。则对于以下关键码:
10 100 32 45 58 126 3 29 200 400 0
散列函数 hash(key) = key % hashSize
hash(10) = 10 % 13 = 10 hash(100) = 100 % 13 = 9
hash(32) = 32 % 13 = 6 hash(45) = 45 % 13 = 6 (冲突) = 7
hash(58) = 58 % 13 = 6 (冲突) = 7 (冲突) = 8
hash(126) = 126 % 13 = 9 (冲突) = 10 (冲突) = 11
hash(3) = 3 % 13 = 3 hash(29) = 29 %13 = 3 (冲突) =4
hash(200) = 200 % 13 = 5 hash(400) = 10 (冲突) = 11(冲突) = 12
hash(0) = 0 % 13 = 0
一次冲突的有45,29;二次冲突的有58,126,400。 (7分)
(2) 散列函数采用先将关键码各位数字折叠相加,再用 %hashSize将相加的结果映像到表中的办法。
关键码: 10 100 32 45 58 126 3 29 200 400 0
hash(10) = (1+0) % 13 = 1, hash(100) = (1+0+0) % 13 = 1 (冲突) = 2,
hash(32) = (3+2) % 13 = 5, hash(45) = (4+5) % 13 = 9,
hash(58) = (5+8) % 13 = 0, hash(126) = (1+2+6) % 13 = 9 (冲突) = 10,
hash(3) = 3 % 13 = 3, hash(29) = (2+9) % 13 = 11,
hash(200) = (2+0+0) % 13 = 2 (冲突) = 3 (冲突) = 4,
hash(400) = (4+0+0) % 13 = 4 (冲突) = 5 (冲突) = 6,
hash(0) = 0 (冲突) = 1 (冲突) = 2 (冲突) = … = 6 (冲突) = 7.
一次冲突的有100,126;二次冲突的有200,400;冲突7次的有0。 (8分)
六、解答 ① push(s, p); ② k=2; (每空3分)
③ p->data = ch; ④ Bt = p;
⑤ ins >> ch;
七解答:
(1) 三者取中子程序median3(a, left, right) 取基准记录pivot采用从left, right和mid = (( left + right ) / 2( 中取中间值,并交换到right位置的办法。 (5分)
void median3( Type a[ ], int left, int right ) {
int mid = ( left + right + 1 ) / 2;
int k = left;
if ( a[mid] < a[k] ) k = mid;
if ( a[high] < a[k] ) k = high; //选最小记录
Type temp = a[k]; a[k] = a[left]; a[left] = temp; //最小者交换到left
if ( a[mid] < a[right] )
{ temp = a[mid]; a[mid] = a[right]; a[right] = temp; }
}
(2) 消去第二个递归调用quicksort (a, i+1, right)。采用循环的办法。(5分)
void quicksort (Type a[ ], int left, int right) {
Type temp; int i, j;
while (left < right) {
Type pivot = median3 (a, left, right); //三者取中子程序
i = left; j = right-1;
for ( ; ; ) {
while (i < j && a[i] < pivot) i++;
while (i < j && pivot < a[j]) j--;
if (i < j) {
temp = a[i]; a[j] = a[i]; a[i] = temp;
i++; j--;
}
else break;
}
if ( a[i] > pivot ) { a[right] = a[i]; a[i] = pivot; }
quicksort (a, left, i -1); //递归排序左子区间
left = i+1;
}
}
(3) 继续改写quicksort算法,消去剩下的递归调用。 (5分)
为实现非递归算法,需要用到一个栈s,暂存右半个区间。栈结点定义如下。
struct stkNode {
int low; //区间左端点
int high; //区间右端点
}
void quicksort (Type a[ ], int n ) {
stack<stkNode> s; stkNode w; Type temp;
left = 0; right = w.low = w.high = n-1; push ( s, w );
while (left < right) {
Type pivot = median3 (a, left, right); //三者取中子程序
int i = left, j = right-1;
for ( ; ; ) {
while (i < j && a[i] < pivot) i++;
while (i < j && pivot < a[j]) j--;
if (i < j) {
temp = a[i]; a[j] = a[i]; a[i] = temp;
i++; j--;
}
else break;
}
temp = a[i]; a[i] = a[right]; a[right] = temp;
if ( left < i-1 ) { w.low = left; w.high = i-1; push(s, w); }
//左区间存在不止一个元素,进栈
if (i+1 < right) left = i+1; //右区间存在不止一个元素,排序右区间
else if ( !IsEmpty(s) ) //否则退栈,取保存的待排序区间
{ w = top(s); pop(s); left = w.low; right = w.high }
}
}
(5分)