算法基礎(chǔ)——遞歸 / 回溯(二)

一菊碟、全排列 II

  • 遞歸過程就像二叉樹纪吮,每一層遞歸結(jié)束后(回溯),看情況清理枝葉
  • 遞歸函數(shù)結(jié)束條件:當(dāng)傳入的排列結(jié)果長(zhǎng)度等于數(shù)組長(zhǎng)度
  • 遞歸函數(shù)執(zhí)行:
    • 循環(huán)數(shù)組查找块仆,當(dāng)該元素未被訪問
    • 將其標(biāo)記被訪問脐彩,是排列的一部分
    • 然后加入排列結(jié)果中(至于new的原因:傳入的排列可能會(huì)有很多新的相關(guān)排列,也即是非葉結(jié)點(diǎn))
    • 接著調(diào)用遞歸函數(shù)犁河,再次組成排列結(jié)果
    • 函數(shù)返回后(回溯):判斷目前這個(gè)元素在數(shù)組中是否重復(fù)
      • 重復(fù)是不需要再用來構(gòu)造排序的
      • 因?yàn)閿?shù)組是被排序過了鳖枕,所以循環(huán)遍歷,直到越界或者不等于
    • 該元素要被重新標(biāo)記為未加入排列桨螺,因?yàn)榭梢栽谙乱粋€(gè)元素的下一層遞歸中宾符,構(gòu)建出新的排列
class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);//數(shù)組排序
        List<List<Integer>> ans = new ArrayList<>();
        checkBack(nums, new int[nums.length], ans, new ArrayList<Integer>());
        return ans;
    }

    private void checkBack(int[] nums, int[] memory, List<List<Integer>> ans, List<Integer> permute) {
        if(permute.size() == nums.length) {
            ans.add(permute);
        } else {
            for(int i = 0; i < nums.length; i++) {//遍歷數(shù)組
                if(memory[i] == 0) {//到當(dāng)前遞歸,數(shù)組中的值不在當(dāng)前傳入permute中
                    memory[i] = 1;//現(xiàn)在在了
                    List<Integer> tpermute = new ArrayList<>(permute);
                    tpermute.add(nums[i]);
                    checkBack(nums, memory, ans, tpermute);//進(jìn)行下一層遞歸
                    memory[i] = 0;//遞歸結(jié)束就還未出現(xiàn)在往后構(gòu)造的排列中
                    while(i < nums.length - 1 && nums[i] == nums[i + 1]) {//處理重復(fù)的排列元素灭翔,不需要重復(fù)的排列
                        i++;//即使最后一個(gè)還是重復(fù)的魏烫,當(dāng)時(shí)在外循環(huán)中,i自增后判斷結(jié)果越界肝箱,結(jié)束循環(huán)哄褒。遞歸結(jié)束
                    }
                }
            }
        }
    }
}

二、組合總和

  • 題目條件

    • 無重復(fù)元素
    • candidates數(shù)組中的元素可無限制重復(fù)選取
    • 數(shù)字值煌张、數(shù)量不相等的組合就是唯一的
    • 給定數(shù)組不一定按升序排列
  • 思路:

    • 類似二叉樹
    • 遞歸回溯處理
    • 每次循環(huán)從下標(biāo)index開始呐赡,保證前面的元素不再選取,因?yàn)槟男┙M合早已出現(xiàn)過
      • index就是限制組合的重復(fù)構(gòu)成
    • 題目要求組合的元素在數(shù)組無限選取
      • 循環(huán)數(shù)組開始就要有個(gè)index開始骏融,防止重復(fù)
    • 遞歸調(diào)用函數(shù)時(shí)链嘀,將組合sum值傳入萌狂,提高效率
      • 當(dāng)sum和target相同,即可得到一個(gè)組合結(jié)果
      • 遞歸的結(jié)束
    • 循環(huán)的另一個(gè)條件sum + 當(dāng)前 下標(biāo)元素不大于target
      • 否則結(jié)束循環(huán)怀泊,遞歸結(jié)束
      • 數(shù)組需先排序茫藏,從而減少?zèng)]必要的遞歸(值比target大)
  • class Solution {
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            Arrays.sort(candidates);//先排序
            List<List<Integer>> ans = new ArrayList<>();
            checkBack(ans, candidates, target, 0, new ArrayList<Integer>(), 0);//遞歸開始,從下標(biāo)0元素開始
            return ans;
        }
    
        private void checkBack(List<List<Integer>> ans, int[] candidates, int target, int index, List<Integer> combination, int sum) {
            if(target == sum) {//當(dāng)值已是target
                ans.add(combination);
            } else if(target > sum) {
                for(int i = index; i < candidates.length && sum + candidates[i] <= target; i++) {//當(dāng)前下標(biāo)不越界且加上sum不大于target
                    List<Integer> tcom = new ArrayList<>(combination);//new一個(gè)新的組合結(jié)果
                    tcom.add(candidates[i]);//添加當(dāng)前的下標(biāo)元素
                    checkBack(ans, candidates, target, i, tcom, sum + candidates[i]);//下次遞歸時(shí)霹琼,數(shù)組循環(huán)從當(dāng)前下標(biāo)開始刷允,前面的元素?zé)o需重復(fù)選取,否則將出現(xiàn)重復(fù)的組合
                }
            }
        }
    }
    

