CodeTop 42/33

本文详细介绍了四种不同的算法方法解决接雨水问题,包括按列求解、动态规划、双指针和栈。每种方法都通过分析数组中每个节点的左右最大高度来计算储水量。此外,还讲解了搜索旋转排序数组的二分查找算法,通过判断中间值与数组起始值的关系来缩小搜索范围。这些算法展示了在处理数组问题时的有效策略。
摘要由CSDN通过智能技术生成

42. 接雨水

按列求:

//计算每个节点左边的最大值和右边的最大值,选择较小的值与当前节点值做减法,大于0则该差值为储水量。
class Solution {
    public int trap(int[] height) {
        int sum=0;
        //外层循环是为了计算每个位置可以接多少水,最左和最右接不到水,所以范围是1~height.length-1
        for(int i=1; i<height.length-1; ++i){
            //记录该位置左边最高的点
            int maxleft=0;
            for(int j=i-1; j>=0; --j){
                if(height[j]>maxleft){
                    maxleft=height[j];
                }
            }
            //记录该位置右边最高的点
            int maxright=0;
            for(int j=i+1; j<height.length; ++j){
                if(height[j]>maxright){
                    maxright=height[j];
                }
            }
            //将两个最高点中较小的点和当前位置进行比较
            //1.当大于当前位置时,可以接min-height[i]的水
            //2.当小于或等于当前位置时,接不到水
            int min=Math.min(maxleft,maxright);
            if(min>height[i]){
                sum+=min-height[i];
            }
        }
        return sum;
    }
}

动态规划:

//使用动态规划,分别记录当前节点左边最高点和右边最高点,降低时间复杂度
class Solution {
    public int trap(int[] height) {
        int nums=0;
        //用两个数组记录不同节点左右两边的最高点
        int[] maxleft=new int[height.length];
        int[] maxright=new int[height.length];
        //不用考虑首尾节点
        //左边的最高点是前一个节点与前一个节点左边节点最大值中较大的一个
        for(int i=1; i<height.length-1; ++i){
            maxleft[i]=Math.max(maxleft[i-1],height[i-1]);
        }
        //右边的最高点是后一个节点与后一个节点右边边节点最大值中较大的一个
        for(int i=height.length-2; i>0; --i){
            maxright[i]=Math.max(maxright[i+1],height[i+1]);
        }
        for(int i=1; i<height.length-1; ++i){
            int min=Math.min(maxleft[i],maxright[i]);
            if(height[i]<min){
                nums+=min-height[i];
            }
        }
        return nums;
    }
}

双指针:

class Solution {
    public int trap(int[] height) {
        int sum=0;
        //首尾两个节点不需要计算
        int left=1,right=height.length-2;
        //为左右两边最大值赋初值,即首尾节点,maxleft代表left左边的最大值,maxright代表right右边的最大值
        int maxleft=height[0],maxright=height[height.length-1];
        //注意等号必须加!因为left和right同时指向一个节点时,还没有计算过该节点的储水量
        while(left<=right){
            //当左边的最大值小于右边的最大值时,此时当前节点右边的最大值是多少已经不重要了
            //因为我们是通过较小的最大值即左边的最大值来计算当前节点(left节点)的储水量
            //更新maxleft的值,指针右移,计算下一个节点的储水量
            if(maxleft<maxright){
                sum+=Math.max(0,maxleft-height[left]);
                maxleft=Math.max(height[left],maxleft);
                left++;
            }else{
                sum+=Math.max(0,maxright-height[right]);
                maxright=Math.max(height[right],maxright);
                right--;
            }
        }
        return sum;
    }
}

栈:

//维护一个单调栈,若current指向元素大于栈顶元素,则出栈并计算该节点上方的水柱高度,否则,将current入栈
class Solution {
    public int trap(int[] height) {
        int sum=0;
        Stack<Integer> stack=new Stack<>();
        int current=0;
        while(current<height.length){
            //当栈不为空且栈顶元素值小于当前元素
            while(!stack.isEmpty() && height[stack.peek()]<height[current]){
                //栈顶元素出栈,计算该元素上方的水柱(行)
                int h=height[stack.pop()];
                //若此时栈为空,则代表该点左边没有最大值,跳出循环
                if(stack.isEmpty()){
                    break;
                }
                //计算宽度
                int distance=current-stack.peek()-1;
                //计算左右最大值中较小值
                int min=Math.min(height[current],height[stack.peek()]);
                sum+=distance * (min-h);
            }
            stack.push(current);
            current++;
        }
        return sum;
    }
}

33. 搜索旋转排序数组

//使用二分法,根据nums[mid]与nums[0]值的关系,判断nums[mid]左边/右边空间有序,然后根据两种不同情况改变i和j的值
class Solution {
    public int search(int[] nums, int target) {
        int i=0,j=nums.length-1;
        while(i<=j){
            int mid=(i+j)/2;
            if(nums[mid]==target){
                return mid;
            }
            //当0-mid是有序的
            else if(nums[mid]>=nums[0]){
                //若target在有序区间内
                if(nums[0]<=target && nums[mid]>target){
                    j=mid-1;
                }else    i=mid+1;
            }
            //当mid-nums.length-1是有序的
            else{
                //若target在有序空间内
                if(target>nums[mid] && target<=nums[nums.length-1]){
                    i=mid+1;
                }else   j=mid-1;
            }
        }
        return -1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值