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

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

中级提升四

题目一:最小路径和

  • 题目:image.png
  • 暴力递归:
    (1)尝试方法:往下或者右的位置一次尝试
    (2)basecase的确定:右下角
    (3)特殊情况:在最右边,只能往下走;在最下边,只能往右走
    public static int minPath(int[][] matrix){
        if (matrix == null){
            return 0;
        }
        return process(matrix, 0, 0);
    }
    public static int process(int[][] martix, int x, int y){
        if (x == martix.length - 1 && y == martix[0].length - 1){
            //basecase
            return martix[x][y];
        }
        if (x == martix.length - 1){
            //最右端,只能往下走
            return martix[x][y] + process(martix, x, y + 1);
        }
        if (y == martix[0].length - 1){
            //最底端
            return martix[x][y] + process(martix, x + 1, y);
        }
        int right = process(martix, x + 1, y);
        int down = process(martix, x, y + 1);
        return martix[x][y] + Math.min(right, down);
    }
  • 动态规划:
    (1)两个可变参数,建立二维的dp表
    (2)dp表的右下角,以及最右边,和最下边,可以直接确定结果
    (3)dp表中的普遍位置依赖关系:由其右边与下边的较小值决定
    (4)确定填表顺序,从dp的右下角到左上角
    (5)最终位置:dp[0][0]
    public static int minPath(int[][] matrix){
        if (matrix == null){
            return 0;
        }
        int[][] dp = new int[matrix.length][matrix[0].length];
        dp[matrix.length - 1][matrix[0].length - 1] = matrix[matrix.length - 1][matrix[0].length - 1];
        for (int x = matrix.length - 2; x >= 0 ; x--){
            //只能往右走
            dp[x][matrix[0].length - 1] = dp[x + 1][matrix[0].length - 1] + matrix[x][matrix[0].length - 1];
        }
        for (int y = matrix[0].length - 2; y >= 0 ; y--){
            //只能往下走
            dp[matrix.length - 1][y] = dp[matrix.length - 1][y + 1] + matrix[matrix.length - 1][y];
        }
        for (int x = matrix.length - 2; x >= 0; x--){
            for (int y = matrix[0].length - 2; y >= 0; y--){
                dp[x][y] = Math.min(dp[x + 1][y], dp[x][y + 1]) + matrix[x][y];
            }
        }
        return dp[0][0];
    }

题目二:容器装水问题

  • 题目:
    image.pngimage.png
  • 思路:
    (1)先找到每一个位置可以容纳水的值,其由左侧的最大值与右侧的最大值决定
    (2)单个位置存水量的表达式:max(min(leftMax,rightMax)- 当前位置高度,0)与0比较防止负数的出现
    (3)最终将每一个位置加起来,就是其整体的容纳水量
    (4)实现过程:leftMax与rightMax数组,存储每个位置左右的最大值,再求每一个位置的存水量
    public static int maxWater(int[] arr) {
        int[] leftMax = new int[arr.length];
        int[] rightMax = new int[arr.length];
        int sum = 0;
        int max = Integer.MIN_VALUE;
        for (int i = 1; i < arr.length; i++) {
            max = Math.max(max, arr[i - 1]);
            leftMax[i] = max;
        }
        max = Integer.MIN_VALUE;
        for (int i = arr.length - 2; i >= 0; i--) {
            max = Math.max(max, arr[i + 1]);
            rightMax[i] = max;
        }
        for (int i = 0; i < arr.length; i++) {
            sum += Math.max((Math.min(leftMax[i], rightMax[i]) - arr[i]), 0);
        }
        return sum;
    }
  • 优化:
    (1)不使用leftMax与rightMax数组,只使用两个变量记录最值
    (2)使用双下标left,right遍历数组,两边可以通过leftMax与rightMax中,较小的值确定一边的存水量,同时不断更新leftMax与rightMax
    public static int maxWater(int[] arr){
        int left = 0;
        int right = arr.length - 1;
        int leftMax = arr[left];
        int rightMax = arr[right];
        int sum = 0;
        while (left < right){
            if (leftMax > rightMax){
                right--;
                if (arr[right] < rightMax){
                    sum += (rightMax - arr[right]);
                }else {
                    rightMax = arr[right];
                }
            }else {
                left++;
                if (arr[left] < leftMax){
                    sum += (leftMax -  arr[left]);
                }else {
                    leftMax = arr[left];
                }
            }
        }
        return sum;
    }

