283. 移動零
題目連結
題目描述
程式碼實現
分析:
快慢指標,快指標指向不為0的數,慢指標指向左邊當前已經處理好的序列的尾部
程式碼:
class Solution {
public void moveZeroes(int[] nums) {
int slow = 0;
int fast = 0;
int n = nums.length;
while(fast < n){
if(nums[fast] != 0){
int temp = nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
slow++;
}
fast++;
}
}
}
11. 盛最多水的容器
題目連結
題目描述
程式碼實現
分析:
-
暴力做法,兩個for迴圈,依次遍歷每兩個柱子之間的面積,注意這裡題目說的意思是選定了兩個柱子,其它柱子就沒有。
點選檢視程式碼,暴力會超時!
class Solution { public int maxArea(int[] height) { int area = 0; for (int i = 0; i < height.length; i++) { for (int j = i + 1; j < height.length; j++){ int areat= Math.min(height[i], height[j]) * (j-i); area = Math.max(area, areat); } } return area; } }
-
雙指標:有點像貪心,left 和 right 一定是選最小的那個值作為高度,假設寬度為t (下標之差), 那麼假設left是最小的,它的面積就是height[left] * t, 再往後,就算right左邊還有更高的,面積也是不可能大於height[left] * t的。因為,首先,t就變小了,t-1或者更小。 其次,如果right左邊有更低的,比left還低,那面積肯定就更小了。所以這個left就可以捨去了,或者說,最小值的那條邊的
剩餘情況
就可以捨去了,不用再考慮了。
程式碼:
class Solution {
public int maxArea(int[] height) {
int area = 0;
int left = 0;
int right = height.length - 1;
while (left < right){
int min = Math.min(height[left], height[right]);
int areaTemp = min * (right - left);
area = Math.max(area, areaTemp);
if(min == height[left]) {
left++;
}else {
right--;
}
}
return area;
}
}
15. 三數之和
題目連結
題目描述
程式碼實現
分析:
注意去重
程式碼:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums); // 自然排序,升序
for (int i = 0; i < nums.length; i++){
// 對第一個元素去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int left = i + 1;
int right = nums.length - 1;
while(left < right){
int sum = nums[left] + nums[right];
if (sum < 0 - nums[i]){
left++;
}else if(sum > 0 - nums[i]){
right--;
}else { // 相等,找到一組
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
res.add(list);
// 對第二個元素去重
while(left < right && nums[left] == nums[left + 1]) left++;
// 對第三個元素去重
while(left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
return res;
}
}
42. 接雨水
題目連結
題目描述
程式碼實現
分析:
最關鍵是要找到對於每個柱子來說,它左邊的 最大值 和右邊的 最大值,兩者取最小值,再減去柱子的值,就是它自己可以接的水。純暴力的話就是要對每個柱子都計算出來
-
分析講解影片
-
單調棧解法 橫著計算 單調棧詳解
點選檢視程式碼
class Solution { public int trap(int[] height) { Deque<Integer> stack = new LinkedList<>(); int res = 0; // 單調棧 // 從左到右, 遇到高的柱子,就可以計算含水量了 stack.push(0); for(int i = 1; i < height.length; i++){ if(height[i] < height[stack.peek()]){ stack.push(i); }else if(height[i] == height[stack.peek()]){ stack.pop(); stack.push(i); }else { // height[stack.peek()] < height[i] while(!stack.isEmpty() && height[stack.peek()] < height[i]){ int mid = stack.pop(); if(!stack.isEmpty()){ int left = stack.peek(); int right = i; int cnt = Math.min(height[left], height[right]) - height[mid]; System.out.println(cnt * (right - left -1)); res += cnt * (right - left -1); } } stack.push(i); } } return res; } }
-
前字尾分解 豎著計算
點選檢視程式碼
class Solution { public int trap(int[] height) { int[] pre = new int[height.length]; int[] suf = new int[height.length]; int ans = 0; pre[0] = height[0]; for(int i = 1; i < height.length; i++){ pre[i] = Math.max(pre[i-1], height[i]); System.out.print(pre[i]); } suf[height.length -1] = height[height.length-1]; for(int i = height.length -2; i >= 0; i--){ suf[i] = Math.max(suf[i+1], height[i]); } for(int i = 0; i < height.length; i++){ ans += Math.min(suf[i], pre[i]) - height[i]; } return ans; } }
-
雙指標 豎著計算
這裡說的字首字尾可以想象成木板的長度。
preMax 記錄著到達 left 的最大字首,sufMax 記錄著到達 right 的最大字尾,找到當前,preMax 和 sufMax 的最小值。
比如,當 preMax 比 sufMax 小的時候,對於 left 來說,它的字尾一定是大於等於這個時候的 sufMax 的,而它的字首最大也知道了,就是當前的 preMax。
同理,對於right來說,當 sufmax<preMax 的時候,說明它的字首一定是大於等於這個時候的 preMax,而它的字尾最大也知道了,就是當前的 sufMax,自然可以計算。
sufmax 和 preMax 相等的時候就無所謂哪個加了。
程式碼:
class Solution {
public int trap(int[] height) {
int n = height.length;
int left =0;
int right = n-1;
int preMax = 0;
int sufMax = 0;
int ans = 0;
while(left < right){
preMax = Math.max(preMax, height[left]);
sufMax = Math.max(sufMax, height[right]);
// 找到小的那邊,小的那邊說明已經確定可以裝多少水了
if (preMax < sufMax){
ans += preMax - height[left];
left++;
}else {
ans += sufMax - height[right];
right--;
}
}
return ans;
}
}