2025-2026学年第一学期数据结构真题解析

2026年7月10日 Crystal-Sky 14 min read 数据结构算法期末考试
下载 PDF
Abstract. 本文主要记录2025-2026学年第一学期数据结构真题,有一个题目没有进行录入,是leetcode原题补全链表归并排序的合并代码。

时间复杂度分析

题目 1

分析下列递归函数的时间复杂度:

int f(int m)
{
    if(m <= 1) return 1;
    else return f(m - 1) + f(m - 1) + f(m - 1);
}

题目 2

分析下列函数的时间复杂度:

int sub2(int m, int n)
{
    int i, j, t, r = 0;
    for (i = 0; i < n; i++)
        for (j = 0; j <= n; j = j + 2) {
            t = 1;
            while (t < m / 2 - 1) t = t * 2;
            r = r + t;
        }
    return r;
}

二叉排序树

已知一棵二叉排序树节点的数据为整数,其前序遍历序列如下:

\[ 60,\ 40,\ 35,\ 48,\ 54,\ 69,\ 82. \]

(1)画出该二叉排序树的逻辑结构

📐 TikZ 图表(请在 PDF 版本中查看完整图表)

(2)根据前序遍历序列构造二叉排序树的算法思路

多个栈共享连续存储空间

设有 $N(N>0)$ 个栈,其元素类型相同,在任何时刻这 $N$ 个栈的元素个数之和不超过 $M(M>0)$,要求采用一片连续的内存空间存储这 $N$ 个栈。请给出一种实现方法,并说明进栈、 出栈操作及时间复杂度。

关键路径

软件项目包含 7 个子任务:

子任务持续时间(天)开始条件
$a_1$2
$a_2$3
$a_3$2$a_1$ 完成
$a_4$3$a_1$ 完成
$a_5$3$a_2$ 完成
$a_6$3$a_3$ 完成
$a_7$3$a_4$ 与 $a_5$ 均完成

(1)分别缩短 $a_1,a_2,a_6$ 的持续时间,能否使项目提前完成?

(2)判断给定子任务持续时间缩短能否使项目提前完成的算法思路

算法设计一:Huffman 树权值序列

在 Huffman 树中,从根节点到叶节点路径上所经过节点(包括根和叶)的权值序列称为权值序列。 节点权值为正整数。

(1)判断下列两组序列能否属于同一棵 Huffman 树

第(1)组

\[ 36,20,11 \qquad\text{和}\qquad 36,16,12 \]

第(2)组

\[ 50,30,16,8 \qquad\text{和}\qquad 50,20,11,3 \]

第(3)组

\[ 36,20,11 \qquad\text{和}\qquad 36,20,9,4,3 \]

第(4)组

\[ 50,14,9 \qquad\text{和}\qquad 50,36,10,5 \]

(2)算法思路与 C/C++ 描述

算法设计二:删除最小堆堆顶并重排

$n$ 个盒子按 $1\sim n$ 编号,卡片满足:

\[ 2i\le n\Rightarrow b_i\le b_{2i}, \]
\[ 2i+1\le n\Rightarrow b_i\le b_{2i+1}. \]

拿走编号 1 的卡片后,将其余 $n-1$ 张卡片重排到 $1\sim n-1$,仍满足该规则, 并使翻看卡片标记值次数尽可能少。

(1)算法思路

(2)C/C++ 算法描述与复杂度

#include <bits/stdc++.h>
using namespace std;

// b[1..n] 初始满足小根堆性质
// 删除 b[1] 后,将堆规模变成 n-1
void deleteMin(vector<int>& b, int& n) {
    if (n <= 0) return;

    // 将最后一个元素移到堆顶
    b[1] = b[n];
    --n;

    int i = 1;

    while (2 * i <= n) {
        int child = 2 * i;  // 左孩子

        // 若右孩子存在,选择更小的孩子
        if (child + 1 <= n && b[child + 1] < b[child]) {
            ++child;
        }

        // 已满足小根堆性质
        if (b[i] <= b[child]) break;

        swap(b[i], b[child]);
        i = child;
    }
}

最短路问题:按时间上线的城市

共有 $N$ 座城市,编号 $0$ 到 $N-1$。城市 $i$ 在时间 $u_i$ 上线, 且

\[ u_0\le u_1\le\cdots\le u_{N-1}. \]

有 $M$ 条无向带权边。询问 $(a,b,t)$ 按 $t$ 非递减给出: 在第 $t$ 天,只允许经过已经上线的城市,求 $a$ 到 $b$ 的最短路; 若不可达或端点未上线,输出 $-1$。

算法思路

C++ 参考代码

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const ll INF = (1LL << 60);

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N, M;
    cin >> N >> M;

    vector<int> u(N);
    for (int i = 0; i < N; ++i) cin >> u[i];

    vector<vector<ll>> dist(N, vector<ll>(N, INF));
    for (int i = 0; i < N; ++i) dist[i][i] = 0;

    for (int e = 0; e < M; ++e) {
        int x, y;
        ll w;
        cin >> x >> y >> w;
        dist[x][y] = dist[y][x] = min(dist[x][y], w);
    }

    int Q;
    cin >> Q;

    int k = 0;  // [0, k-1] 已经被允许作为中间点

    while (Q--) {
        int a, b, t;
        cin >> a >> b >> t;

        // 按时间增量加入新上线城市
        while (k < N && u[k] <= t) {
            for (int i = 0; i < N; ++i) {
                if (dist[i][k] == INF) continue;

                for (int j = 0; j < N; ++j) {
                    if (dist[k][j] == INF) continue;

                    dist[i][j] = min(
                        dist[i][j],
                        dist[i][k] + dist[k][j]
                    );
                }
            }
            ++k;
        }

        // 源点或终点尚未上线
        if (u[a] > t || u[b] > t || dist[a][b] == INF) {
            cout << -1 << '\n';
        } else {
            cout << dist[a][b] << '\n';
        }
    }

    return 0;
}

复杂度分析

每个城市只会被加入一次。每加入一个城市 $k$,进行一次 $N^2$ 级别松弛, 所以总计:

\[ \boxed{O(N^3)}. \]

每个询问除增量更新外只需 $O(1)$ 输出判断,因此总时间复杂度为

\[ \boxed{O(N^3+Q)}. \]

距离矩阵空间复杂度为

\[ \boxed{O(N^2)}. \]

样例

输入:

4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4

输出:

-1
-1
5
4

总结

本卷主要考查以下知识点:

  1. 递归与多重循环的时间复杂度分析;
  2. 二叉排序树的构造与遍历;
  3. 多栈共享存储空间;
  4. AOE/DAG 中的关键路径与项目工期;
  5. Huffman 树权值关系与贪心合并思想;
  6. 小根堆删除堆顶后的向下调整;
  7. 按时间增量开放中间点的 Floyd 最短路。