算法简答题及计算题

1. 程序题

题目

给定两个字符串a和b,c同是a和b的子序列,称字符串c是a和b的公共子序列,求a和b的最长公共子序列

题解

解法 1:二维 DP 标准模板

点击查看Java代码
class Solution {
    /**
     * 动态规划求解最长公共子序列长度
     * @param text1 字符串a
     * @param text2 字符串b
     * @return 最长公共子序列长度
     */
    public int longestCommonSubsequence(String text1, String text2) {
        // m:text1长度,n:text2长度
        int m = text1.length();
        int n = text2.length();

        // dp[i][j]:text1前i个字符、text2前j个字符的最长公共子序列长度
        // 下标0代表空字符串,方便边界处理
        int[][] dp = new int[m + 1][n + 1];

        // 遍历text1所有字符
        for (int i = 1; i <= m; i++) {
            // 遍历text2所有字符
            for (int j = 1; j <= n; j++) {
                // 当前两个字符相等,公共子序列长度+1
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    // 字符不等:舍弃text1当前字符 / 舍弃text2当前字符,取较大值
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        // dp[m][n] 即为两完整字符串的LCS长度
        return dp[m][n];
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        // 示例1:text1 = "abcde", text2 = "ace" → 输出3
        System.out.println(sol.longestCommonSubsequence("abcde", "ace"));
        // 示例2:text1 = "abc", text2 = "def" → 输出0
        System.out.println(sol.longestCommonSubsequence("abc", "def"));
    }
}

解法 2:空间优化一维 DP

点击查看Java代码
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        // 保证text2是更短字符串,减少数组长度
        if (text1.length() < text2.length()) {
            return longestCommonSubsequence(text2, text1);
        }
        int m = text1.length();
        int n = text2.length();
        int[] dp = new int[n + 1];

        for (int i = 1; i <= m; i++) {
            // 保存dp[i-1][j-1],避免覆盖丢失
            int prev = 0;
            for (int j = 1; j <= n; j++) {
                int temp = dp[j];
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[j] = prev + 1;
                } else {
                    dp[j] = Math.max(dp[j], dp[j - 1]);
                }
                prev = temp;
            }
        }
        return dp[n];
    }
}

输出完整最长公共子序列字符串

点击查看Java代码
import java.util.Deque;
import java.util.LinkedList;

class Solution {
    public String getLCS(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        int[][] dp = new int[m + 1][n + 1];

        // 填充DP表
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }

        // 反向回溯获取字符
        Deque<Character> stack = new LinkedList<>();
        int i = m, j = n;
        while (i > 0 && j > 0) {
            if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                stack.push(text1.charAt(i - 1));
                i--;
                j--;
            } else {
                // 向数值大的方向移动
                if (dp[i - 1][j] > dp[i][j - 1]) i--;
                else j--;
            }
        }

        // 拼接结果
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) sb.append(stack.pop());
        return sb.toString();
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        System.out.println(sol.getLCS("abcde", "ace")); // ace
    }
}

2. 计算题

2.1

img

题解

2.2

img

题解

2.3

img

题解

2.4

img

题解

2.5

img

题解

2.6

img

题解

2.7

img

题解

2.8

img

题解

2.9

img

题解

2.10

img

题解

2.11

img

题解

2.12

img

题解

2.13

img

题解

2.14

img

题解

2.15

img

题解

3. 简答题

3.1

计算机编程语言都提供了随机函数random(或类似的名称)产生0~1之间的随机数。请查阅资料,看看C、C++、C#、Java、Python等语言中,都使用(过)哪些算法实现随机函数。要求查阅、对不少于3种语言,且必须含Python

答:

  1. C语言中,随机函数rand()是基于线性同余算法实现的,该算法通过递推公式生成伪随机数序列。
  2. C++中,随机函数rand()同样使用线性同余算法,但C++11标准引入了头文件,提供了更复杂的随机数生成算法,如梅森旋转算法。
  3. Python中,随机函数random()是基于梅森旋转算法实现的,该算法具有较长的周期和较好的随机性。

3.2

简述算法的定义及其在计算机科学中的重要性。

答:

算法是解决特定问题的一系列明确指令的有限步骤。在计算机科学中,算法是程序设计的核心,决定了程序的效率和正确性。

3.3

什么是算法时间复杂度,什么是算法空间复杂度,什么是算法最坏时间复杂度,什么是算法最好时间复杂度,什么是算法平均时间复杂度

答:

  1. 时间复杂度:衡量算法运行时间随输入规模 n 增长的变化趋势,只保留最高阶项,忽略常数与低阶项,用于评估算法运行效率。
  2. 空间复杂度:衡量算法执行过程中,额外占用的存储空间随输入规模 n 的增长趋势,包含临时变量、递归栈、辅助数组等额外空间,不统计输入本身占用空间。
  3. 最坏时间复杂度:输入为最不利于算法执行的极端数据时,算法运行时间的增长阶,是算法性能上限,工程上最常用作性能评估依据。
  4. 最好时间复杂度:输入为最优数据时,算法运行时间的增长阶,仅代表理想情况,无实际参考价值。
  5. 平均时间复杂度:所有合法输入下,算法运行时间的数学期望,综合反映算法整体平均性能。

