代码随想录【回溯】

理论

与递归相辅相成,递归下面的逻辑就是回溯

  1. 本质:纯暴力搜索。
  2. 回溯算法能解决的问题:(普通for循环搜不出来)


    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
};

剪枝优化

思路:如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了
参数:


image.png
image.png

优化过程:


image.png

组合总和 III

leecode题目

  1. 思路:


    image.png

    代码:

var combinationSum3 = function(k, n) {
    let sum=0;
    let path=[]//控制只有k个数
    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. 剪枝优化
    思路:sum超过n时,不继续往下搜索

电话号码的字母组合

leecode题目

  1. 思路:
    回溯三部曲:
  • 确定回溯函数参数
    index: 记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时也表示树的深度。
  • 确定终止条件
    index 超过digits的大小
    -单层遍历逻辑

此外注意:数字和字母如何映射

image.png
var letterCombinations = function(digits) {
    if(!digits) return []
    let res =[]
    let path=[]
    let map =['','','abc','def','ghi','jkl','mno','pqrs','tuv','wxyz']//用数据存储数字与字母的映射
    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

力扣题目链接
与前面题区别:有重复元素
思路: 使用过的元素,不再使用
树层去重 树枝去重
关键点:排序

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. 参数:
    全局变量数组path存放切割后回文的子串,二维数组result存放结果集。 (这两个参数可以放到函数参数里)

本题递归函数参数还需要startIndex,因为切割过的地方,不能重复切割,和组合问题也是保持一致的。

  1. 递归函数终止条件
    切割线切到了字符串最后面,说明找到了一种切割方法
  2. 单层搜索的逻辑
    在for (int i = startIndex; i < s.size(); i++)循环中,我们 定义了起始位置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
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容