题目三:分割数组最大值

  • 题目:image.png
  • 思路:
    (1)预处理,得到每个位置左侧与右侧的最大值
    (2)按照要求找到最大差值即可
    public static int maxDevide(int[] arr) {
        int[] leftMax = new int[arr.length];
        int[] rightMax = new int[arr.length];
        int res = 0;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
            leftMax[i] = max;
        }
        max = Integer.MIN_VALUE;
        for (int i = arr.length - 1; i >= 0; i--) {
            max = Math.max(max, arr[i]);
            rightMax[i] = max;
        }
        for (int i = 0; i < arr.length; i++) {
            res = Math.max(res, Math.abs(leftMax[i] - rightMax[i]));
        }
        return res;
    }
  • 优化
    (1)先找到整个数组的最大值,因为是整体划分成两部分,结果一定是max - 某个数
    (2)若max划分到左部分,那么为了使得差值最大,右部分就是最右侧的数
    (3)同理,若max划分到右部分,那么为了使得差值最大,左部分就是最左侧的数
    (4)找到这两种情况产生的最大值即可
    public static int maxDevide(int[] arr) {
        int leftMax = arr[0];
        int rightMax = arr[arr.length - 1];
        int max = 0;
        for (int i : arr){
            max = Math.max(max, i);
        }
        return Math.max(max - leftMax, max - rightMax);
    }

题目四:旋转字符串

  • 题目:image.png
  • 思路:
    (1)先判断a,b字符串的长度是否相同,若不相同直接放回false
    (2)a = a + a
    (3)判断b是否是a的字串,可以使用kmp算法
    public static boolean circleString(String a, String b){
        if (a.length() != b.length()){
            return false;
        }
        a = a + a;
        return kmp(a, b);
    }
    public static boolean kmp(String str1, String str2){
        if (str1 == null || str2 == null || str1.length() < 1 || str2.length() < 1){
            return false;
        }
        char[] chs1 = str1.toCharArray();
        char[] chs2 = str2.toCharArray();
        int index1 = 0;
        int index2 = 0;
        int[] next = getNextArray(chs2);//str2的nextArr数组
        while (index1 < chs1.length && index2 < chs2.length){
            if (chs1[index1] == chs2[index2]){
                index1++;
                index2++;
            } else if (index2 == 0){
                //等同与:else if (next[index2] == -1)
                //index2回到0位置,且str1仍不匹配,直接使index1移动
                index1++;
            } else {
                //index2的跳跃,就是对应下标next数组中的值
                index2 = next[index2];
            }
        }
        //判断是否匹配成功
        return index2 == chs2.length;
    }
    public static int[] getNextArray(char[] str) {
        if (str.length == 1){
            return new int[] {-1};
        }
        //默认,0位置是-1,1位置是1
        int[] nextArr = new int[str.length];
        nextArr[0] = -1;
        nextArr[1] = 1;
        int index = 2; //next数组的位置
        int pre = 0; //要比较字符的位置(前缀的下一个)也有前缀长度的含义
        while (index < str.length){
            if (str[index - 1] == str[pre]){
                //index - 1 与 pre 位置相同
                //nextArr[index] 就是上一个位置加一
                //pre:前缀长度增加
                nextArr[index] = nextArr[index - 1] + 1;
                index++;
                pre++;
            } else if (pre > 0){
                //不相同,跳到上一个前缀进行比较
                pre = nextArr[pre];
            }else {
                //跳到了最前面,也不相同
                nextArr[index] = 0;
                index++;
            }
        }
        return nextArr;
    }

题目五:咖啡杯问题

  • 题目

首先,给你几个数据:

数组arr:表示几个咖啡机,这几个咖啡机生产一杯咖啡所需要的时间就是数组中的值,例如arr=[2,3,7]就表示第一台咖啡机生产一杯咖啡需要2单位时间,第二台需要3单位时间,第三台需要7单位时间。

int N:表示有N个人需要用咖啡机制作咖啡,每人一杯,同时,假设制作完咖啡后,喝咖啡时间为0,一口闷。

int a:表示用洗碗机洗一个咖啡杯需要的时间,串行运行。

int b:表示咖啡杯也可以不洗,自然晾干的时间,咖啡杯要么洗,要么晾干。

