美文网首页硬核空间技术博客
每日一题之《剑指offer》64,65,66,67题

每日一题之《剑指offer》64,65,66,67题

作者: 憨憨二师兄 | 来源:发表于2020-03-28 17:01 被阅读0次

    第64题:滑动窗口的最大值

    难易度:⭐⭐⭐

    题目描述
    给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
    例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口:
    他们的最大值分别为{4,4,6,6,6,5}; 
    针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: 
    {[2,3,4],2,6,2,5,1},
    {2,[3,4,2],6,2,5,1},
    {2,3,[4,2,6],2,5,1}, 
    {2,3,4,[2,6,2],5,1},
    {2,3,4,2,[6,2,5],1},
    {2,3,4,2,6,[2,5,1]}。
    

    解析:
    本题的思路是使用双端队列
    双端队列用来保存窗口最大数值的index值
    每次都是用队列的队尾与新进入的数字进行比较,如果队列队尾要比新的值小,那么就出队,需要注意的是,此处应该使用while循环,而不是if判断:

    while(!deque.isEmpty() && num[deque.peekLast()] < num[i]){
        deque.pollLast();
    }
    

    同时,还需要注意的点是需要判断对首数字是否过期,对于数组

    {2,3,4,2,6,2,5,1}
    

    来说,当index = 5的时候,原本对首的数字4过期,当满足deque.peekFirst() == i - size时,满足过期的条件。整理好思路后就可以写代码了。
    代码如下:

    import java.util.ArrayList;
    import java.util.Deque;
    import java.util.LinkedList;
    
    public class Solution {
        public ArrayList<Integer> maxInWindows(int [] num, int size){
            ArrayList<Integer> arrayList = new ArrayList<>();
            if(num == null || num.length == 0 || size == 0 || num.length < size){
                return arrayList;
            }
            Deque<Integer> deque = new LinkedList<>();
            for(int i = 0;i < num.length;i++){
                while(!deque.isEmpty() && num[deque.peekLast()] < num[i]){
                    deque.pollLast();
                }
                deque.addLast(i);
                // 判断对首数字是否过期
                if(deque.peekFirst() == i - size){
                    deque.pollFirst();
                }
                if(i >= size - 1){
                    arrayList.add(num[deque.peekFirst()]);
                }
            }
            return arrayList;
        }
    }
    

    第65题:矩阵中的路径

    难易度:⭐⭐⭐

    题目描述
    请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
    路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
    如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 
    例如:
    a b c e
    s f c s
    a d e e
    矩阵中包含一条字符串"bcced"的路径,
    但是矩阵中不包含"abcb"路径,
    因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
    

    又是一道我想破头没写出来的题目,思路其实还是比较简单的,即:
    暴力递归
    当矩阵中坐标为(row,col)的格子和路径字符串中的下标pathLength的字符一样时,使用暴力递归的思想,从相邻的四个格子中去定位路径字符串中下标为pathLenth + 1的字符。
    如果四个相邻的个子都没有匹配字符串中下标为pathLength + 1的字符,则表明当前路径字符串中下标为pathLength的字符在矩阵中的定位是错误的,那么则需要返回到前一个字符pathLength - 1,然后重新定位
    本题同时还要使用一个矩阵大小的boolean数组,去记录,哪些点是被访问过的,因为题中有明确说明,“好马不吃回头草”。当走过的部分有被标记过则无法再走。
    代码如下:

    public class Solution {
        public boolean hasPath(char[] matrix, int rows, int cols, char[] str){
            if(matrix == null || matrix.length == 0 || str == null || str.length == 0 || rows * cols != matrix.length || rows * cols < str.length){
                return false;
            }
            boolean [] visited = new boolean[rows * cols];
            int[] pathLength = {0};
            for(int i = 0;i < rows;i++){
                for(int j = 0;j < cols;j++){
                    if(hasPathCore(matrix,rows,cols,str,i,j,visited,pathLength)){
                        return true;
                    }
                }
            }
            return false;
        }
        public boolean hasPathCore(char[] matrix,int rows,int cols,char[] str,int row,int col,boolean[] visited,int[] pathLength){
            boolean res = false;
            if(row >= 0 && row < rows && col >= 0 && col < cols && !visited[row * cols + col] && matrix[row * cols + col] == str[pathLength[0]]){
                visited[row * cols + col] = true;
                pathLength[0]++;
                if(str.length == pathLength[0]){
                    return true;
                }
                res = hasPathCore(matrix,rows,cols,str,row + 1,col,visited,pathLength)
                    || hasPathCore(matrix,rows,cols,str,row - 1,col,visited,pathLength)
                    || hasPathCore(matrix,rows,cols,str,row,col + 1,visited,pathLength)
                    || hasPathCore(matrix,rows,cols,str,row,col - 1,visited,pathLength);
                if(!res){
                    visited[row * cols + col] = false;
                    pathLength[0]--;
                }
            }
            return res;
        }
    }
    

    第66题:机器人的运动范围

    难易度:⭐⭐⭐

    题目描述:
    地上有一个m行和n列的方格。
    一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
     例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
    但是,它不能进入方格(35,38),因为3+5+3+8 = 19。
    请问该机器人能够达到多少个格子?
    

    解析:
    本题和上一题实际上都属于一个类型的问题,都属于暴力递归
    本题的解析,在二刷的时候会进行改进,写的详细一些,敬请期待
    代码如下:

    public class Solution {
        public int movingCount(int threshold, int rows, int cols){
            if(threshold < 0 || rows < 0 || cols < 0){
                return 0;
            }
            boolean[] visited = new boolean[rows * cols];
            int count = movingCountCore(threshold,rows,cols,0,0,visited);
            return count;
        }
        
        public int movingCountCore(int threshold,int rows,int cols,int row,int col,boolean[] visited){
            int res = 0;
            if(check(threshold,rows,cols,row,col,visited)){
                visited[row * cols + col] = true;
                res  = 1 + movingCountCore(threshold,rows,cols,row + 1,col,visited)
                         + movingCountCore(threshold,rows,cols,row - 1,col,visited)
                         + movingCountCore(threshold,rows,cols,row,col + 1,visited)
                         + movingCountCore(threshold,rows,cols,row,col - 1,visited);
            }
            return res;
        }
        
        public boolean check(int threshold,int rows,int cols,int row,int col,boolean[] visited){
            if(row >= 0 && row < rows && col >= 0 && col < cols && !visited[row * cols + col] && (getDigitSum(row) + getDigitSum(col) <= threshold)){
                return true;
            }
            return false;
        }
        
        public int getDigitSum(int target){
            int res = 0;
            while(target != 0){
                res += target % 10;
                target /= 10;
            }
            return res;
        }
    }
    

    第67题:剪绳子

    难易度:⭐⭐⭐

    题目描述:
    给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。
    请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?
    例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
    

    解析:
    思路一:
    贪心算法:
    贪心策略如下:
    当绳子的长度大于等于5的时候,我们需要尽可能多去剪出长度为3的绳子;当剩下的绳子长度为4的时候,把绳子简称两段长度为4的绳子。因为2 × 2 > 3 × 1
    贪心策略的证明非常难,在这里就不予以证明了。
    代码如下:

    public class Solution {
        // 贪心策略
        public int cutRope(int target) {
            if(target < 2){
                return 1;
            }
            if(target == 2){
                return 1;
            }
            if(target == 3){
                return 2;
            }
            int timesOf3 = target / 3;
            if(target - timesOf3 * 3 == 1){
                timesOf3--;
            }
            int timesOf2 = (target - timesOf3 * 3) / 2;
            return (int)(Math.pow(3,timesOf3) * Math.pow(2,timesOf2));
        }
    }
    

    本题使用动态规划也可以求解,在二刷的时候,会进行补充和改进,谢谢大家。

    相关文章

      网友评论

        本文标题:每日一题之《剑指offer》64,65,66,67题

        本文链接:https://www.haomeiwen.com/subject/yrqsuhtx.html