hot100-一刷-02雙指標(共4道題)

chendsome發表於2024-12-01

283. 移動零

題目連結

題目描述

image

程式碼實現

分析:
快慢指標,快指標指向不為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. 盛最多水的容器

題目連結

題目描述

image

程式碼實現

分析:

  • 暴力做法,兩個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. 三數之和

題目連結

題目描述

image

程式碼實現

分析:
注意去重

程式碼:

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. 接雨水

題目連結

題目描述

image

程式碼實現

分析:
最關鍵是要找到對於每個柱子來說,它左邊的 最大值 和右邊的 最大值,兩者取最小值,再減去柱子的值,就是它自己可以接的水。純暴力的話就是要對每個柱子都計算出來
image

  • 分析講解影片

  • 單調棧解法 橫著計算 單調棧詳解

    點選檢視程式碼
    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;
    }
}

相關文章