现在,请你求出这N个人从开始用咖啡杯制作咖啡到杯子洗好或者晾干的最少时间?

  • 思路:
    (1)先生成每一个人对应最短时间喝完咖啡的时间drinkTimes
    (2)drinkTimes可以通过小根堆的结果获得,小根堆元素由两个元素组成:机器空闲的时间,机器生成一次咖啡的时间。根据二者的和来确定小根堆的比较方式
    (3)便可以根据dirnkTimes尝试a,b两个洗杯子的时间,暴力递归得到最小时间
    public static class Machine{
        //堆中放入的数据类型
        public int timePoint; //一个机器可以开始使用的时间
        public int workTime; //生成一杯咖啡的时间
        public Machine(int t, int w){
            timePoint = t;
            workTime = w;
        }
    }
    public static class MachineComparator implements Comparator<Machine>{
        //小根堆的比较器
        public int compare(Machine o1, Machine o2){
            return (o1.workTime + o1.timePoint) - (o2.workTime + o2.timePoint);
        }
    }
    public static int minTime(int[] arr, int n, int a, int b){
        PriorityQueue<Machine> heap = new PriorityQueue<Machine>(new MachineComparator());
        for (int i : arr){
            //初始的开始时间均为0,机器运行时间在arr中
            heap.add(new Machine(0, i));
        }
        int[] drinkTimes = new int[n];
        for (int i = 0; i < n; i++){
            //使用堆排序填drinkTimes
            Machine cur = heap.poll();
            cur.timePoint += cur.workTime;
            drinkTimes[i] = cur.timePoint;
            heap.add(cur);
        }
        return process(drinkTimes, a, b, 0, 0);
    }
    public static int process(int[] drinkTimes, int a, int b, int index, int washLine){
        //washTime:洗咖啡的机器有空的时间
        //index:目前遍历到drinkTimes的位置
        if (index == drinkTimes.length - 1){
            //basecase
            //使用机器要受机器是否有空的时间控制
            return Math.min(Math.max(washLine, drinkTimes[index]) + a, drinkTimes[index] + b);
        }
        //选择机器洗,计算时间
        int wash = Math.max(washLine, drinkTimes[index]) + a;
        //洗完剩余咖啡杯最早的结束时间
        int next1 = process(drinkTimes, a, b, index + 1, wash);
        //需要完成所有事情的时间,是由前二者的较大值决定的
        int p1 = Math.max(wash, next1);
        //选择风干
        int dry = drinkTimes[index] + b;
        //风干剩余咖啡杯最早的结束时间
        int next2 = process(drinkTimes, a, b, index + 1, washLine);
        //需要完成所有事情的时间,是由前二者的较大值决定的
        int p2 = Math.max(wash, next1);
        return Math.min(p1, p2);
    }

题目六:arr数组的特殊要求

  • 题目:image.png
  • 思路:
    (1)将arr中的数分为3类,奇数,因子含4的偶数,因子不含4的偶数,假设个数为a,b,c
    (2)因为:奇数 * 奇数为奇数,奇数 * 因子不含4的偶数不满足题意,故奇数一定要与含4的偶数相邻;因子不含4的偶数要与偶数相邻
    (3)故我们可以通过三种数字的个数判断arr的要求能否成功
  • 情况分析
    (1)c == 0:奇 _ 含4因子 _ 奇 _ 含四因子 _ 奇 ; b >= a - 1
    (2)c != 0:将不含二因子的偶数放到一起 _ 含4因子 _ 奇 _ 含四因子 _ 奇 ;b >= a
  • 代码:
    public static boolean adjustArr(int[] arr){
        int a = 0;
        int b = 0;
        int c = 0;
        for (int i : arr){
            if (i % 4 == 0){
                b++;
            }else if (i % 2 == 0){
                c++;
            }else {
                a++;
            }
        }
        return c == 0 && b >= a - 1 || c != 0 && b >= a;
    }

题目七:仅用栈实现队列

  • 题目:在只有栈的数据结构的情况下,如何实现队列
  • 应用:在一些必须使用队列的题目中,只允许使用栈的情况下,可以使用提供的栈,实现队列的功能。例如:使用栈的结构实现图的广度优先遍历
  • 思路:
    (1)构造两个栈push,pop
    (2)每次加入数据时先加入push栈
    (3)若要弹出数据先检查pop栈是否为空,若pop栈为空,一次性将push栈中的数据全部加入pop栈,再弹出pop栈的栈顶元素;若pop栈不为空则直接弹出pop的栈顶
  • 代码:
    public static class TwoStacksQueue{
        private Stack<Integer> pushStack;
        private Stack<Integer> popStack;
        public TwoStacksQueue(){
            pushStack = new Stack<Integer>();
            popStack = new Stack<Integer>();
        }
        public void push(int value){
            pushStack.push(value);
        }
        public int pop(){
            if (pushStack.empty() &&popStack.empty()){
                throw new RuntimeException("Queue is empty");
            }
            if (popStack.isEmpty()){
                while (!pushStack.isEmpty()){
                    popStack.push(pushStack.pop());
                }
            }
            return popStack.pop();
        }
        public int peek(){
            if (pushStack.empty() &&popStack.empty()){
                throw new RuntimeException("Queue is empty");
            }
            if (popStack.isEmpty()){
                while (!pushStack.isEmpty()){
                    popStack.push(pushStack.pop());
                }
            }
            return popStack.peek();
        }
    }

