代碼隨想錄【回溯】

理論

與遞歸相輔相成,遞歸下面的邏輯就是回溯

  1. 本質:純暴力搜索。
  2. 回溯算法能解決的問題:(普通for循環(huán)搜不出來)


    image.png

    排列和組合的差別:
    組合沒有順序,排列有順序

理解時袱讹,用圖形化的理解:抽象為樹形結構。
解題模版:


image.png

77.組合

leecode題目
圖示:

image.png

var combine = function(n, k) {
    let path=[]
    let res=[]
    let arr=[]
    for(let i =1;i<=n;i++){
        arr[i-1]=i
    }

    var backTracking=function(arr,index){
        if(path.length ===k){
            res.push([...path])
            return 
        }
        for(let i=index;i<arr.length;i++){
            path.push(arr[i])
            backTracking(arr,i+1)
            path.pop()
        }
    }
    backTracking(arr,0)
    return res
};

剪枝優(yōu)化

思路:如果for循環(huán)選擇的起始位置之后的元素個數(shù) 已經(jīng)不足 我們需要的元素個數(shù)了昵时,那么就沒有必要搜索了
參數(shù):


image.png
image.png

優(yōu)化過程:


image.png

組合總和 III

leecode題目

  1. 思路:


    image.png

    代碼:

var combinationSum3 = function(k, n) {
    let sum=0;
    let path=[]//控制只有k個數(shù)
    let res= []
    let arr=[1,2,3,4,5,6,7,8,9]
    var backTracking =function(n,k,sum,startIndex){
        if(path.length===k){
            if(sum === n){
                res.push([...path])
            }
            return 
        }
        for(let i=startIndex;i<arr.length;i++){
            sum +=arr[i]
            path.push(arr[i])
            backTracking(n,k,sum,i+1)
            sum -=arr[i]
            path.pop()
        }

    }
    backTracking(n,k,sum,0)
    return res
};
  1. 剪枝優(yōu)化
    思路:sum超過n時捷雕,不繼續(xù)往下搜索

電話號碼的字母組合

leecode題目

  1. 思路:
    回溯三部曲:
  • 確定回溯函數(shù)參數(shù)
    index: 記錄遍歷第幾個數(shù)字了,就是用來遍歷digits的(題目中給出數(shù)字字符串)壹甥,同時也表示樹的深度救巷。
  • 確定終止條件
    index 超過digits的大小
    -單層遍歷邏輯

此外注意:數(shù)字和字母如何映射

image.png
var letterCombinations = function(digits) {
    if(!digits) return []
    let res =[]
    let path=[]
    let map =['','','abc','def','ghi','jkl','mno','pqrs','tuv','wxyz']//用數(shù)據(jù)存儲數(shù)字與字母的映射
    var backTracking=function(map,index){
        if(index >=digits.length){
            res.push(path.join(''))
            return
        }
        let str = map[digits[index]]

        for(let i=0;i<str.length;i++){
            path.push(str[i])
            backTracking(map,index+1)
            path.pop()
        }
    }
    backTracking(map,0)
    return res
};

組合綜合

力扣題目
1.思路:
跟 77.組合不同的地方是,可以選取重復元素

image.png

拓展:
對于組合問題句柠,什么時候需要startIndex呢浦译?
如果是一個集合來求組合的話,就需要startIndex溯职,例如:77.組合精盅,216.組合總和III

如果是多個集合取組合谜酒,各個集合之間相互不影響叹俏,那么就不用startIndex,例如:17.電話號碼的字母組合(opens new window)

var combinationSum = function(candidates, target) {
   let res =[]
   let path=[]
   let sum=0
   let backTracking=function(sum,index){
       if(sum > target){
           return 
       }
       if(sum === target){
           res.push([...path])
       }
       

       for(let i=index;i<candidates.length;i++){
           path.push(candidates[i])
           sum +=candidates[i]
           backTracking(sum,i)
           sum -=candidates[i]
           path.pop()
       }

   }
   backTracking(sum,0)
   return res
};

組合總和2

力扣題目鏈接
與前面題區(qū)別:有重復元素
思路: 使用過的元素僻族,不再使用
樹層去重 樹枝去重
關鍵點:排序

image.png

var combinationSum2 = function(candidates, target) {
    let res=[]
    let path=[]
    let sum=0
    candidates.sort((a,b)=>a-b);
    let used=[]
    let backTacking=function(sum,startIndex,used){
        if(sum > target) return 
        if(sum ===target){
            res.push([...path])
            return
        }
        for(let i=startIndex;i<candidates.length;i++){
            //used[i-1] 為true表示粘驰,前一個元素未用過
            if(i>startIndex && candidates[i]===candidates[i-1] && !used[i-1]){
                continue
            } 
            sum +=candidates[i]
            used[i] =true
            path.push(candidates[i])
            backTacking(sum,i+1,used)
            sum -=candidates[i]
            path.pop()
            used[i] =false
        }
    }
    backTacking(sum,0,used)
    return res
};

