Leetcode 40组合总数(回溯)Ⅱ41缺失的第一个正数42接雨水
維護公眾號:bigsai ,回復進群加入打卡,回復bigsai分享一些學習資源!
上周第一次 LeetCode 36有效的數獨&37解數獨(八皇后問題)
上周第二次 LeetCode 38外觀數列&39組合總和
昨日打卡 Leetcode 40組合總數(回溯)Ⅱ&41缺失的第一個正數
目錄
- 組合總數(回溯)
- 缺失的第一個正數
- 接雨水
- 結語
組合總數(回溯)
題目描述:
給定一個數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。
andidates 中的每個數字在每個組合中只能使用一次。
說明:
所有數字(包括目標數)都是正整數。
解集不能包含重復的組合。
分析,這題和組合總數Ⅰ有所不同,上一題是任意一個數可以出現任意多次并且形式上沒有重復的。
但是這題有重復的但是每個數只能使用一次。如果按照上一題的分析思路就是就是回溯的過程需要進行下一個,并且需要一個哈希對結果去重。
但是我們都知道哈希雖然效率還行但是頻繁的判斷很影響算法的效率,有什么好的方法去解決?我們在回溯算法上進行合理剪枝。用一個策略去重。
首先對于這個序列,我們先進行排序,排完序之后我們進行一下分析:
怕重復?無非是怕以下的這種情況嘛:
而我們在實際處理的時候,同一個元素如果用多次,只能從第一個元素順序使用而不能跳躍使用。
在具體的處理方式我,剛開始我用了比較笨的方法:每次找到這個元素找到它相同的區間。然后遍歷這個區間排列所有個數(從左向右),同時判斷滿足條件(數值總和不超出的情況下),遞歸下一個節點到下一個數值的編號上。當然不使用該元素在最后特殊進行一次dfs即可。
List<Integer> list = new ArrayList<Integer>();// list用來回溯過程中儲存元素 List<List<Integer>> val = new ArrayList<List<Integer>>();// 結果public List<List<Integer>> combinationSum2(int[] candidates, int target) {Arrays.sort(candidates);dfs(0, candidates, target);return val; }/** index 表示當前編號 count 表示當前數值的和*/ private void dfs(int index, int[] candidates, int target) {// System.out.println(list.toString());if (target == 0) {val.add((new ArrayList<Integer>(list)));return;} else if (index == candidates.length) {return;} else {int r = index;while (r < candidates.length && candidates[index] == candidates[r]) {r++;}int i;for (i = index; i < r; i++) {target -= candidates[i];list.add(candidates[i]);if (target>=0) {dfs(r, candidates, target);} else {//沒有執行dfs但是數值需要恢復 因為i還是變了target += candidates[index];list.remove(list.size() - 1);break;}}for (; i > index; i--) {target += candidates[index];list.remove(list.size() - 1);}dfs(r, candidates, target);// 不使用該元素的} }
算法 4ms不太行,因為提前計算出右r的位置的時候進行一部分重復量挺大的運算。參考了別人的代碼,才發現具體處理上可以這么處理:
遍歷時候正常遍歷,只不過進行遍歷的時候判斷如果該元素不是這次首元素的話和前面元素相等那么直接跳過。否則正常進行dfs。這樣就可以過濾掉所有重復情況:
最終ac的代碼為:
缺失的第一個正數
對于這題,如果不要求其他的,給一個數組空間的話,可以使用數組作為索引,直接干0ms:
public int firstMissingPositive(int[] nums) {int time[]=new int[nums.length+1];for(int i=0;i<nums.length;i++){if(nums[i]>0&&nums[i]<nums.length){time[i]++;}}for(int i=1;i<time.length;i++){if(time[i]==0)return i;}return nums.length;}但問題是人家要求的是常數級別空間。還是沒想出來忍不住看了下題解恍然大悟:
數組妙用。分析問題,求未出現的最小正數,也就是最理想的情況是1.否則就找個未出現的最小的。這題核心的想法是復用數組,我們只需標記這個位置的數字有沒有出現過! 所以可以用負數表示。
按照以下三點分析,首先我們知道這個數組中可能有負數或者大于數組長度數值的數存在,我們第一次遍歷要將這些數置為1,并且看看1是否存在。如果不存在直接返回1即可。
第二次遍歷,將對應編號位置數改為負數(注意初始正負)。注意取值。
第三次遍歷找到第個為正數的那個位置,返回編號加1.如果為正數說明這個數未出現。
具體ac代碼為:
public int firstMissingPositive(int[] nums) {boolean jud =false;//第一輪循環,將無用的數字置為1 如果整個數列中沒有1 那么將返回1for(int i=0;i<nums.length;i++){if(nums[i]<=1||nums[i]>nums.length){if(nums[i]==1)jud=true;nums[i]=1;}}if(!jud)return 1;//第二輪循環,所有數字現在目前的狀態是1 - nums.length區間內for(int i=0;i<nums.length;i++){int index=Math.abs(nums[i])-1;//該位置對應的索引if(nums[index]>0) {nums[index]=-nums[index];}}for(int i=0;i<nums.length;i++){if(nums[i]>0)return i+1;}return nums.length+1;}接雨水
題目分析
遇到這種題總有那么種似曾分析的感覺,我來談談如何分析這道題。
我的個人思路來說,我覺得這道題的核心就是看看能不能找到中間最高的元素。
找到這個最高點后:
- 左右進行相等操作,從左來看,需要一個l和lteam,讓lteam往右試探.
- 試探途中如果height[lteam]<height[l],繼續前進,后面一定會有更大的。
- 試探途中如果height[lteam]>height[l],計算途中雨點和,然后l賦值為lteam,繼續試探。
而同樣右側是進行一個從右往左的操作。
不過再具體實現上,本人初次只考慮從左往右然后遇到沒有比它更大的找個次大的計算雨水。然后重新進行。具體代碼為:
public int trap(int[] height) {int l=0,r=l+1;int count=0;int max=0,maxindex=0;while (l<height.length&&r<height.length) {if(height[r]>=height[l])//找到更大的靠山{for(int i=l;i<r;i++){count+=height[l]-height[i];}l=r;r++;max=0;maxindex=0;}else {//不大于if(height[r]>=max){max=height[r];maxindex=r;}r++;if(r==height.length){for(int i=l+1;i<maxindex;i++){count+=max-height[i];}l=maxindex;r=l+1;max=0;maxindex=0;}}}return count;}
效果一般般因為遇到逆序形的會O(n2)算法。最好情況O(n)。
而換一種O(n)的寫法之后,找到最大后右側往左一次即可:
public int trap(int[] height) { int l=0,r=l+1; int count=0;while (l<height.length&&r<height.length) {if(height[r]>=height[l])//找到更大的靠山{for(int i=l;i<r;i++){count+=height[l]-height[i];}l=r;r=l+1;}else {//不大于r++;if(r==height.length)//說明當前的left已經是天底下最大的值了{int max=0;for(int i=r-1;i>l;i--){if(height[i]>max){max=height[i];}else{count+=max-height[i];}}l=r;}} } return count;效果還行:
結語
原創不易,bigsai請你幫兩件事幫忙一下:
star支持一下, 您的肯定是我在平臺創作的源源動力。
微信搜索「bigsai」,關注我的公眾號,不僅免費送你電子書,我還會第一時間在公眾號分享知識技術。加我還可拉你進力扣打卡群一起打卡LeetCode。
記得關注、咱們下次再見!
總結
以上是生活随笔為你收集整理的Leetcode 40组合总数(回溯)Ⅱ41缺失的第一个正数42接雨水的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 回溯算法 | 追忆那些年曾难倒我们的八皇
- 下一篇: 硬核!手写一个优先队列