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