移除元素
class Solution {
public int removeElement(int[] nums, int val) {
int fast=0,low=0;
int len=nums.length;
for(int i=0;i<nums.length;i++)
{
if(nums[i]==val)
{
fast++;
len--;
}
else{
nums[low]=nums[fast];
fast++;
low++;
}
}
return len;
}
}
在Java中,for(int i = 0; i < nums.length; ++i)
和for(int i = 0; i < nums.length; i++)
这两种循环方式在功能上是等价的。它们都会从i = 0
开始,每次循环递增i
的值,直到i
不再小于nums.length
。
这两种循环方式的区别在于递增操作的执行时机:
- 在
++i
中,i
的值会在每次循环的开始之前递增。 - 在
i++
中,i
的值会在每次循环的结束之后递增。
class Solution {
public void moveZeroes(int[] nums) {
if(nums==null) {
return;
}
//第一次遍历的时候,j指针记录非0的个数,只要是非0的统统都赋给nums[j]
int j = 0;
for(int i=0;i<nums.length;i++) {
if(nums[i]!=0) {
nums[j] = nums[i];
j++;
}
}
//非0元素统计完了,剩下的都是0了
//所以第二次遍历把末尾的元素都赋为0即可
for(int i=j;i<nums.length;i++) {
nums[i] = 0;
}
}
}
容器高度由短的那一侧决定
class Solution {
public int maxArea(int[] height) {
//如果我们固定短的那边的边缘的话 移动另一边的柱子,那么水的高度一定不会增加,且宽度一定减少
//双指针在于缩减搜索空间
int maxsize=0;
int i=0,j=height.length-1;
int size=0;
while(i<j)
{
int wide=j-i;
//找出短的那一侧
if(height[i]<height[j]){
size=height[i]*wide;
if(size>maxsize)
{
maxsize=size;
}
i++;
}else{
//左高右低
size=height[j]*wide;
if(size>maxsize)
{
maxsize=size;
}
j--;
}
}
return maxsize;
}
}
1.动态规划法
-
理解问题:
- 首先要理解题目中的每个柱子都可以看作是一个独立的单元,雨水可以在柱子之间的空隙中积累。而一个位置能积累多少雨水,取决于它左边和右边最高的柱子(这决定了水位的高度),以及它自身的高度(这决定了水位与柱子顶部的差距)。
-
找到状态转移方程:
- 对于数组中的每个位置
i
,它能积累的雨水量等于min(左边最高柱子高度, 右边最高柱子高度) - 当前柱子高度
。这个公式的含义是:雨水量取决于左右两边最高柱子中较低的那个(这是雨水能达到的最高高度),再减去当前柱子的高度(这是雨水需要填满的高度)。
- 对于数组中的每个位置
-
处理边界条件:
- 对于数组中的第一个和最后一个位置,它们无法积累雨水,因为它们分别没有左边和右边的柱子来形成凹槽。
-
实现算法:
- 遍历数组,使用两个辅助数组
preMax
和sufMax
分别记录每个位置左边和右边的最高柱子高度。 - 再次遍历数组,根据每个位置的
preMax
和sufMax
计算该位置能积累的雨水量,并累加到总量中
- 遍历数组,使用两个辅助数组
class Solution {
public int trap(int[] height) {
int n = height.length;
int[] preMax = new int[n]; // preMax[i] 表示从 height[0] 到 height[i] 的最大值
preMax[0] = height[0];
for (int i = 1; i < n; i++) {
preMax[i] = Math.max(preMax[i - 1], height[i]);
}
int[] sufMax = new int[n]; // sufMax[i] 表示从 height[i] 到 height[n-1] 的最大值
sufMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; i--) {
sufMax[i] = Math.max(sufMax[i + 1], height[i]);
}
int ans = 0;
for (int i = 0; i < n; i++) {
ans += Math.min(preMax[i], sufMax[i]) - height[i]; // 累加每个水桶能接多少水
}
return ans;
}
}
双指针法
-
左右两边各站一个人: 让两个人分别站在路的两头,一个向右走(我们叫他左侧人),一个向左走(我们叫他右侧人)。
-
手里拿个标尺记录最高柱子: 左侧人手里的标尺记录他到目前为止看到的最高的柱子有多高,右侧人也一样。
-
比较看谁面前的柱子更矮: 如果左侧人面前的柱子比右侧人面前的矮,这意味着如果要积水的话,水的高度会被左边最高的柱子限制,因为右侧至少有一个柱子比当前这个高或者相等,保证了水不会从右边流走。此时,左侧人检查他面前的柱子是否比他手中标尺的记录矮:
- 如果是,那么这个地方可以积水,积水的高度就是他手中标尺的高度减去当前柱子的高度。
- 如果不是,说明这个柱子成了新的最高柱子,左侧人就更新他手中标尺的记录。
- 然后左侧人向前走一步。
-
反之亦然: 如果右侧人面前的柱子更矮,同样的逻辑也适用于右侧人,只不过是用右侧人手中的标尺来计算积水。
-
重复直到两人相遇: 这个过程一直重复,直到左侧人和右侧人在路的某处相遇。这时,我们就考虑了所有可能积水的位置,并计算出了能收集到的总雨水量。
这个方法之所以有效,是因为积水的多少总是由当前位置两边最高的柱子中较矮的那个决定的。通过让两个人从两端向中间移动,我们可以保证每一步都能准确地计算出每个位置可能的积水量,而不遗漏任何一个可能积水的地方。
- 动态规划法:侧重于预先计算和存储每个位置的左右最大高度,然后基于这些信息来计算雨水量。
- 双指针法:侧重于通过双指针的移动来动态地更新左右最大高度,并根据当前的左右最大高度来决定如何计算雨水量。
class Solution {
public int trap(int[] height) {
int water=0;
int left=0,right=height.length-1;
int leftMax = 0, rightMax = 0;
while(left<right)
{
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
water += leftMax - height[left];
++left;
} else {
water += rightMax - height[right];
--right;
}
}
return water;
}
}