回溯和递归

回溯和递归


写这篇博客的目的是为了解决leetcode的39和40题,这道题是一道很经典的面试题.属于中等难度的问题

回溯法是解决很多算法问题的常见思想,甚至可以说是传统人工智能的基础方法。其本质依然是使用递归的方法在树形空间中寻找解。在这一章,我们来具体看一下将递归这种技术使用在非二叉树的结构中,从而认识回溯这一基础算法思想。…

1-1 树形问题 Letter Combinations of a Phone Number

题意介绍

注意:这里是树形问题,不是树的问题

我们先看leetcode17题:17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

在这里插入图片描述

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

分析步骤和思路

如何分析问题?
    - 字符串的合法性
    - 空字符串
    - 多个解顺序
这些要求不是本题中的要求,但是我们在面试中遇到这种题的时候,需要考虑以上情况

在这里插入图片描述

这也是我们称这些问题为树形问题的原因,这些树还是满树

递归式的分析

在这里插入图片描述

#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
    /*
     * 思路:
     * */

private:
    const string letterMap[10]={
            "",     //0
            "",     //1
            "abc",  //2
            "def",  //3
            "ghi",  // 4
            "jkl",  //5
            "mno",  //6
            "pqrs", //7
            "tuv",  //8
            "wxyz"   //9
      };
    vector<string>  result;
    /**
     *
     * @param digits  数字字符串"23"
     * @param index    每一次都处理字符串的一位,每位数字字符串代表的索引
     * @param str  之前已经转换的字符串
     */
    void findCombination(const string &digits,int index, const string &str){
//        1.0每位字符处理
        char c = digits[index];


//        4.0递归终止条件
        if (index == digits.size()){
            result.push_back(str);
            return;
        }
//       2.0 找到当前位数字符,可以代表的字符
        string letters=letterMap[c-'0'];
//       3.0  我们获得了每位数字代表的字符串例如2,代表"abc"
        for(int i=0;i<letters.size();i++)
        {
            findCombination(digits,index+1,str+letters[i]);

        }
        return;
    }
public:
    vector<string> letterCombinations(string digits) {

//        使用之前情况存储总值的数组
        result.clear();
        if (digits=="")
            return result;
        findCombination(digits,0,"");

        for(int i=0;i<result.size();i++)
        {
            cout<<result[i]<<" ";
        }
        return result;
    }
};


int main(){
    string str="234";
    Solution one;
    one.letterCombinations(str);

    return 0;
}

在递归函数里面观察递归过程

在这里插入图片描述

时间复杂度大约是3n=O(2n)

回溯是暴力解法的一个主要实现手段

93. 复原IP地址

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效的 IP 地址。

示例 1:
输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]
示例 2:

输入:s = "0000"
输出:["0.0.0.0"]
示例 3:

输入:s = "1111"
输出:["1.1.1.1"]
示例 4:

输入:s = "010010"
输出:["0.10.0.10","0.100.1.0"]
示例 5:

输入:s = "101023"
输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
提示:

0 <= s.length <= 3000
s 仅由数字组成
131. 分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab"
输出:
[
  ["aa","b"],
  ["a","a","b"]
]

1-2 什么是回溯_排列问题 Permutations

我们先根据一道例题进行了解什么是回溯?

回溯方法解决问题,和上面的树形问题的递归解法,有一些区别,就是选过的目标数组,不能出现重复,所以我们需要添加一个布尔数组放置重复放入

46. 全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

//
// Created by jiayadong on 2020/11/14.
//

