算法设计与分析 中级提升二

简介: 算法设计与分析 中级提升二

中级提升二

题目一:二叉树个数

  • 题目:image.png
  • 暴力递归:
    (1)总节点个数为n,若此时分为左子树 i 个节点,右子树就是n - i - 1,那么该种情况的情况个数为:process(i) * process(n - i - 1)
    (2)将所有情况相加就是process(n)
    (3)basecase:n < 0,return 0;n = 0,return 1;
    public static int process(int n){
        if (n < 0){
            return 0;
        }
        if (n == 0){
            return 1;
        }
        int res = 0;
        for (int left = 0; left < n; left++){
            int leftWays = process(left);
            int rightWays = process(n - left - 1);
            res += leftWays * rightWays;
        }
        return res;
    }
  • dp思路:
    (1)递归函数只有一个可变参数,只需要一个一维的dp表进行填写
    (2)初始化的时候dp[0] = 1;从dp[1]开始找寻规律
    (3)根据递归函数可知:dp[ i ] += dp[ j ] * dp[ i - j - 1 ]
    public static int process(int n){
        int[] dp = new int[n + 1];
        dp[0] = 1;
        for (int i = 1; i <= n; i ++){
            //从dp[3]开始进行计算
            for (int j = 0; j < i; j++){
                dp[i] += dp[j] * dp[i - j - 1];
            }
        }
        return dp[n];
    }

题目二:完整括号字符串

  • 题目:image.png
  • 问题一:如何判断括号的完整性
    (1)使用count计数,初始值为0
    (2) “ ( ”:左括号count++;“ ) ”:右括号count- -
    (3)要求:整个过程中,count的值 >= 0
    (4)结束后count的值 == 0
    public static boolean test(String s){
        char[] chs = s.toCharArray();
        int count = 0;
        for (char ch : chs){
            if (count < 0){
                return false;
            }
            if (ch == '('){
                count++;
            }
            if (ch == ')'){
                count--;
            }
        }
        return count == 0;
    }
  • 问题二:需要添加多少个括号
    (1)使用问题一中count计数的方法
    (2)若过程中count为0时,碰到一个 “ ) ”右括号,说明缺少一个左括号,num++
    (3)若结束遍历后count > 0,那么就说明缺少 count 个右括号,num += count
    public static int needParentheses(String s){
        char[] chs = s.toCharArray();
        int count = 0;
        int num = 0;
        for (char ch : chs){
            if (ch == '('){
                count++;
            }
            if (ch == ')'){
                if (count == 0){
                    num++;
                }else {
                    count--;
                }
            }
        }
        return num + count;
    }

题目三:去重数字对

  • 题目:image.png
  • 例arr = [3,2,5,7,0,0] ; k = 2
    结果:(0,2),(3,5),(5,7)
  • 思路:
    (1)将所有数字加入一个哈希表
    (2)遍历到一个数字后,寻找该数字 + k得到的数字是否在哈希表中
    (3)若在将该数字对加入一个HashSet,若不存在遍历下一个数字
    public static List<List<Integer>> allPair(int[] arr, int k){
        HashSet<Integer> set = new HashSet<>();
        for (int x : arr){
            set.add(x);
        }
        List<List<Integer>> res = new ArrayList<>();
        for (Integer x : set){
            if (set.contains(x + k)){
                res.add(Arrays.asList(x, x + k));
            }
        }
        return res;
    }

题目四:两集合平均值问题

  • 题目:image.png
  • 问题分析:
    (1)当a,b集合的平均值相同的时候,magic无法操作,因为此时取数字,不可能做到a,b集合的平均值同时增大
    (2)当a,b集合的平均值不相同的时候,magic不能从平均值较小的集合中取数字到较大的集合中,只可能从较大的集合中取数字到较小的集合中
    (3)magic选取的数字一定是:较小的平均值< x < 较大的平均值
    (4)在满足要求的数字中,为了实现magic的多次操作,每次选取都要选择数值最小的
    public static int maxMagic(int[] arr1, int[] arr2){
        double sum1 = 0;
        double sum2 = 0;
        for (int i : arr1){
            sum1 += (double) i;
        }
        for (int i : arr2){
            sum2 += (double) i;
        }
        if (avg(sum1, arr1.length) == avg(sum2, arr2.length)){
            return 0;
        }
        //重定位
        int[] arrMore = null;
        int[] arrLess = null;
        double sumMore = 0;
        double sumLess = 0;
        if (avg(sum1, arr1.length) > avg(sum2, arr2.length)){
            arrMore = arr1;
            sumMore = sum1;
            arrLess = arr2;
            sumLess = sum2;
        }else {
            arrMore = arr2;
            sumMore = sum2;
            arrLess = arr1;
            sumLess = sum1;
        }
        //平均值大的排序方便操作
        Arrays.sort(arrMore);
        //记录移动的数字,防止重复
        HashSet<Integer> setLess = new HashSet<>();
        //计数
        int sizeMore = arrMore.length;
        int sizeLess = arrLess.length;
        int num = 0;
        for (int i : arrMore){
            if (i < avg(sumMore, sizeMore) && i > avg(sumLess, sizeLess) && !setLess.contains(i)){
                sumMore -= i;
                sumLess += i;
                sizeMore--;
                sizeLess++;
                setLess.add(i);
                num++;
            }
        }
        return num;
    }
    public static double avg(double sum, int size){
        return sum / (double) size;
    }