分割回文子串

力扣題目

思路:
回溯三部曲:

  1. 參數(shù):
    全局變量數(shù)組path存放切割后回文的子串,二維數(shù)組result存放結果集述么。 (這兩個參數(shù)可以放到函數(shù)參數(shù)里)

本題遞歸函數(shù)參數(shù)還需要startIndex蝌数,因為切割過的地方,不能重復切割碉输,和組合問題也是保持一致的籽前。

  1. 遞歸函數(shù)終止條件
    切割線切到了字符串最后面,說明找到了一種切割方法
  2. 單層搜索的邏輯
    在for (int i = startIndex; i < s.size(); i++)循環(huán)中,我們 定義了起始位置startIndex枝哄,那么 [startIndex, i] 就是要截取的子串肄梨。

首先判斷這個子串是不是回文,如果是回文挠锥,就加入在vector<string> path中众羡,path用來記錄切割過的回文子串。
此外:判斷是否是回文子串蓖租。


image.png
var partition = function(s) {
    let res=[]
    let path=[]
    var backtracking = function(s,startIndex){
        if(startIndex>=s.length){
            res.push([...path])
        }
        for(let i=startIndex;i<s.length;i++){
            if(isHuiwen(s.substring(startIndex,i+1))){
                path.push(s.substring(startIndex,i+1))
                backtracking(s,i+1)
                path.pop()
            }
        }
    }
    backtracking(s,0)
    return res
};
function isHuiwen(str){
    for(let i=0,j=str.length-1;i<str.length;i++,j--){
        if(str[i] !=str[j]){
            return false
        }
    }
    return true
}

復原 IP 地址

leecode題目
思路:
回溯三部曲

  • 確定終止條件
    path的長度大于等于4
  • 單層遍歷邏輯
    合法的子串放入path


    image.png
var restoreIpAddresses = function(s) {
   let path=[]
   let res=[]
   let backTracking=function(startIndex){
       const len = path.length;
       if(len > 4) return;
       if(len === 4 && startIndex === s.length) {
           res.push(path.join("."));
           return;
       }

       for(let i=startIndex;i<s.length;i++){
            const str = s.slice(startIndex, i + 1);
           if(isValid(str)){
               path.push(s.slice(startIndex,i+1))
               backTracking(i+1)
               path.pop()
           }
       }

   }
   backTracking(0,0)
   return res
};
function isValid(str){
   if(str.length > 3 || str > 255) return false;
   if(str.length > 1 && str[0] === "0") return false;
   return true

}

子集

力扣題目
思路:

image.png

var subsets = function(nums) {
    let res = []
    let path=[]
    let backTracking=function(startIndex){
        res.push([...path])
        if(startIndex>=nums.length)return
        for(let i=startIndex;i<nums.length;i++){
            path.push(nums[i])
            backTracking(i+1)
            path.pop()
        }
    }
    backTracking(0)
    return res
};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末粱侣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蓖宦,更是在濱河造成了極大的恐慌齐婴,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稠茂,死亡現(xiàn)場離奇詭異柠偶,居然都是意外死亡,警方通過查閱死者的電腦和手機睬关,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門诱担,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人电爹,你說我怎么就攤上這事蔫仙。” “怎么了丐箩?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵摇邦,是天一觀的道長。 經(jīng)常有香客問我雏蛮,道長涎嚼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任挑秉,我火速辦了婚禮法梯,結果婚禮上,老公的妹妹穿的比我還像新娘犀概。我一直安慰自己立哑,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布姻灶。 她就那樣靜靜地躺著铛绰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪产喉。 梳的紋絲不亂的頭發(fā)上捂掰,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天敢会,我揣著相機與錄音,去河邊找鬼这嚣。 笑死鸥昏,一個胖子當著我的面吹牛,可吹牛的內容都是我干的姐帚。 我是一名探鬼主播吏垮,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼罐旗!你這毒婦竟也來了膳汪?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤九秀,失蹤者是張志新(化名)和其女友劉穎遗嗽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颤霎,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡媳谁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年涂滴,在試婚紗的時候發(fā)現(xiàn)自己被綠了友酱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡柔纵,死狀恐怖缔杉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情搁料,我是刑警寧澤或详,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站郭计,受9級特大地震影響霸琴,放射性物質發(fā)生泄漏。R本人自食惡果不足惜昭伸,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一梧乘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庐杨,春花似錦选调、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至填渠,卻和暖如春弦聂,著一層夾襖步出監(jiān)牢的瞬間鸟辅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工莺葫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剔桨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓徙融,卻偏偏與公主長得像洒缀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子欺冀,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容