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;
}
}