Leetcode之贪心法

目录

1.gas-station

2.jump-game

3.jump-game-ii

4.minimum-window-substring

5.maximum-subarray

6.maximal-rectangle


1.gas-station

题目:环形路上有n个加油站,第i个加油站的汽油量是gas[i]。你有一辆车,车的油箱可以无限装汽油。从加油站i走到下一个加油站(i+1)花费的油量是cost[i],你从一个加油站出发,刚开始的时候油箱里面没有汽油。求从哪个加油站出发可以在环形路上走一圈。返回加油站的下标,如果没有答案的话返回-1。注意:答案保证唯一。

分析:从最后一个加油站start开始出发,如果油量充足,可以一直往前走end++;如果油量不充足,向后寻找新的出发点即start--,这样就可以利用已有的计算的结果。最终start==end时,也就是车已经走完一圈了,如果这时有剩余量,那么从该起始点出发就能到达终点。

    public int canCompleteCircuit(int[] gas, int[] cost) {
        int start = gas.length - 1,end = 0;
        int rest = gas[start] - cost[start];
        while(start > end){
            if(rest >= 0){//可到达end,继续往前走
                rest = rest + gas[end] - cost[end];
                end++;
            }
            else{
                start--;
                rest = rest + gas[start] - cost[start];
            }
        }
        return rest >= 0 ? start:-1;
    }

2.jump-game

题目:给出一个非负整数数组,你最初在数组第一个元素的位置,数组中的元素代表你在这个位置可以跳跃的最大长度,判断你是否能到达数组最后一个元素的位置。例如:A =[2,3,1,1,4], 返回 true;A =[3,2,1,0,4], 返回 false.

分析:用max来标记能到达的最远处的点,遍历整个数组,如果能到达该点,计算从该点出发能到达的最远点从而更新max。

    public boolean canJump(int[] A) {
        if(A.length <= 1)
            return true;
        int max = 0;
        for(int i = 0;i < A.length;i++){
            if(max < i)
                return false;
            max = Math.max(max,i + A[i]);//在该点能到达的最大点
        }
        if(max >= A.length - 1)
            return true;
        return false;
    }

3.jump-game-ii

题目:给出一个非负整数数组,你最初在数组第一个元素的位置,数组中的元素代表你在这个位置可以跳跃的最大长度,你的目标是用最少的跳跃次数来到达数组的最后一个元素的位置。例如:给出数组 A =[2,3,1,1,4],最少需要两次才能跳跃到数组最后一个元素的位置。(从数组下标为0的位置跳长度1到达下标1的位置,然后跳长度3到数组最后一个元素的位置)

分析:计算每一点能到达的最远的地方,遍历从当前点能到达的每个点,若该点还没到达过,到达该点的步数为到达当前点步数+1。用一个数组来存储到达该点所需的最小步数,第一次到达终点的步数肯定是最小值。

    public int jump(int[] A) {
        int len = A.length;
        if(len < 2)
            return 0;
       int[] dp = new int[len];
       for(int i = 0;i < len;i++){
           int max = Math.min(len-1,i + A[i]);//从该点出发能到达的最远点
           for(int j = i + 1;j <= max;j++){
               if(dp[j] == 0)
                   dp[j] = dp[i] + 1;
           }
           if(dp[len-1] != 0)//第一次到达终点即为最小值
               return dp[len-1];
       }
       return -1;
    }

4.minimum-window-substring

题目:给出两个字符串S和T,要求在O(n)的时间复杂度内在S中找出最短的包含T中所有字符的子串。例如:S ="ADOBECODEBANC",T ="ABC",找出的最短子串为"BANC".