题目五:括号深度

  • 题目:image.png
  • 思路
    (1)与题目二类似,使用count计数
    (2)最大深度就是count能达到的最大值
    public static int maxDepth(String s){
        char[] chs = s.toCharArray();
        int count = 0;
        int max = 0;
        for (char ch : chs){
            if (ch == '('){
                count++;
                max = Math.max(max, count);
            }
            if (ch == ')'){
                count--;
            }
        }
        return max;
    }

题目六:最长有效括号字符字串

  • 题目

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: “(()”

输出: 2

解释: 最长有效括号子串为 “()”

示例 2:

输入: “)()())”

输出: 4

解释: 最长有效括号子串为 “()()”

  • 思路:
    (1)创建字符串等大的数组dp[],每个位置与字符串位置对应
    (2)求以每一个位置的字符作为结尾,对应最长有效括号的长度
    (3)若为左括号,一定不能以其结尾,结果为0
    (4)若 i 位置为右括号,分析 i - 1 位置,考虑pre位置是否为左括号(pre:处 i - 1位置有效的括号后的前一个括号:i - dp[i -1] - 1 ),若不是左括号,则 i 位置为0,若是左括号,就是:i - 1位置的值加2再加上dp[pre - 1]位置的值
    public static int maxLength(String s){
        if (s == null || s.equals("")){
            return 0;
        }
        char[] chs = s.toCharArray();
        int[] dp = new int[chs.length];
        int pre;
        int max = 0;
        for (int i = 0; i < chs.length; i++){
            if (chs[i] == ')'){
                //左括号均为0,只考虑右括号
                pre = i - dp[i - 1] - 1;
                if (pre >= 0 && chs[pre] == '('){
                    //pre位置的检查
                    dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
                }
            }
            max = Math.max(max, dp[i]);
        }
        return max;
    }

题目七:顺序栈

  • 题目:image.png
  • 思路:
    (1)目标:将栈中元素从栈顶最大到栈底最小的排序
    (2)辅助栈:将辅助栈设计成栈顶最小到栈底最大的排序
    (3)原栈中每次弹出一个元素,加入辅助栈之前判断目前的辅助栈顶的元素是否比该元素小,若小:则弹出辅助栈的元素到原栈中,直至栈顶比该元素大;若大直接加入
    (4)当将原栈中元素全部移入辅助栈后,再将辅助栈的元素依次从栈顶到栈底加入原栈
    public static void sortStack(Stack<Integer> stack){
        Stack<Integer> temp = new Stack<>();
        while (!stack.isEmpty()){
            int x = stack.pop();
            if (!temp.isEmpty() && x > temp.peek()){
              //将不满足条件的元素从temp移动到stack
                stack.push(temp.pop());
            }
            temp.push(x);
        }
        while (!temp.isEmpty()){
            stack.push(temp.pop());
        }
    }

