day59|503.下一个更大元素II、42. 接雨水

文章目录

  • 一、503.下一个更大元素II
  • 二、42. 接雨水

一、503.下一个更大元素II

注意的点:

  1. 提到循环,就比较容易想到将两个数组拼接。
  2. 栈中存放的必须时数组下标而非数组值,因为在处理的过程中需要用到下标。

以下是代码部分:

public class 下一个更大元素II503 {

    //思路:再拼接一个nums数组,然后就构成了一个循环
    public int[] nextGreaterElements(int[] nums) {

        //构造一个新的数组,是原数组的两倍
        int[] nums2 = new int[nums.length*2];
        //踩坑,这里是 
        for (int i = 0; i < nums.length; i++) {
            nums2[i] = nums[i];
            nums2[i + nums.length] = nums[i];
        }

        int[] result = new int[nums.length];
        int[] result2 = new int[nums.length*2];
        for (int i = 0; i < result2.length; i++) {
            result2[i] = -1;
        }

        Stack<Integer> s = new Stack<>();

        for (int i = 0; i < nums2.length; i++) {

            while (!s.isEmpty() && nums2[s.peek()] < nums2[i]){
                //栈中必须存下标,因为这里会用到
                result2[s.peek()] = nums2[i];
                s.pop();
            }

            s.push(i);
        }

        //将result2中的前半部分赋给result
        for (int i = 0; i < result.length; i++) {
            result[i] = result2[i];
        }

        return result;
    }
}

二、42. 接雨水

注意的点

  1. 可用动态规划、单调栈、双指针三种方式实现。
  2. 动态规划和双指针思路相同,都是寻找每一列两侧的最大值,从而记录该列最终存储的数量;而单调栈则是寻找左右两侧第一个大的值,记录的仅仅只是暂时该列存储的雨水数量,而非最终的雨水数量,可以看作通过行的方式求解。

以下是代码部分:

public class 接雨水42 {

    //动态规划
    //思路:维持两个dp数组,一个从左向右看取最大值;一个从右向左看取最大值
    public int trap(int[] height) {

        //定义dp数组
        int[] left = new int[height.length];
        int[] right = new int[height.length];

        //初始化,由于非负,所以最小值设为0
        int leftMax = 0;
        int rightMax = 0;

        //遍历
        for (int i = 0; i < left.length; i++) {
            leftMax = Math.max(leftMax, height[i]);
            left[i] = leftMax;
        }
        for (int i = right.length-1; i >= 0; i--) {
            rightMax = Math.max(rightMax, height[i]);
            right[i] = rightMax;
        }

        int result = 0;
        //取两个dp数组中的最小值,再减去当列的原高度作为第i列可以储水的高度
        for (int i = 0; i < height.length; i++) {
            result += Math.min(left[i], right[i]) - height[i];
        }

        return result;
    }


    //单调栈
    //思路:使用单调栈找到右边第一个大于当前列的值,此时形成凹槽,可以储水。相比动态规划,单调栈更类似于从行的角度解决问题(每次求解的并不是该列真正可以储水的量)
    public int trap2(int[] height) {

        Stack<Integer> s = new Stack<>();

        int weight = 0;
        int high = 0;
        int tmp = 0;
        int result = 0;

        for (int i = 0; i < height.length; i++) {

            //维持一个递减序列
            //当发现凹槽时,就进行一个储水值的计算
            while (!s.isEmpty() && height[s.peek()] < height[i]){

                //首先要弹出当前的栈头,因为要在栈头下边寻找它的左边界(左右两个比栈头大的值形成了一个凹槽)
                tmp = s.pop();

                //这里要判断弹出栈顶元素后,当前栈是否为空
                //弹出为空,证明左边没有更大的值,所以储存不了水
                if(s.isEmpty())
                    break;

                //求凹槽的高度
                //这里注意减的是 height[tmp] ,而不是tmp
                high = Math.min(height[i], height[s.peek()]) - height[tmp];
                //求凹槽的宽度,这里就会用到下标,所以要在栈中存储下标。多减一个 1 是因为要求两端中间的宽度,不包括两端
                weight = i - s.peek() - 1;
                //记录储水的值
                result += high * weight;
            }

            //入栈操作,每个元素都会进行
            s.push(i);
        }

        return result;
    }


    //双指针
    //思路:其实就是在动态规划的基础上进行一个空间上的一个减少,只需要O(1)的空间
    public int trap3(int[] height) {

        //这里必须初始化成第一个和最后一个值,否则在刚开始的时候left、right还是0,比较就有问题了
        int left = height[0];
        int right = height[height.length-1];
        int result = 0;

        int i = 0;
        int j = height.length - 1;

        //踩坑: 这里比较必须是 i<=j 而不能是 i

        while (i <= j){

            //如果左边的最高值小于右边的,则移动左边的
            while (left <= right && i <= j){
                //如果当前高度大于维持的左边最高,则更新,同时也储蓄不了水
                if(height[i] >= left){
                    left = height[i];
                    i++;
                }
                //否则,可以储水。储水的值就是动态规划中的公式
                else {
                    result += Math.min(left, right) - height[i];
                    i++;
                }
            }

            //踩坑:直接就复制过来了,忘了i、j互换了
            while (left >= right && i <= j){

                if(height[j] >= right){
                    right = height[j];
                    j--;
                }

                else {
                    result += Math.min(left, right) - height[j];
                    j--;
                }
            }
        }

        return result;
    }
}

你可能感兴趣的:(LeetCode,算法,数据结构)