分析:滑动窗口。先统计出子串T各字符的个数,可以存在hashMap中(一般输入字母串的字符只有 128 个,可以用大小为128的数组来代替HashMap)。然后再遍历S串,初始化begin=end=0,对于S中的每个遍历到的字母,都在 HashMap 中的映射值减1,如果减1后的映射值仍大于等于0,说明当前遍历到的字母是T串中的字母,使用一个计数器 cnt,使其自减1,当计数为0时,说明begin到end之间已包含T中所有字符,记录窗口长度w;然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被包含在窗口;继续后移end,直到begin到end之间包含T中所有字符,重新记录最小的窗口;循环直到end到S中的最后一个字符。

    public String minWindow(String S, String T) {
        if(S.length() < T.length() || S.length() == 0 || T.length() == 0)
            return "";
        int[] a = new int[128];
        //统计T中各字符的个数
        for(int i = 0;i < T.length();i++)
            a[T.charAt(i)]++;
        //注意S中未必包含T中所有字符,所以最小长度是S的长度加1
        int start = 0,end = 0,minLen = S.length() + 1,head = 0;
        int count = T.length();//计数器,标记窗口中还缺失的字符个数
        while(end < S.length()){
            if(a[S.charAt(end)] > 0)
                count--;
            a[S.charAt(end++)]--;
            while(count == 0){
                if(minLen > end - start){//更新窗口最小值
                    head = start;
                    minLen = end - start;
                }
                if(a[S.charAt(start)] == 0)//窗口左边开始移除元素
                    count++;
                a[S.charAt(start++)]++;
            }
        }
        if(minLen == S.length() + 1)
            return "";
        return S.substring(head,head + minLen);
    }

5.maximum-subarray

题目:请计算给出的数组(至少含有一个数字)中具有最大和的子数组(子数组要求在原数组中连续)。例如:给出的数组为[−2,1,−3,4,−1,2,1,−5,4],子数组[−2,1,−3,4,−1,2,1,−5,4],具有最大的和:6.  拓展:如果你已经提出了O(n)的解决方法,请尝试使用分治算法来解决这道题。这道题分治的解法更巧妙一些。

分析:最简单的方法是使用贪心法,见剑指offer面试题42 https://blog.csdn.net/Nibaby9/article/details/104126765

另外还可以用分治法解决,见蓝桥杯分治法与动态规划例2 https://blog.csdn.net/Nibaby9/article/details/79900267

6.maximal-rectangle

题目:给出一个只包含0和1的二维矩阵,找出最大的全部元素都是1的长方形区域,返回该区域的面积。

分析:把矩阵沿着某一行切下来,然后把切的行作为底面,将自底面往上的矩阵看成一个直方图,直方图的中每个项的高度就是从底面行开始往上连续为1的数量。这样就把题转化成求直方图中最大矩阵面积问题了,largest-rectangle-in-histogram见leetcode之栈leetcode5 https://blog.csdn.net/Nibaby9/article/details/104525658

   public int maximalRectangle(char[][] matrix) {
        if(matrix.length == 0)
            return 0;
        int max = 0;
        int row = matrix.length,col = matrix[0].length;
        int[] height = new int[col];
        for(int i = 0;i < row;i++){//沿每一行切割
            for(int j = 0;j < col;j++){
                if(matrix[i][j] == '1')//注意,matrix类型为char型
                    height[j]++;
                else
                    height[j] = 0;
            }
            int val = largestRectangleArea(height);
            if(max < val)
                max = val;
        }
        return max;
    }

    public int largestRectangleArea(int[] height) {
        if(height.length == 0)
            return 0;
        int max = 0;
        Stack<Integer> s = new Stack<>();
        int i = 0;
        while(!s.empty() || i < height.length){
            if(s.empty() || i < height.length && height[i] >= height[s.peek()])
                s.push(i++);
            else{
                int high= height[s.pop()];
                //stack.peek() + 1为左边界,右边界为i
                int width = s.empty()? i : i- s.peek() - 1;
                if(high * width > max)
                    max = high * width;
            }
        }
        return max;
    }

猜你喜欢

转载自blog.csdn.net/Nibaby9/article/details/105054895