C++刷题记录——回溯算法

1.基础知识

回溯算法本质就是穷举,可以就填充不行就回溯

回溯模板:
(1)return条件
(2)for循环遍历,{
	每次递归时操作;
	递归;
	回溯;
}

2.刷题

77.组合问题

回溯

理解思路,很精巧
1.path.pop_back回溯:先得到{1} ;递归得到{1,2},此时path.size()==k,return再path.pop_bac回溯 ;得到 {1,3}以此类推,最终得到{1,n}。同时startIndex可以确保数字递增不重复!
2.for循环实现单层横向遍历。

class Solution {
   
   
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(int n,int k,int startIndex){
   
   
        if(path.size()==k) 
        {
   
   
            result.push_back(path);
            return;
        }
        for(int i=startIndex;i<=n;i++){
   
   
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }

    }
public:
    vector<vector<int>> combine(int n, int k) {
   
   
    backtracking(n,k,1);
    return result;
    }
};

剪枝

如果n-startIndex<k,此时递归结束path.size()必然小于k(因为path.size()都不一定为0),没有必要遍历了。因此for循环条件可以修改为

 for(int i=startIndex;i<=n-(k-path.size())+1;i++)

216.组合总和III.

同上题,一直出错发现return写错行了,注意代码规范性

class Solution {
   
   
private:
vector<vector<int>> result;
vector<int> path;
   void backtracking(int index,int k,int n){
   
   
     if(path.size()==k){
   
   
       if(accumulate(path.begin(),path.end(),0)==n){
   
   
           result.push_back(path);
       }return;
     } 
     for(int i=index;i<=9-(k-path.size())+1;i++){
   
   
       path.push_back(i);
       backtracking(i+1,k,n);
       path.pop_back();
     }
   }
public:
   vector<vector<int>> combinationSum3(int k, int n) {
   
   
       backtracking(1,k,n);
       return result;
   }
};

17.电话号码的字母组合

本题基础语法不熟练,多刷

Q1.怎么将数字与字母对应起来?

有多种选择:map,unordered_map或是const string map[10]

1. std::mapstd::unordered_map 的使用情况
std::map(有序映射)

std::map 是一种基于平衡二叉树(通常是红黑树)实现的有序映射,它会按照key值的顺序自动排列键。

  • 适用场景

    • 需要有序数据:如果你需要保持key的顺序,可以使用 std::map。它会根据key的大小自动排序,例如按字典顺序或者数值大小排序。
    • 区间查询:如果你需要进行类似查找最小值、最大值,或者执行区间查询(例如查找key在某一范围内的所有元素)时,std::map 更加合适,因为它会保持key的有序性。
    • 时间复杂度和内存std::map 的时间复杂度是 O(log N),因为它基于红黑树,因此查询、插入、删除操作的时间复杂度是对数级别。它也比 std::unordered_map 使用更多的内存,因为要维持树的结构。
  • const map 的使用

    • 如果你希望在定义的 map 之后不再修改它的数据,可以将其声明为 const。这会确保该映射不被修改(不能进行插入或删除操作),但仍然可以读取其中的值。
  • 代码示例

    #include <iostream>
    #include <map>
    
    int main() {
         
         
        const std::map<int, std::string> orderedMap = {
         
         {
         
         3, "Three"}, {
         
         1, "One"}, {
         
         2, "Two"}};
    
        // 输出会按照键的顺序(从小到大)打印
        for (const auto& pair : orderedMap) {
         
         
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    
        // 由于是 const,因此不能修改 map
        // orderedMap[4] = "Four";  // 会报错!
    
        return 0;
    }
    

    输出:

    1: One
    2: Two
    3: Three
    
std::unordered_map(哈希映射)

std::unordered_map 是一种基于哈希表实现的无序映射,它不会按照键的顺序存储元素。

  • 适用场景

    • 快速查找:如果你不关心键的顺序,并且需要进行高效的查找、插入和删除操作,std::unordered_map 更为合适。它的查找、插入、删除操作平均时间复杂度为 O(1),因此适用于大规模数据存储和快速查询的场景。
    • 哈希操作:当你需要对键进行哈希操作时,std::unordered_map 是一个很好的选择。
  • const unordered_map 的使用

    • 如果你希望 unordered_map 中的内容不可修改,可以将其声明为 const,这意味着你不能在后续修改该映射的内容,只能进行查询。
  • 代码示例

    #include <iostream>
    #include <unordered_map>
    
    int main() {
         
         
        const std::unordered_map<int, std::string> unorderedMap = {
         
         {
         
         3, "Three"}, {
         
         1, "One"}, {
         
         2, "Two"}};
    
        // 输出的顺序是不可预测的,因为哈希映射没有顺序
        for (const auto& pair : unorderedMap) {
         
         
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    
        // 由于是 const,因此不能修改 map
        // unorderedMap[4] = "Four";  // 会报错!
    
        return 0;
    }
    

    输出(顺序可能不同):

    1: One
    2: Two
    3: Three
    

2. const string letterMap[10] 的对比

const string letterMap[10];
  • 定义解释

    • const:表示该数组是常量,数组中的元素一旦初始化后就不能被修改。
    • string:数组的元素类型是 std::string,也就是说,letterMap 数组中的每个元素都是一个字符串。
    • letterMap[10]:表示数组的大小是 10,意味着这个数组最多能存储 10 个 std::string 类型的元素。
  • std::mapstd::unordered_map 的区别

    • 大小固定letterMap[10] 是一个大小为 10 的静态数组,大小在编译时就已经确定,并且无法动态扩展。如果需要存储更多或更少的元素,需要手动调整数组的大小。
    • 没有键值对结构letterMap 只是一个简单的数组,其中的元素按索引排列,没有明确的键值对关系。你不能像在 std::mapstd::unordered_map 中那样通过键来访问元素。
    • 性能:数组具有常数时间的访问效率(O(1)),因为可以通过索引直接访问元素,而 std::mapstd::unordered_map 的查找效率则依赖于其实现(树或哈希表)。
主要区别
  • std::mapstd::unordered_map 都是动态数据结构,可以存储任意数量的元素,而 letterMap[10] 是一个固定大小的静态数组,大小固定为 10。
  • std::map 会保持键的顺序,而 letterMap[10] 没有顺序,它只是一个简单的数组。
  • std::unordered_map 提供了快速查找的特性,而 letterMap[10] 是通过索引来访问的。

总结

  • 使用 std::map:当你需要保证元素按键排序,并且需要在有序的数据上进行操作时。
  • 使用 std::unordered_map:当你不关心顺序并且需要高效的插入、删除和查找操作时。
  • 使用 letterMap[10]:当你知道需要一个固定大小的数组,并且不需要按键查找数据时,可以使用简单的数组。如果你想要动态数据存储,std::mapstd::unordered_map 会更加合适。
class Solution {
   
   
private:
//如何定义键盘与字母的对应关系
    const string lettermap[10] = {
   
   
        "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz",
    };

public:
//这里定义result是string类型
    vector<string> result;
    string path;
    void backtracking(const string& digits, int index) {
   
   
        if (path.size() == digits.size()) {
   
   
            result.push_back(path);
            return;
        }
     //递归过程实现深度
     //将输入的每位字符转化成数字
        int dight = digits[index] - '0';
     //获得对应字符代表的几个字母,作为一个单独的字符串,用for循环查找
        string cur = lettermap[dight];
        for (int i = 0; i < cur.length(); i++) {
   
   
            path.push_back(cur[i]);
            backtracking(digits, index + 1);
     //记得回溯
            path.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
   
   
    //需要排除size==0的情况,从而得到[],否则会输出[""]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值