3.4

请简述递归算法的基本思想,并举例说明。

答:

递归算法的基本思想是将一个问题分解为若干个相似的子问题,通过递归调用解决这些子问题,最终得到原问题的解。例如,计算阶乘的递归算法:factorial(n) = n * factorial(n-1),其中factorial(0) = 1是递归出口

3.5

递归算法的优缺点是什么?请结合实际应用场景进行分析。

答:

  • 优点:
  1. 代码简洁清晰,能够直观地表达问题的分治思想。
  2. 适合解决具有递归性质的问题,如树形结构、分治算法等。
  • 缺点:
  1. 递归调用会引入额外的函数调用开销,可能导致性能下降。
  2. 如果递归深度过大,可能会导致栈溢出问题。
  3. 递归算法的时间复杂度和空间复杂度通常较高,尤其是对于重复子问题较多的情况(如斐波那契数列的递归实现)。
  • 应用场景:
  1. 适合解决分治问题(如归并排序、快速排序)。
  2. 适合解决树形结构问题(如二叉树的遍历、计算深度)。
  3. 不适合解决大规模问题或递归深度较大的问题(如斐波那契数列的大规模计算)。

3.6

简述回溯法中主要的剪枝策略

答:

剪枝是回溯法提前终止无效分支、减少搜索量的核心手段,分为两类:

  1. 可行性剪枝
    判断当前部分解是否已经违反约束条件,若不满足约束,该分支不可能生成合法完整解,直接舍弃该分支,不再向下递归。
    例:0-1 背包,当前已选物品总重量超过背包容量,直接停止遍历该分支后续物品。
  2. 最优性剪枝(界限剪枝)
    针对最优解问题,记录当前已找到的最优解,计算当前部分解能得到的最优理论值;若该理论值无法优于当前最优解,则直接剪去该分支。
    例:旅行商 TSP,当前路径总代价已大于已知最短回路代价,停止继续扩展该路径。

3.7

简述回溯法中常见的两种类型的解空间树。

答:

  1. 子集树
    适用于从 n 个元素中选出任意子集的问题,每个节点对应一个元素,分支只有两种:选 / 不选该元素。树总叶子节点数 (2^n)。
    典型例题:0-1 背包问题、子集和问题。
  2. 排列树
    适用于 n 个元素全排列类问题,每层确定一个位置放置的元素,每层分支数量逐层减 1,叶子节点总数 (n!)。
    典型例题:旅行商 TSP、全排列、n 皇后。

3.8

简述分支限界法的搜索策略

答:

  1. 以广度优先或带优先级的最小 / 最大代价优先(优先队列)遍历解空间树;
  2. 扩展当前节点时,一次性生成所有子节点,计算每个子节点的代价上下界;
  3. 使用界限函数做最优性剪枝:若子节点的下界已超过当前全局最优解,直接丢弃该节点,不加入队列;
    分两种主流实现:
  • 队列式分支限界:普通先进先出队列,广度优先遍历;
  • 优先队列式分支限界(最优分支限界):用堆维护节点,每次优先取出代价最优节点扩展,能更快找到最优解。
  1. 搜索目标:快速找到第一个可行最优解即可终止搜索。

3.9

回溯法的搜索特点是什么

答:

  1. 深度优先遍历解空间树:一路向下递归至叶子节点,再逐层回溯;
  2. 边搜索边构造部分解,到达叶子时生成完整解;
  3. 支持剪枝优化:提前剔除不可能产生合法 / 最优解的分支,减少搜索范围;
  4. 空间开销小:仅保存当前路径的部分解,无需存储所有待扩展节点;
  5. 搜索完整:会遍历所有未被剪枝的可行分支,可求出全部可行解;
  6. 缺点:单纯回溯无优先级,找到最优解速度慢,适合求全部解,不适合只求最优解。

3.10

请简述什么是贪心选择性

答:

一个问题的最优解可以通过一系列局部最优选择(即贪心选择)得到,且每次选择后不会影响后续子问题的贪心最优解。换言之,当前做出的看似最佳的选择,最终能够导向全局最优解,而无需回溯或考虑所有可能的子问题解。这是贪心算法能够正确求解问题的关键性质。

3.11

请简述什么是最优子结构性质

答:

一个问题的最优解包含其子问题的最优解。即,若我们能够通过组合子问题的最优解来构造原问题的最优解,则该问题具有最优子结构。这一性质是使用动态规划和贪心算法的前提条件。例如,最短路径问题中,从起点到终点的最短路径上的任意一段子路径,也必定是该段子路径两端点间的最短路径。

3.12

给定以下代码片段,分析其时间复杂度,并给出详细推导过程。

for (int i = 0; i < n; i++) {
   for (int j = 0; j < i; j++) {
       cout << i + j << endl;
   }}

答:

外层循环执行n次,
内层循环执行次数为0, 1, 2, ..., n-1,
总次数为:0+1+2+⋯+(n−1)=n(n-1)/2
因此,时间复杂度为O(n²)。

posted @ 2026-07-02 18:27  kitic  阅读(6)  评论(0)    收藏  举报