三碧囊、組合總和 II

  • 題目條件

    • 數(shù)組 candidates存在重復(fù)的元素
    • 數(shù)組 candidates中的元素在每個(gè)組合中只能使用一次
    • 組合不能重復(fù)
    • 數(shù)組 candidates并非一定升序
  • 思路:

    • 先將數(shù)組 candidates升序排列
    • 遞歸回溯處理
      • 循環(huán)數(shù)組的每個(gè)元素树灶,同時(shí)當(dāng)下標(biāo)值 + 傳入sum 不大于target
        • sum:上層遞歸的值總和,也是傳入combination的值總和
      • 當(dāng)前下標(biāo)在memory中值為0:未被訪問糯而,可作為組合一部分
      • 然后將該下標(biāo)在memory記錄為已使用天通、同時(shí)添加到隊(duì)列
        • 隊(duì)列的作用:記錄這一層遞歸中循環(huán)使用了的元素,循環(huán)結(jié)束才可將這些元素在memory中標(biāo)記為未被使用的初始狀態(tài)
        • 為什么:在下一層循環(huán)中熄驼,不能重復(fù)使用已在組合內(nèi)的元素像寒;同一層循環(huán)內(nèi):不能使用前面構(gòu)成的組合的元素
        • 保證不重復(fù)組合的關(guān)鍵
      • 調(diào)用函數(shù)遞歸
      • 由于存在重復(fù)元素,不需要再次使用構(gòu)成數(shù)組
        • 改變下標(biāo)瓜贾、記錄memory诺祸、queue內(nèi)即可
      • 循環(huán)訪問數(shù)組元素結(jié)束:循環(huán)queue,改變memory值
    • 當(dāng)target = sum祭芦,得到一個(gè)組合結(jié)果筷笨,添加到結(jié)果集合
  • class Solution {
        public List<List<Integer>> combinationSum2(int[] candidates, int target) {
            Arrays.sort(candidates);//排序數(shù)組
            List<List<Integer>> ans = new ArrayList<>();
            checkBack(ans, candidates, target, new int[candidates.length], new ArrayList<Integer>(), 0);
            return ans;
        }
    
        private void checkBack(List<List<Integer>> ans, int[] candidates, int target, int[] memory, List<Integer> combination, int sum) {
            if(target == sum) {//是一個(gè)組合結(jié)果,加入到結(jié)果集合
                ans.add(combination);
            } else if(target > sum) {
                Queue<Integer> queue = new LinkedList<>();//記錄循環(huán)中被標(biāo)記已訪問的下標(biāo)
                for(int i = 0; i < candidates.length && sum + candidates[i] <= target; i++) {
                    if(memory[i] == 0) {//未被訪問
                        memory[i] = 1;//標(biāo)記是組合一部分
                        queue.offer(i);//入隊(duì)
                        List<Integer> tcom = new ArrayList<>(combination);
                        tcom.add(candidates[i]);//構(gòu)造新組合
                        checkBack(ans, candidates, target, memory, tcom, sum + candidates[i]);
                        while(i < candidates.length - 1 && candidates[i] == candidates[i + 1]) {//將下標(biāo)指向不重復(fù)的元素龟劲,同時(shí)也要標(biāo)記這些重復(fù)元素被使用(假使用胃夏,也即是不能用)
                            i++;
                            memory[i] = 1;
                            queue.offer(i);
                        }
                    }
                }
                while(!queue.isEmpty()) {//恢復(fù)元素的可用性
                    memory[queue.poll()] = 0;
                }
            }
        }
    }
    
  • 改進(jìn):

    • 因?yàn)閿?shù)組已排序

    • 使用下標(biāo)變化可達(dá)到標(biāo)記效果

    • 調(diào)用函數(shù)傳入當(dāng)前下標(biāo) 加 1 值,作為下一層遞歸中昌跌,循環(huán)開始的下標(biāo)

      • 將不會(huì)存在重復(fù)訪問元素的情況
      • 構(gòu)造組合的元素不會(huì)在數(shù)組中重復(fù)選取
    • class Solution {
          public List<List<Integer>> combinationSum2(int[] candidates, int target) {
              Arrays.sort(candidates);
              List<List<Integer>> ans = new ArrayList<>();
              checkBack(ans, candidates, target, 0, new ArrayList<Integer>(), 0);
              return ans;
          }
      
          private void checkBack(List<List<Integer>> ans, int[] candidates, int target, int index, List<Integer> combination, int sum) {
              if(target == sum) {
                  ans.add(combination);
              } else if(target > sum) {
                  for(int i = index; i < candidates.length && sum + candidates[i] <= target; i++) {
                      List<Integer> tcom = new ArrayList<>(combination);
                      tcom.add(candidates[i]);
                      checkBack(ans, candidates, target, i + 1, tcom, sum + candidates[i]);
                      while(i < candidates.length - 1 && candidates[i] == candidates[i + 1]) {//將下標(biāo)指向不重復(fù)的元素
                          i++;
                      }
                  }
              }
          }
      }
      
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仰禀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蚕愤,更是在濱河造成了極大的恐慌答恶,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍诱,死亡現(xiàn)場(chǎng)離奇詭異悬嗓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)砂沛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門烫扼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曙求,“玉大人碍庵,你說我怎么就攤上這事映企。” “怎么了静浴?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵堰氓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我苹享,道長(zhǎng)双絮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任得问,我火速辦了婚禮囤攀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宫纬。我一直安慰自己焚挠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布漓骚。 她就那樣靜靜地躺著蝌衔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝌蹂。 梳的紋絲不亂的頭發(fā)上噩斟,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音孤个,去河邊找鬼剃允。 笑死,一個(gè)胖子當(dāng)著我的面吹牛齐鲤,可吹牛的內(nèi)容都是我干的硅急。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼佳遂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼营袜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丑罪,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤荚板,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后吩屹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跪另,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年煤搜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了免绿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡擦盾,死狀恐怖嘲驾,靈堂內(nèi)的尸體忽然破棺而出淌哟,到底是詐尸還是另有隱情,我是刑警寧澤辽故,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布徒仓,位于F島的核電站,受9級(jí)特大地震影響誊垢,放射性物質(zhì)發(fā)生泄漏掉弛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一喂走、第九天 我趴在偏房一處隱蔽的房頂上張望殃饿。 院中可真熱鬧,春花似錦芋肠、人聲如沸壁晒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秒咐。三九已至,卻和暖如春碘裕,著一層夾襖步出監(jiān)牢的瞬間携取,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工帮孔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雷滋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓文兢,卻偏偏與公主長(zhǎng)得像晤斩,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姆坚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • 設(shè)原始數(shù)據(jù)規(guī)模為n澳泵,需要采樣的數(shù)量為k 先選取數(shù)據(jù)流中的前k個(gè)元素,保存在集合A中兼呵; 從第j(k + 1 <= j...
    Impossible安徒生閱讀 297評(píng)論 0 0
  • 他人的整理與總結(jié): https://leetcode.wang/ 1. & 15. K-sum 問題 此類問題的解...
    oneoverzero閱讀 1,859評(píng)論 0 1
  • 一兔辅、鏈表問題 鏈表問題一定要進(jìn)行舉例畫圖,輔助思考击喂!使用快慢指針遍歷鏈表维苔。因?yàn)殒湵頍o法得知長(zhǎng)度,所以嘗試用這種方法...
    voidFan閱讀 292評(píng)論 0 1
  • 七.數(shù)組 1.給定一個(gè)整數(shù)數(shù)組 nums和一個(gè)目標(biāo)值 target懂昂,請(qǐng)你在該數(shù)組中找出和為目標(biāo)值的那兩個(gè)整數(shù)介时,并返...
    寒江_d764閱讀 286評(píng)論 0 0
  • 目錄 1 左神部分集錦 2 Leetcode前150題 3 牛客網(wǎng)劍指offer 4 JavaG 5 題目中的...
    小小千千閱讀 1,012評(píng)論 0 0