#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
private:
    vector<vector<int>>  result;   //我们将所有的结果放入
    vector<bool> used;              //辅助数组
    /**
     *
     * @param nums      当前数组
     * @param index     此时第index的元素
     * @param p         此时第index+1元素,获得一个index+1个元素的排列
     */
    void generation(const vector<int>& nums,int index,vector<int>& p)
    {
        cout<<"index: "<<index<<endl;
//      1.先书写递归的终止条件
        if (index == nums.size()){
//      6.满足一次结果的数组放入
            cout<<"nums.size(): -------------->"<<index<<endl;
            result.push_back(p);
            return;
        }

//        2.我们开始进行循环
        for(int i=0;i<nums.size();i++)
        {
//        3.先判断当前位置的数组是否已经使用过了
            if (!used[i]){
//        4.将nums[i] 添加到当前数组里面
                p.push_back(nums[i]);
                used[i] = true;
//        5. 比较经典的是,回溯到上一步的时候,我们把这个数字恢复到,可选择的
                cout<<"now number:"<<nums[i]<<endl;
                generation(nums, index+1, p);
                p.pop_back();
                used[i] = false;
            }

        }
        return;
    }

public:
    vector<vector<int>> permute(vector<int>& nums) {

//        初始一下
        result.clear();
        if (nums.size() == 0) return result;

        vector<int> p;
//        初始化这个判断数组
        used=vector<bool>(nums.size() , false);
        generation(nums , 0, p);

        return result;
    }
};

int main(){
     vector<int> nums{1,2,3};

     Solution one;

    vector<vector<int>> result=  one.permute(nums);

     for(int i=0;i<result.size();i++)
    {
        for(int j=0;j<result[i].size();j++)
        {
            cout<<result[i][j]<<" ";

        }
        cout<<endl;

    }
    return 0;
}

在这里插入图片描述

47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]

提示:

1 <= nums.length <= 8
-10 <= nums[i] <= 10

1-3 组合问题 Combinations

77. 组合

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

//
// Created by jiayadong on 2020/11/14.
//
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
private:
    vector<vector<int>>  res;

    void generation(int n, int k, int start, vector<int> &c )
    {
        if (c.size()==k){
            res.push_back(c);
            return;
        }
        for(int i=start;i<=n;i++)
        {
            c.push_back(i);
            generation(n,k,i+1,c);
            c.pop_back();

        }
        return;
    }


public:
    vector<vector<int>> combine(int n, int k) {
//        在使用之前先进行清空
        res.clear();

//这里我们先考虑,不合法的参数

        if (n<=0 ||k<=0 || k>n) return res;
        vector<int> c;
        generation(n,k,1,c);
        return res;
    }
};

int main(){

    return 0;
}


在这里插入图片描述

优化回溯和剪枝思想的使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dyCAdgis-1605352322975)(E:\计算机学习\pat汇总\leetcode汇总\回溯和递归.assets\image-20201114175459753.png)]

 for(int i=start;i<=n-(k-c.size())+1; i++)
        {
            c.push_back(i);
            generation(n,k,i+1,c);
            c.pop_back();

        }
        return;

只需要改一下i的界限就可以了

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
40. 组合总和 II

给定一个数组 candidates和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]
216. 组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

所有数字都是正整数。
解集不能包含重复的组合。
示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
78. 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
90. 子集 II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
401. 二进制手表

二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。

每个 LED 代表一个 0 或 1,最低位在右侧。

在这里插入图片描述

例如,上面的二进制手表读取 “3:25”。

给定一个非负整数 n 代表当前 LED 亮着的数量,返回所有可能的时间。

示例:

输入: n = 1
返回: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]

提示:

  • 输出的顺序没有要求。
  • 小时不会以零开头,比如 “01:00” 是不允许的,应为 “1:00”。
  • 分钟必须由两位数组成,可能会以零开头,比如 “10:2” 是无效的,应为 “10:02”。
  • 超过表示范围(小时 0-11,分钟 0-59)的数据将会被舍弃,也就是说不会出现 “13:00”, “0:61” 等时间。

1-5 二维平面上的回溯法 Word Search

1-6 floodfill算法,一类经典问题 Number of Islands-

1-7 回溯法是经典人工智能的基础 N Queens

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

以码平川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值