题目八:仅用队列实现栈

  • 题目:在只有队列的数据结构的情况下,如何栈
  • 思路:
    (1)构造两个队列one,two
    (2)每次所有的数据都只在一个队列中
    (3)若要加入数据只能加入到目前有数据的一个队列中,若目前两个队列均为空,默认加入队列one
    (4)若要弹出数据,先将有数据的队列将除了最后一个全部加入另一个空队列,最后将最后一个元素返回即可
  • 代码:
    public static class TwoQueueStack{
        private Queue<Integer> one;
        private Queue<Integer> two;
        public TwoQueueStack(){
            one = new LinkedList<Integer>();
            two = new LinkedList<Integer>();
        }
        public void push(int value){
            if (two.isEmpty()){
                one.add(value);
            }else {
                two.add(value);
            }
        }
        public int pop(){
            int res = 0;
            if (one.isEmpty() && two.isEmpty()){
                throw new RuntimeException("Stack is empty");
            }else {
                if (one.isEmpty()){
                    while (!two.isEmpty()){
                        res = two.poll();
                        if (!two.isEmpty()){
                            one.add(res);
                        }
                    }
                }else {
                    while (!one.isEmpty()){
                        res = one.poll();
                        if (!one.isEmpty()){
                            two.add(res);
                        }
                    }
                }
            }
            return res;
        }
    }


目录
相关文章
|
28天前
|
数据采集 机器学习/深度学习 算法
|
23天前
|
人工智能 算法 BI
第一周算法设计与分析 D : 两面包夹芝士
这篇文章介绍了解决算法问题"两面包夹芝士"的方法,通过找出两个数组中的最大最小值,计算这两个值之间的整数个数,包括特判不存在整数的情况。
|
15天前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
|
1天前
|
机器学习/深度学习 存储 人工智能
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
使用Python作为开发语言,基于文本数据集(一个积极的xls文本格式和一个消极的xls文本格式文件),使用Word2vec对文本进行处理。通过支持向量机SVM算法训练情绪分类模型。实现对文本消极情感和文本积极情感的识别。并基于Django框架开发网页平台实现对用户的可视化操作和数据存储。
11 0
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
|
23天前
|
算法
算法设计与分析作业
这篇文章是关于算法设计与分析的作业,其中包含了两个算法实现:一个是使用分治算法实现的十进制大整数相乘(包括加法、减法和乘法函数),并进行了正确性和健壮性测试;另一个是使用快速排序思想实现的分治查找第K小元素的程序,并分析了其平均和最坏时间复杂度。
算法设计与分析作业
|
1月前
|
算法 Java 测试技术
算法分析(蛮力法与减治算法应用实验报告)
这篇文章是关于算法分析的实验报告,介绍了如何使用蛮力法解决背包问题,并通过伪代码和Java代码实现,同时分析了其时间效率;还介绍了基于减治法思想实现的二叉查找树的插入与查找,同样提供了伪代码、Java源代码实现和时间效率分析,最后展示了测试结果截图。
算法分析(蛮力法与减治算法应用实验报告)
|
19天前
|
编解码 算法 图形学
同一路RTSP|RTMP流如何同时回调YUV和RGB数据实现渲染和算法分析
我们播放RTSP|RTMP流,如果需要同时做渲染和算法分析的话,特别是渲染在上层实现(比如Unity),算法是python这种情况,拉两路流,更耗费带宽和性能,拉一路流,同时回调YUV和RGB数据也可以,但是更灵活的是本文提到的按需转算法期望的RGB数据,然后做算法处理
|
1月前
|
机器学习/深度学习 数据采集 算法
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
本文介绍了一个基于Python的时间序列模型,用于分析和预测2021-2022年重庆地区的气温变化趋势,通过ARIMA和LSTM模型的应用,揭示了气温的季节性和趋势性变化,并提供了对未来气温变化的预测,有助于气象预报和相关决策制定。
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
|
23天前
|
人工智能 算法
第一周算法设计与分析:C : 200和整数对之间的情缘
这篇文章介绍了解决算法问题"200和整数对之间的情缘"的方法,通过统计数组中每个数模200的余数,并计算每个同余类中数的组合数来找出所有满足条件的整数对(i, j),使得\( A_i - A_j \)是200的整数倍。
|
23天前
|
人工智能 算法
第一周算法设计与分析 G : 排队援救
这篇文章介绍了解决算法问题"排队援救"的方法,通过使用队列和映射来模拟救援点的排队过程,并确定最终得到救援的人的顺序和编号。