LeetCode 中一些经典的线性时间复杂度问题

[LeetCode 11] Container With Most Water

题目

Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

Note: You may not slant the container and n is at least 2.

1

The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

测试案例

Input: [1,8,6,2,5,4,8,3,7]
Output: 49

思路一

采用贪心算法,每次计算两端元素 a 和 b 构成的容积,并更新最大值 max。同时将两端元素较小(设为 b )的一端往中间移动一个单元。因为所有以 b 作为一端的容器的容积必不大于以 a 和 b 作为两边构成的容积。从而得到一个子问题。

代码如下

class Solution {
    public int maxArea(int[] height) {
        int n = height.length, max = 0;
        for(int start = 0, end = n - 1, min, sub; start < end;){
            min = height[start];
            sub = end - start;
            if(min > height[end]){
                min = height[end];
                end--;
            }
            else{
                start++;
            }
            if(max < min * sub){
                max = min * sub;
            }
        }
        return max;
    }
}

思路二

  1. 将元素值和下标作为一个结点新建一个结点数组。
  2. 将节点数组按元素值排序。
  3. 从 n-3 号元素开始依次遍历每个元素,并更新 i ~ n - 1号元素的最左端下标和最右端下标。

代码如下

class Solution {
    public int maxArea(int[] height) {
        int n = height.length;
        Node[] arr = new Node[n];
        for(int i = 0; i < n; i++){
            arr[i] = new Node(height[i], i);
        }
        Arrays.sort(arr);
        int left = arr[n - 1].index, right = left, h = arr[n - 2].height;
        if(left > arr[n - 2].index){
            left = arr[n - 2].index;
        }
        else{
            right = arr[n - 2].index;
        }
        int max = (right - left) * h, temp, pos;
        for(int i = n - 3; i > -1; i--){
            h = arr[i].height;
            pos = arr[i].index;
            if(left < pos){
                if((temp = (pos - left) * h) > max){
                    max = temp;
                }
            }
            else{
                if((temp = (left - pos) * h) > max){
                    max = temp;
                }
                left = pos;
            }
            if(right > pos){
                if((temp = (right - pos) * h) > max){
                    max = temp;
                }
            }
            else{
                if((temp = (pos - right) * h) > max){
                    max = temp;
                }
                right = pos;
            }
        }
        return max;
    }
}
class Node implements Comparable<Node>{
    int height;
    int index;
    public Node(int height, int index){
        this.height = height;
        this.index = index;
    }
    public int compareTo(Node obj){
        if(height != obj.height){
            return height - obj.height;
        }
        return index - obj.index;
    }
}

[LeetCode 42] Trapping Rain Water

题目

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

2

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

测试案例

Input: [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6

思路1

  1. 从左至右遍历一遍,如果当前值大于等于 max,则 sum += count,count = 0, max = height[i],index = i。否则 count += max - height[i]。
  2. 从 n - 1 ~ index 反向遍历一遍。

代码如下

class Solution {
    public int trap(int[] height) {
        int sum = 0, count = 0, index = 0, max = 0, n = height.length;
        //从左至右遍历一遍,如果当前值大于等于max,则sum += count,count = 0, max = height[i],index = i。
        //否则,count += max - height[i]
        for(int i = 0; i < n; i++){
            if(height[i] >= max){                
                sum += count;
                count = 0;
                max = height[i];
                index = i;
            }
            else{
                count += max - height[i];
            }
        }
        //从 n - 1 ~ index 反向遍历一遍
        max = 0;count = 0;
        for(int i = n - 1; i >= index; i--){
            if(height[i] >= max){
                sum += count;
                count = 0;
                max = height[i];
            }
            else{
                count += max - height[i];
            }
        }
        return sum;
    }
}

思路2

  1. 定义两个下标,start 和 end。分为初始化为 -1 和 n。
  2. 定义两个变量 leftmax 和rightmax。分别表示 0 ~ start 和 end ~ n - 1区间的最大值。初始化为 0.
  3. 然后遍历元素,当 leftmax <= rightmax时,start右移一位。如果移位后,start 处的 height 值小于 leftmax,sum += leftmax - height[start]。否则,leftmax = height[start]。反之亦然。

代码如下

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if(n < 2){
            return 0;
        }
       int sum = 0, start = 0, end = n - 1, leftmax = height[start], rightmax = height[end];     
        while(start < end){
            if(leftmax <= rightmax){
                start++;
                if(leftmax > height[start]){
                    sum += leftmax - height[start];                    
                }
                else{
                    leftmax = height[start];
                }
            }
            else{
                end--;
                if(height[end] < rightmax){
                    sum += rightmax - height[end];
                }
                else{
                    rightmax = height[end];
                }
            }
        }
        return sum;
    }
}