题目八:数字与字符的转化问题

  • 题目:image.png
  • 暴力递归:
    (1)从左往右的尝试,在数组中index为当前遍历到的位置,尝试index位置的所有可能出现的情况后,index继续移动,直至数组结束即可
    (2)若index位置出现0,说明前面转化出错,返回0
    (3)若index位置为 1 或 2,可以考虑与 index + 1位置结合转化,也可以单独转化,产生两种递归的方式:直接到下一个位置,或者跳过下一个位置
    (4)若index位置为3 ~ 9,就直接单独转化,没有产生多余的情况,直接递归到下一个位置
    (5)若index等于数组长度,说明此次转化结束返回 1
    public static int number(String str){
        if (str == null || str.length() == 0){
            return 0;
        }
        return process(str.toCharArray(), 0);
    }
    public static int process(char[] chs, int index){
        //遍历数组
        if (index == chs.length){
            //index到最后一个说明此次递归得到了一种情况
            return 1;
        }
        if (chs[index] == '0'){
            //index位置出现0,说明此次递归出错
            return 0;
        }
        if (chs[index] == '1'){
            //index位置出现1
            //单独转化index位置
            int result = process(chs, index + 1);
            if (index + 1 < chs.length){
                //结合index + 1位置转化
                result += process(chs, index + 2);
            }
            return result;
        }
        if (chs[index] == '2') {
            //index位置出现2
            //单独转化index位置
            int result = process(chs, index + 1);
            if (index + 1 < chs.length && (chs[index + 1] <= '6')){
                //结合index位置转化
                //该转化时,数字小于等于26
                result += process(chs, index + 2);
            }
            return result;
        }
        //index位置为 3 ~ 9
        return process(chs, index + 1);
    }
  • 动态规划:
    (1)递归过程中,只有index一个参数只需要创建一维的dp数组
    (2)根据递归函数:dp[chs.length] = 1
    (3)dp[i]通过chs[i]考虑,若chs[i] =‘0’,那么dp[i] = 0;若chs[i] =‘1’,并且i + 2不越界dp[i] = dp[i + 1] +dp[i + 2],越界则dp[i] = dp[i + 1];同理考虑chs[i] =‘2’,只需要多一步判断chs[i + 1]是否小与’6‘,其余情况dp[i] = dp[i + 1]
    (4)最终结果为dp[0]位置
    public static int dpWays(String str){
        if (str == null || str.length() == 0){
            return 0;
        }
        char[] chs = str.toCharArray();
        int[] dp = new int[chs.length + 1];
        dp[chs.length] = 1;
        for (int i = chs.length - 1; i >= 0; i--){
            if (chs[i] == '0'){
                dp[i] = 0;
            }else if (chs[i] == '1'){
                dp[i] = dp[i + 1];
                if ((i + 2) < chs.length){
                    dp[i] += dp[i + 2];
                }
            }else if (chs[i] == '2'){
                dp[i] = dp[i + 1];
                if ((i + 2) < chs.length && chs[i + 1] <= '6'){
                    dp[i] += dp[i + 2];
                }
            }else {
                dp[i] = dp[i + 1];
            }
        }
        return dp[0];
    }

题目九:二叉树权值和最大问题

  • 题目:image.png
  • 思路:
    (1)设置最大权值,只有在叶子节点处更新
    (2)递归方法遍历树,若不是叶子节点,移动当前节点位置,并记录路径权值,若是叶子节点停止移动,与最大权值比较
    //maxSum,全局遍历,表示最大权值路径,只有到达叶节点时才更新
    public static int maxSum = Integer.MIN_VALUE;
    public static int maxPath(Node head){
        process(head, 0);
        return maxSum;
    }
    public static void process(Node p, int pre){
        //p当前节点,pre当前节点以上的权值路径和
        if (p.left == null && p.right == null){
            maxSum = Math.max(maxSum, pre + p.value);
        }
        if (p.left != null){
            process(p.left, pre + p.value);
        }
        if (p.right != null){
            process(p.right, pre + p.value);
        }
    }

题目十:部分有效二维数组查找数字

  • 题目:image.png
  • 思路:image.png

例:如图为二维数组matrix,从该数组中寻找7是否存在

(1)遍历的时间复杂度为O(N * M),N行数,M列数

(2)快捷方法:从右上角,10的位置开始遍历

(3)10 > 7,那么10下方的数组全部不能使用,移动到9

(4)9 > 7,继续移动到5

(5)5 < 7,向下移动到6,然后9

(6)最后通过9找到7

(7)这样时间复杂度为O(N + M)

image.png

    public static boolean checkAim(int[][] matrix, int aim){
        for (int i = matrix.length - 1; i >= 0;){
            for (int j = 0; j < matrix[0].length;){
                if (matrix[i][j] == aim){
                    return true;
                }
                if (matrix[i][j] < aim){
                    i--;
                    if (i == -1){
                        //数组越界就没有找到
                       return false;
                    }
                }
                if (matrix[i][j] > aim){
                    j++;
                    if (j == matrix[0].length){
                        return false;
                    }
                }
            }
        }
        return false;
    }


目录
相关文章
|
2月前
|
机器学习/深度学习 算法 搜索推荐
从理论到实践,Python算法复杂度分析一站式教程,助你轻松驾驭大数据挑战!
【10月更文挑战第4天】在大数据时代,算法效率至关重要。本文从理论入手,介绍时间复杂度和空间复杂度两个核心概念,并通过冒泡排序和快速排序的Python实现详细分析其复杂度。冒泡排序的时间复杂度为O(n^2),空间复杂度为O(1);快速排序平均时间复杂度为O(n log n),空间复杂度为O(log n)。文章还介绍了算法选择、分而治之及空间换时间等优化策略,帮助你在大数据挑战中游刃有余。
92 4
|
4月前
|
人工智能 算法 BI
第一周算法设计与分析 D : 两面包夹芝士
这篇文章介绍了解决算法问题"两面包夹芝士"的方法,通过找出两个数组中的最大最小值,计算这两个值之间的整数个数,包括特判不存在整数的情况。
|
3天前
|
缓存 算法 搜索推荐
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
15 6
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
59 1
|
2月前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
2月前
|
算法
PID算法原理分析
【10月更文挑战第12天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
3月前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
70 4
|
3月前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
65 1
|
2月前
|
算法
PID算法原理分析及优化
【10月更文挑战第6天】PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。
|
3月前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
207 19