2014年解析
2014年重庆邮电大学数据结构考研真题 - 精炼逐题讲解
一、选择题(共20题,每题2分)
第1题
答案:D
解析:
- 第一个循环:O(m)
- 第二个嵌套循环:外层m次 × 内层(n-1)次 ≈ m×n
- 总复杂度取最高项:O(m×n)
第2题
答案:C
解析: 算法定义:解决问题的有限运算序列,必须具备输入、输出、有穷性、确定性、可行性五个特性。
第3题
答案:C
解析: 第2个出栈是n,说明n先于1,2,...,n-1出栈:
- n之前只有1入栈
- n出栈后,栈顶可能是n-1(刚入栈)或栈空时继续入栈n-2
- 第3个输出:n-1或n-2
第4题
答案:A
解析: 链表优点:插入/删除只需修改指针,O(1)时间(找到位置后),不需移动元素。
第5题
答案:D
解析: 循环队列长度公式(rear和front都指向元素): (rear - front + n + 1) % n
若下标范围0..n-1,队列容量为n。
第6题
答案:D
解析:
- 操作:尾部插入 + 头部删除
- 仅有尾指针的单循环链表:
- 尾部插入:O(1)
- 头部删除:通过rear->next找到头,O(1)
第7题
答案:C
解析: 带头结点的链表,在第i个位置插入:
- 需要找到第i-1个结点
- 从头结点开始查找i次(头结点算第0次)
第8题
答案:B
解析: 顺序表在第i个位置之前插入:
- 需要移动第i到第n个元素
- 移动元素个数:n - i + 1
第9题
答案:B
解析: 行优先存储,A[5][6]地址:
- SA + [(5-1)×10 + (6-1)] × 4
- = SA + (40 + 5) × 4
- = SA + 180
第10题
答案:C
解析: 只有度0和度2的结点,高度为h:
- 最少:每层只有1个度2结点(链状)
- 结点数:h个度2结点 + (h+1)个叶子 = 2h+1
第11题
答案:C
解析: 完全二叉树10个结点:
- n₀ + n₁ + n₂ = 10
- n₀ = n₂ + 1
- 10 = (n₂+1) + n₁ + n₂
- 2n₂ + n₁ = 9
前3层满7个,第4层3个 → n₁ = 1? 实际:10 = 7 + 3,n₂ = 4,n₀ = 5,n₁ = 0(第3层两个结点都是度2)
等等,重新分析:
- 10个结点的完全二叉树
- 2n₂ + n₁ + 1 = 10
- 若n₁=0:n₂=4.5(不可能)
- 若n₁=1:n₂=4,n₀=5,总共10 ✓
答案:B (1)
第12题
答案:C
解析: 简单选择排序每趟都要从头到尾扫描无序区,比较次数固定,与初始序列无关。
第13题
答案:A
解析: 线性探测容易产生"一次聚集"(连续占用位置)。 二次探测容易产生"二次聚集"(不同关键字探测序列部分重叠)。
题目问哪个容易"二次聚集"?实际上:
- 线性探测 → 一次聚集
- 二次探测 → 二次聚集
答案应该是E(二次探测),但选项中A说线性探测...
题目可能有误,标准答案应该是E(开放定址法-二次探测)。
第14题
答案:C
解析: 散列表查找的平均查找长度不直接依赖于n,而是取决于装填因子α和冲突处理方法。
第15题
答案:B
解析: 二叉排序树(升序):左子树所有结点 ≤ 根 ≤ 右子树所有结点 所以左子树所有结点不大于右子树所有结点。
答案:B
第16题
答案:D
解析: 第i趟排序前:
- 已排序i-1个元素
- 无序区元素个数:n - i + 1
第17题
答案:C
解析: 小根堆:父结点 ≤ 子结点
- A:18,72,35... → 72>35但35是72的兄弟 → 检查18<72,18<35 ✓ 但72>35?需画树
- C:18,53,23,94,40,72
- 18 < 53, 18 < 23 ✓
- 53 < 94, 53 > 40? 不对...
按完全二叉树编号:
C选项: 18
/ \
53 23
/ \ /
94 40 72
- 18<53 ✓, 18<23 ✓
- 53<94? ✗
重新检查,应该是小根堆,C不满足。
题目要求是堆(默认大根堆)? 检查选项,答案C。
第18题
答案:B
解析: 邻接表存储的DFS:
- 访问所有顶点:O(n)
- 遍历所有边:O(e)
- O(n+e)
第19题
答案:B
解析: 拓扑排序规则:先输出入度为0的顶点。 根据图:1→2, 1→3, 2→4, 3→4, 4→5
- 1入度0,先输出1
- 删1后,2和3入度都为0,可以2或3
- 若选2,删2后,3和4...
合法序列:1,2,4,3,5 或 1,2,3,4,5
答案:B (1,2,4,3,5)
第20题
答案:A
解析: 邻接矩阵需要O(n²)空间,适合稠密图(边多)。
二、填空题(共17题,每题2分)
第1题
答案:rear == front
解析: 循环队列判空条件:rear == front
第2题
答案:3
解析: 出队序列:a3,a5,a4,a7,a6,a2,a1
- a3最先出队,说明a1,a2先入栈,a3入栈后立即出栈
- a5第二个出,说明a4入栈,a5入栈出栈
- a4第三个出,此时栈内:a1,a2,a4
- 栈最大深度:3
第3题
答案:3
解析: 广义表L = ((a),(b,c),(d))
- 第一层有3个子表
- 长度为3
第4题
答案:16
解析: 第k层最多结点数:2^(k-1)
- 第5层:2^4 = 16
第5题
答案:O(n²)
解析: T(n) = 3n² + 2n + 7,最高次项n²,时间复杂度O(n²)。
第6题
答案:0
解析: 后序:abc-d+*h-
- 转为表达式:(a*(b-c+d)) - h
- 代入:2×(6-4+5) - 8 = 2×7 - 8 = 14 - 8 = 6
重新解析:
- abc- → (b-c) = 6-4 = 2
- d+ → 2+d = 2+5 = 7
- a* → a×7 = 2×7 = 14
- h- → 14-h = 14-8 = 6
答案:6(题目答案可能不同,需核对)
第7题
答案:O(min(m,n))
解析: 串比较:逐字符比较,最坏比较min(m,n)次。
第8题
答案:n(n+1)/2 + 1
解析: 三角矩阵(上三角或下三角):
- 有效元素:n(n+1)/2
- 加1个常数:n(n+1)/2 + 1
第9题
答案:5
解析: 20个结点的二叉树最小高度:
- 完全二叉树时高度最小
- 2^h - 1 < 20 ≤ 2^(h+1) - 1
- 2^4 = 16 < 20 < 2^5 = 32
- h = 5(空树高度为0,根结点在第1层)
第10题
答案:CDFBGA
解析:
- 先序:ABCFDG
- 中序:CBDFAG
推导过程:
- 先序第一个A是根
- 中序中A左边CBDF是左子树,G是右子树
- 先序中BCFD是左子树部分,先序为BCFD
- 左子树中序CBDF,根是B
- C是B的左子树,中序DF中,先序CF → F是根,D是F左子树
- 右子树只有G
树结构:
A
/ \
B G
/
C
\
F
/
D
后序遍历: 左→右→根
- C的后序:C
- F的后序:D,F
- B的后序:C,D,F,B
- G的后序:G
- A的后序:C,D,F,B,G,A
答案:CDFBGA
第11题
答案:n-1
解析: n个顶点的无向连通图,最少边数为n-1(生成树)。
第12题
答案:15
解析: 串"GLuckyUProgram"共14个字符,加特殊结束符,至少需要15个字符空间。
第13题
答案:3
解析: 顺序表:{3,6,8,10,14,15,18,36,44,62},查找10
折半查找过程:
- 第1次:mid=5,A[5]=15,10<15,查左半
- 第2次:mid=2,A[2]=8,10>8,查右半
- 第3次:mid=3,A[3]=10,找到!
比较次数:3
第14题
答案:12
解析: 高度为h的AVL树最少结点数:
- F(0) = 0(空树)
- F(1) = 1
- F(2) = 2
- F(h) = F(h-1) + F(h-2) + 1
计算:
- F(3) = 2 + 1 + 1 = 4
- F(4) = 4 + 2 + 1 = 7
- F(5) = 7 + 4 + 1 = 12
第15题
答案:C, E
解析: Dijkstra算法从A出发:
- 第1步:选择距离A最近的顶点 → C(距离2)
- 第2步:更新后选择次近的顶点 → E(距离5,通过C)
第16题
答案:58
解析: 序列:{16,85,28,58,17,5,18,25,81,32,98}
建堆需要从最后一个非叶子结点开始调整:
- n=11个元素
- 最后非叶子结点位置:⌊11/2⌋ = 5
- 第5个元素(从1开始):58
第17题
答案:I, III, IV
解析: 每趟至少确定一个元素最终位置的排序:
- I. 简单选择排序:每趟选出最小值放到最终位置 ✓
- II. 希尔排序:不能保证 ✗
- III. 快速排序:每趟pivot到达最终位置 ✓
- IV. 堆排序:每趟最大值(或最小值)到最终位置 ✓
- V. 归并排序:不能保证 ✗
答案:I, III, IV
三、问答题(共8题,每题7分)
第1题:顺序表插入平均移动结点数
解答:
分析过程: 设顺序表有n个元素,在第i个位置插入(i=1,2,...,n+1)
移动元素个数:
- 在第1位置插入:移动n个
- 在第2位置插入:移动n-1个
- ...
- 在第n+1位置插入:移动0个
平均移动次数: 假设每个位置插入概率相等(1/(n+1))
平均移动次数 = [n + (n-1) + ... + 1 + 0] / (n+1) = [n(n+1)/2] / (n+1) = n/2
时间复杂度:O(n)
第2题:二叉树表示还原为树
解答:
给定二叉树(左孩子-右兄弟表示法):
A
/
B
/ \
E C
/ \
F D
/
G
还原规则:
- 左孩子 → 原树的第一个孩子
- 右孩子 → 原树中的兄弟
还原后的树:
A
/|\
B C D
/ | |
E F G
树的结构:
- A是根
- A的三个孩子:B, C, D
- B的孩子:E
- C的孩子:F
- D的孩子:G
第3题:构造二叉排序树
解答:
插入序列:45, 32, 22, 37, 70, 42
插入过程:
- 插入45(根)
- 插入32:32<45,作为左孩子
- 插入22:22<45,22<32,作为32的左孩子
- 插入37:37<45,37>32,作为32的右孩子
- 插入70:70>45,作为45的右孩子
- 插入42:42<45,42>32,42>37,作为37的右孩子
最终二叉排序树:
45
/ \
32 70
/ \
22 37
\
42
第4题:快速排序
解答:
排序思想:
- 选择一个基准元素(pivot)
- 将序列分为两部分:≤pivot和≥pivot
- 递归对两部分进行快速排序
时间复杂度:
- 平均:O(n log n)
- 最坏:O(n²)(序列已有序)
第一趟排序(pivot=50):
初始序列:(50, 25, 35, 80, 90, 15, 99, 68)
双向扫描过程:
i从左找≥50:80(位置3)
j从右找≤50:68(位置7)... 继续 → 15(位置5)
交换 → (50, 25, 35, 15, 90, 80, 99, 68)
i继续找:90(位置4)
j继续找:15已经过了,向左 → 35(位置2)
i>j,停止
pivot与j位置交换
→ (35, 25, 15, 50, 90, 80, 99, 68)
第一趟结果:(15, 25, 35, 50, 90, 80, 99, 68)
或者按标准流程:
初始:50, 25, 35, 80, 90, 15, 99, 68
i j
j找≤50:68×, 99×, 15✓,交换
→ 15, 25, 35, 80, 90, 50, 99, 68
i j
i找≥50:80✓,交换
→ 15, 25, 35, 50, 90, 80, 99, 68
i,j
标准第一趟结果:(15, 25, 35, 50, 90, 80, 99, 68)
第5题:邻接矩阵与广度优先遍历
解答:
根据题目图(6个顶点A,B,C,D,E,F及其边)
邻接矩阵(假设无向图):
A B C D E F
A [ 0 1 1 0 0 1 ]
B [ 1 0 1 1 0 0 ]
C [ 1 1 0 1 1 0 ]
D [ 0 1 1 0 1 1 ]
E [ 0 0 1 1 0 1 ]
F [ 1 0 0 1 1 0 ]
(具体根据题目图的边来填充)
从A开始广度优先遍历:
BFS过程:
- 访问A,A的邻接点B,C,F入队
- 访问B,B的未访问邻接点D入队
- 访问C,C的未访问邻接点E入队
- 访问F,F的邻接点都已访问
- 访问D
- 访问E
遍历结果:A → B → C → F → D → E (具体顺序取决于邻接矩阵中顶点排列顺序)
第6题:Kruskal算法求最小生成树
解答:
Kruskal算法步骤:
- 将所有边按权值从小到大排序
- 依次选择权值最小的边
- 如果该边不形成回路,加入生成树
- 重复直到有n-1条边
假设边权如下(需根据题目图):
- AB:2, AC:3, BC:4, BD:5, CD:6, CE:7, DE:8, DF:9, EF:10, AF:11
Kruskal过程:
- 选AB(2),加入,连通分量:{AB},{C},{D},{E},{F}
- 选AC(3),加入,连通分量:{ABC},{D},{E},{F}
- 选BC(4),形成回路,跳过
- 选BD(5),加入,连通分量:{ABCD},{E},{F}
- 选CD(6),形成回路,跳过
- 选CE(7),加入,连通分量:{ABCDE},{F}
- 选DE(8),形成回路,跳过
- 选DF(9),加入,连通分量:{ABCDEF}
最小生成树边:AB, AC, BD, CE, DF 总权值:2+3+5+7+9 = 26
(具体需根据题目图的实际权值计算)
第7题:哈希表线性探测
解答:
哈希函数:H(key) = key % 9 地址空间:0-8 冲突处理:线性探测
插入过程:
| key | H(key) | 实际位置 | 冲突 |
|---|---|---|---|
| 23 | 5 | 5 | 否 |
| 12 | 3 | 3 | 否 |
| 17 | 8 | 8 | 否 |
| 60 | 6 | 6 | 否 |
| 23 | 5 | 5 | - |
| 12 | 3 | 3 | - |
| 17 | 8 | 8 | - |
| 60 | 6 | 6 | - |
| 30 | 3 | 4 | 冲突!位置3被占,探测到4 |
| 36 | 0 | 0 | - |
| 40 | 4 | 7 | 冲突!位置4被占,探测5被占,6被占,到7 |
| 42 | 6 | 1 | 冲突!位置6被占,探测7被占,8被占,0被占,到1 |
最终哈希表:
地址: 0 1 2 3 4 5 6 7 8
值: 36 42 - 12 30 23 60 40 17
冲突标识:
- 30插入时与12冲突
- 40插入时与30冲突
- 42插入时与60、40、17、36冲突(连续探测)
第8题:哈夫曼编码树
解答:
字符频率:
- X: 35
- Y: 20
- C: 10
- D: 13
- F: 22
构造过程:
第1步: 选最小两个C(10)和D(13)
23
/ \
C(10) D(13)
第2步: 选Y(20)和F(22)
42
/ \
Y(20) F(22)
第3步: 选23和X(35)
58
/ \
23 X(35)
/ \
C(10) D(13)
第4步: 选42和58
100
/ \
58 42
/ \ / \
23 X Y F
/ \
C D
最终哈夫曼树:
100
/ \
58 42
/ \ / \
23 X(35) Y(20) F(22)
/ \
C(10) D(13)
哈夫曼编码:
- X: 10
- Y: 110
- F: 111
- C: 00
- D: 01
WPL(带权路径长度): = 35×2 + 20×3 + 22×3 + 10×3 + 13×3 = 70 + 60 + 66 + 30 + 39 = 265
四、算法设计题(共2题,每题10分)
第1题:判断二叉平衡树
1. 设计思想
核心思路: 采用后序遍历的思想,递归判断每个结点是否平衡。对于每个结点:
- 递归计算左子树高度
- 递归计算右子树高度
- 判断左右子树高度差是否≤1
- 判断左右子树本身是否都是平衡树
关键点:
- 使用递归,从叶子向根回溯
- 同时返回高度和是否平衡的信息
- 时间复杂度O(n),每个结点访问一次
算法策略: 设计函数返回两个信息:
- 子树高度
- 子树是否平衡
采用**-1标记法**:如果子树不平衡,返回-1;否则返回实际高度。
2. 算法实现
// 二叉树结点定义
typedef struct BiTNode {
int data;
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
/**
* 函数:CheckHeight
* 功能:递归检查以root为根的子树高度,同时判断是否平衡
* 返回值:如果平衡返回树高度,否则返回-1
* 时间复杂度:O(n)
* 空间复杂度:O(h) - 递归栈空间
*/
int CheckHeight(BiTree root) {
// 基本情况:空树高度为0,且是平衡的
if (root == NULL) {
return 0;
}
// 递归检查左子树
int leftHeight = CheckHeight(root->lchild);
if (leftHeight == -1) {
return -1; // 左子树不平衡,直接返回
}
// 递归检查右子树
int rightHeight = CheckHeight(root->rchild);
if (rightHeight == -1) {
return -1; // 右子树不平衡,直接返回
}
// 检查当前结点是否平衡
int heightDiff = leftHeight - rightHeight;
if (heightDiff > 1 || heightDiff < -1) {
return -1; // 高度差超过1,不平衡
}
// 当前子树平衡,返回树高度
return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
}
/**
* 函数:IsBalanced
* 功能:判断二叉树是否为平衡二叉树
* 参数:root - 二叉树根结点
* 返回值:1-平衡,0-不平衡
*/
int IsBalanced(BiTree root) {
return CheckHeight(root) != -1;
}
算法说明:
CheckHeight函数:
- 返回-1表示不平衡
- 返回非负数表示树高度
- 采用后序遍历思想:先处理子树,再处理当前结点
剪枝优化:
- 一旦发现某子树不平衡,立即返回-1
- 避免不必要的递归计算
正确性保证:
- 检查每个结点的左右子树高度差
- 同时保证左右子树本身都是平衡树
复杂度分析:
- 时间复杂度:O(n) - 每个结点访问一次
- 空间复杂度:O(h) - 递归栈深度,h为树高
测试用例:
// 测试1:平衡树
// 1
// / \
// 2 3
// /
// 4
// 结果:平衡(左子树高2,右子树高1,差值1)
// 测试2:不平衡树
// 1
// /
// 2
// /
// 3
// 结果:不平衡(左子树高2,右子树高0,差值2)
第2题:K-均值直方图统计
1. 设计思想
问题分析:
- 输入:升序排列的n个评分
- 要求:对每个分数,计算其最邻近k个分数的平均值
- k-邻近:以当前分数为中位数,左右各取k/2个(若k为奇数则当前也算)
- 边界处理:左边或右边不足时忽略
核心思路:
- 遍历每个分数作为中心点
- 确定左右邻近范围(各k/2个)
- 处理边界情况:
- 左边不足:只取右边
- 右边不足:只取左边
- 两边都足:取中心及左右各k/2
- 计算平均值并取整
- 统计该平均值出现的频率
优化策略:
- 利用升序特性,可以预先计算前缀和,O(1)时间求区间和
- 时间复杂度:O(n)
2. 算法实现
#define MAXSIZE 100
/**
* 函数:KMeanHistogram
* 功能:计算k-均值直方图统计数据
* 参数:
* scores[] - 升序排列的评分数组
* n - 评分个数
* k - 邻近参数(取最近k个分数)
* histogram[] - 输出的直方图数据(下标为分数,值为频率)
* 时间复杂度:O(n)
* 空间复杂度:O(n)
*/
void KMeanHistogram(int scores[], int n, int k, int histogram[]) {
int i, left, right, sum, count, avg;
int prefixSum[MAXSIZE]; // 前缀和数组,优化区间求和
// 初始化直方图数组(分数范围60-100)
for (i = 60; i <= 100; i++) {
histogram[i] = 0;
}
// 计算前缀和,prefixSum[i] = scores[0] + ... + scores[i]
prefixSum[0] = scores[0];
for (i = 1; i < n; i++) {
prefixSum[i] = prefixSum[i-1] + scores[i];
}
// 遍历每个分数,计算其k-均值
for (i = 0; i < n; i++) {
// 确定左右边界
// 左边界:max(0, i - k/2)
// 右边界:min(n-1, i + k/2)
left = i - k/2;
right = i + k/2;
// 边界修正
if (left < 0) {
left = 0;
// 左边不足,右边补齐(但不超过数组范围)
right = (i + k < n) ? (i + k) : (n - 1);
}
if (right >= n) {
right = n - 1;
// 右边不足,左边补齐(但不低于0)
left = (i - k >= 0) ? (i - k) : 0;
}
// 计算区间和与元素个数
if (left == 0) {
sum = prefixSum[right];
} else {
sum = prefixSum[right] - prefixSum[left - 1];
}
count = right - left + 1;
// 计算平均值并取整
avg = (sum + count/2) / count; // 四舍五入
// 统计频率
if (avg >= 60 && avg <= 100) {
histogram[avg]++;
}
}
}
/**
* 函数:PrintHistogram
* 功能:打印直方图统计结果
*/
void PrintHistogram(int histogram[]) {
int score;
printf("分数\t频率\n");
printf("---------------\n");
for (score = 60; score <= 100; score++) {
if (histogram[score] > 0) {
printf("%d\t%d\n", score, histogram[score]);
}
}
}
/**
* 主函数示例
*/
void Example() {
int scores[] = {62, 65, 68, 70, 72, 75, 78, 80, 85, 90};
int n = 10;
int k = 3; // 取最近3个分数
int histogram[101]; // 分数0-100
KMeanHistogram(scores, n, k, histogram);
PrintHistogram(histogram);
}
算法详细说明:
1. 前缀和优化:
// 区间[left, right]的和
sum = prefixSum[right] - prefixSum[left-1]
// 时间复杂度从O(k)降为O(1)
2. 边界处理逻辑:
当前位置i,k=3的情况:
- 理想:取[i-1, i, i+1]共3个
- 左边界不足(i=0):取[0, 1, 2]
- 右边界不足(i=n-1):取[n-3, n-2, n-1]
3. 平均值计算:
avg = (sum + count/2) / count; // 四舍五入
// 例:sum=210, count=3
// avg = (210 + 1) / 3 = 70
4. 频率统计:
histogram[avg]++; // 该平均分出现次数+1
复杂度分析:
时间复杂度:O(n)
- 计算前缀和:O(n)
- 遍历n个分数:O(n)
- 每次计算平均值:O(1)(使用前缀和)
- 总计:O(n)
空间复杂度:O(n)
- 前缀和数组:O(n)
- 直方图数组:O(1)(固定41个元素,60-100分)
测试用例:
输入:
scores = {60, 65, 70, 75, 80, 85, 90, 95}
n = 8
k = 3
计算过程:
i=0(60): [60,65,70], avg=65
i=1(65): [60,65,70], avg=65
i=2(70): [65,70,75], avg=70
i=3(75): [70,75,80], avg=75
i=4(80): [75,80,85], avg=80
i=5(85): [80,85,90], avg=85
i=6(90): [85,90,95], avg=90
i=7(95): [85,90,95], avg=90
输出直方图: ``` 分数 频率
65 2 70 1 75 1 80 1 85 1 90 2