[LeetCode 84] Largest Rectangle in Histogram

题目

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

img
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

img
The largest rectangle is shown in the shaded area, which has area = 10 unit.

测试案例

Input: [2,1,5,6,2,3]
Output: 10

思路

将第一个数据放入栈中,从左往右遍历剩余数据。

  • 如果当前数据大于栈顶数据,当前数据入栈。
  • 当前数据等于栈顶数据,栈顶数据个数加1.
  • 当前数据小于栈顶数据,栈顶数据依次出站,用 count 记录已出栈的各结点的 num 之和。并计算栈顶元素构成的矩阵的最大值。直至栈顶元素小于当前数据或栈为空。将当前数据入栈,将其 num 置位 count + 1。因为当数据大于右侧数据时,以其为高的最大直方图已可以计算出。

代码如下

class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length, max, temp, index = 0, count;
        if(n == 0){
            return 0;
        }        
        Node[] stack = new Node[n];
        stack[0] = new Node(heights[0], 1);
        max = heights[0];
        for(int i = 1; i < n; i++){         
            if((temp = heights[i] - stack[index].val) > 0){
                stack[++index] = new Node(heights[i], 1);
            }
            else if(temp == 0){
                stack[index].num++;
            }
            else{
                count = 0;
                //将栈中高度大于当前值的结点都退出来,累计的个数放入heights[i]中
                while(index > -1 && stack[index].val > heights[i]){
                    count += stack[index].num;
                    if((temp = count * stack[index].val) > max){
                        max = temp;
                    }
                    index--;
                }
                stack[++index] = new Node(heights[i], count + 1);
            }
        }
        //处理栈中元素
        count = 0;
        while(index > -1){
            count += stack[index].num;
            if((temp = count * stack[index].val) > max){
                max = temp;
            }
            index--;
        }
        return max;
    }
}
class Node{
    int val;
    int num;
    public Node(int val, int num){
        this.val = val;
        this.num = num;
    }
}

[LeetCode 45] Jump Game II

题目

Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Your goal is to reach the last index in the minimum number of jumps.

Note:
You can assume that you can always reach the last index.

测试案例

Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
    Jump 1 step from index 0 to 1, then 3 steps to the last index.

思路

  1. 定义变量 cur 表示当前起跳位置,right 表示下一跳的最远距离。初始情况,cur = 0 处,right = nums[0]
  2. temp = cur, cur = right 作为下一个起跳位置。遍历 temp +1 ~ right之间的每个点,并用他们跳的最远距离更新 right。
  3. 更新跳的次数。
  4. 直到 right >= n - 1

代码如下

class Solution {
    public int jump(int[] nums) {
        //cur 为当前起跳位置,right 为其可跳的最远距离
        int n = nums.length, count = 1, cur = 0, right = nums[0], temp;
        if(n == 1){
            return 0;
        }
        while(right < n - 1){
            temp = right;
            //从 cur + 1 ~ right中选出该跳往的位置,选择标准是,从该位置可跳的距离最远。
            //假定吓一跳位置为 j. 当 j < temp 时,j + 1 ~ temp之间的位置都没有j跳的远,再进行从j起跳的迭代中,不需要考虑。所以,可以将起跳位置设为 temp。保持right值正确即可
            while(++cur <= temp){
                if(cur + nums[cur] > right){
                    right = cur + nums[cur];                    
                }
            }
            count++;
            cur = temp;
        }
        return count;
    }
}
posted @ 2018-09-04 11:03  Echie  阅读(689)  评论(0编辑  收藏  举报