剑指offer12矩阵中的路径刷题打卡

简介: 剑指offer12矩阵中的路径刷题打卡

剑指offer12

深度优先遍历做法:

思路:

确定起始位置,从起始位置开始,扫描上下左右位置是否可以前进(isValid函数),可以前进则前进至直到路径包含完毕。

解题:

工具函数
isvalid函数

一个isvalid函数,判断遍历到的位置是否符合正确路径的值

bool isValid(string word, int ptr, char value){
    if(word[ptr] == value) return true;
    return false;
}
// 调用形式
isValid(isValid(word, ptr, board[i][j]));
  • 需要注意的是在每次判断之前,ptr位置前面的值一定是正确的,因为是逐个判断和遍历过来的
findFirst函数

这个函数是用来保存boardword第一个字符的位置,因为可能有很多个,所以我使用unordered_map来保存

void findFirst(vector<vector<char>>& board, char word, unordered_map<int, pair<int, int>>& firstword){
    // 查找word起始字符的位置
    int count = 0;
    for(int i = 0; i <board.size(); i++){
        for(int j = 0; j <board[0].size(); j++){
            if(board[i][j] == word) firstword.insert(pair<int, pair<int, int>>(count++, make_pair(i, j)));
        }
    }
}
  • 其中泛型为<int, pair<int, int>>,其中key值仅仅用于标识唯一的位置,没有用mutimap所以如果使用<int, int>,就会出现如果两个位置是[3, 0]、[3, 2]这样其中一个值就会被覆盖掉
主函数与深度优先函数
exist函数

题解中的主函数,用于初始化与驱动回溯函数

bool exist(vector<vector<char>>& board, string word) {
    vector<vector<int>> used(board.size(), vector<int>(board[0].size(), 0));  // 与board大小相同的条件数组
    unordered_map<int, pair<int, int>> firstword;  // 存放所有起始位置的集合
    findFirst(board, word[0], firstword);  // 查找并保存起始位置元素集合的位置
    for(auto i: firstword){  // 试着从每一个起始位置开始深度优先遍历查找
        if(backtracking(board, word, i.second.first, i.second.second, 0, used)) 
            return true;
    }
    return false;
}
  • 第一个是used数组,大小与board数组一样,主要作用是防止走倒路,后面会解释
  • 第二个是firstwordmap映射集合,用来存放所有起始位置的集合
  • 第三个是findFirst函数,用于查找找并保存起始位置元素集合的位置
  • 接着就是尝试从每一个起始位置开始深度优先搜索
backtracking函数

递归三步:

  • 确定参数和返回值
  • 除了exist中的两个参数之外还需要添加当前位置参数(int i, int j),和遍历到了word中的那哪个位置(int ptr),和一个防止走倒路的数组(vector<vector<int>>& used)
  • 返回值是bool,只要找到一条合适的路径直接返回true即可
bool backtracking(
    vector<vector<char>>& board, 
    string word, 
    int i, 
    int j, 
    int ptr, 
    vector<vector<int>>& used
);
  • 确定终止条件
  • ptr遍历到word.size() - 1时并且该位置与其最后一个位置的字符是相同的则返回true
if(ptr == word.size() - 1 && board[i][j] == word[ptr]) return true;
  • 确定本层逻辑
  • 大致框架
bool backtracking(vector<vector<char>>& board, string word, int i, int j, int ptr, vector<vector<int>>& used
){
    if(ptr == word.size() - 1) return true;
    if(isValid(word, ptr, board[i][j])){
        // 左
        // 下
        // 右
        // 上
    }
}
  • 以这个框架展开讲解,首先要是确保当前值是有效的(是在路径上并且对应的位置也相同),然后开始遍历
if(backtracking(board, word, i, j + 1, ptr + 1,used)) return true;
if(backtracking(board, word, i + 1, j, ptr + 1,used)) return true;   
if(backtracking(board, word, i, j - 1, ptr + 1,used)) return true;
if(backtracking(board, word, i - 1, j, ptr + 1,used)) return true;
  • 以第一个作为讲解,开始往右遍历也就是j+1然后ptr+1判断右侧值是否有效,这里就有回溯,回溯在于ptr+1j+1,在函数执行完毕后,j的值不会变,也就进行了一次回溯然后继续查看下方合适吗,在这一过程中,只要找到了合适的路,直接返回,不会后面的递归。边界判断:
  • 既然对ij上有操作,那么就一定要判断是否越界
if(j + 1 < colSize) 
    if(backtracking(board, word, i, j + 1, ptr + 1,used)) return true; 
if(i + 1 < rowSize)                    
    if(backtracking(board, word, i +1, j, ptr + 1,used)) return true;   
if(j - 1 >= 0) 
    if(backtracking(board, word, i, j - 1, ptr + 1,used)) return true;        
if(i - 1 >= 0) 
    if(backtracking(board, word, i - 1, j, ptr + 1,used)) return true;
  • 在执行isvalid前获取colsizerowsize这样整体的代码会是这样
bool backtracking(vector<vector<char>>& board, string word, int i, int j, int ptr, vector<vector<int>>& used){
    if(ptr == word.size() - 1) return true;
    int rowSize = board.size();
    int colSize = board[0].size();
    if(isValid(word, ptr, board[i][j])){
        if(j + 1 < colSize) 
            if(backtracking(board, word, i, j + 1, ptr + 1)) return true; 
        if(i + 1 < rowSize) 
            if(backtracking(board, word, i +1, j, ptr + 1)) return true; 
        if(j - 1 >= 0) 
            if(backtracking(board, word, i, j - 1, ptr + 1)) return true; 
        if(i - 1 >= 0) 
            if(backtracking(board, word, i - 1, j, ptr + 1)) return true; 
    }
    return false;
}
  • 运行测试用例发现很开心过了
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FVLwAhDZ-1681874766177)(image\image-20230419111229004.png)]
    于是屁颠屁颠的提交
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fOPjnaFk-1681874766178)(image\image-20230419111304192.png)]
    后来根据打日志调试,发现是他走了倒路!
    这个测试用例长这样
A B C E
S F C S
A D E E
  • 这版代码他会按照[0,0] -> [0, 1] -> [0, 2] -> [0, 3] -> [1 , 2] -> [0, 2]这样走,你就会发现他走了倒路,于是这就要用上我们的最后一个参数used数组,加入之后代码大框架的变化会是这样
bool backtracking(vector<vector<char>>& board, string word, int i, int j, int ptr, vector<vector<int>>& used
){
    if(used[i][j] == 1) return false;
    if(ptr == word.size() - 1 && board[i][j] == word[ptr]) return true;
  used[i][j] = 1;
    if(isValid(word, ptr, board[i][j])){
        // 左
        // 下
        // 右
        // 上
    }
    used[i][j] = 0;
    return false;
}
  • 这样一来他就不会在[0,0] -> [0, 1] -> [0, 2] -> [0, 3] -> [1 , 2] -> [0, 2]这条路中走最后一个结点,因为我们使用了一个used数组避免了走倒路

完整代码:

class Solution {
public:
    bool isValid(string word, int ptr, char value){
        if(word[ptr] == value) return true;
        return false;
    }
    bool backtracking(vector<vector<char>>& board, string word, int i, int j, int ptr, vector<vector<int>>& used){
        if(used[i][j] == 1) return false;
        if(ptr == word.size() - 1 && board[i][j] == word[ptr]) return true;
        int rowSize = board.size();
        int colSize = board[0].size();
        used[i][j] = 1;
        if(isValid(word, ptr, board[i][j])){  // 如果该值可以则深度优先
            if(j + 1 < colSize) 
                if(backtracking(board, word, i, j + 1, ptr + 1,used)) return true; 
            if(i + 1 < rowSize)                    
                if(backtracking(board, word, i +1, j, ptr + 1,used)) return true;   
            if(j - 1 >= 0) 
                if(backtracking(board, word, i, j - 1, ptr + 1,used)) return true;        
            if(i - 1 >= 0) 
                if(backtracking(board, word, i - 1, j, ptr + 1,used)) return true;   
        }
        used[i][j] = 0;
        return false;
    }
    void findFirst(vector<vector<char>>& board, char word, unordered_map<int, pair<int, int>>& firstword){
        int count = 0;
        for(int i = 0; i <board.size(); i++){
            for(int j = 0; j <board[0].size(); j++){
                if(board[i][j] == word) firstword.insert(pair<int, pair<int, int>>(count++, make_pair(i, j)));
            }
        }
    }   
    bool exist(vector<vector<char>>& board, string word) {
        vector<vector<int>> used(board.size(), vector<int>(board[0].size(), 0));
        unordered_map<int, pair<int, int>> firstword;
        findFirst(board, word[0], firstword);
        for(auto i: firstword){
            cout<<"i = "<<i.second.first<< "; j = "<<i.second.second<<endl;
            if(backtracking(board, word, i.second.first, i.second.second, 0, used)) return true;
        }
        return false;
    }
};

当然对于边界的判断可以放在最开始

bool backtracking(vector<vector<char>>& board, string word, int i, int j, int ptr, vector<vector<int>>& used){
    // 边界判断
    int rowSize = board.size();
    int colSize = board[0].size();
    if(j >= colSize || i >= rowSize || j < 0 || i < 0) return false;
    if(used[i][j] == 1) return false;  // 防止走倒路
    if(ptr == word.size() - 1 && board[i][j] == word[ptr]) return true;  // 路径长度已经符合并且是目标路径
    // 标记当前结点已经访问
    used[i][j] = 1;
    // 开始深度优先搜索  分为上下左右移动搜索  我根据示意图 按照右 下 左 上的顺序遍历
    if(isValid(word, ptr, board[i][j])){  // 如果当前位置符合可以则进行深度优先
            if(backtracking(board, word, i, j + 1, ptr + 1,used)) return true;    
            if(backtracking(board, word, i + 1, j, ptr + 1,used)) return true;   
            if(backtracking(board, word, i, j - 1, ptr + 1,used)) return true;                   if(backtracking(board, word, i - 1, j, ptr + 1,used)) return true;   
    }
    used[i][j] = 0;
    return false;
}


相关文章
|
存储 设计模式 前端开发
|
存储 分布式数据库
GaussDB分布式与单机模式的比较
【4月更文挑战第7天】GaussDB分布式与单机模式的比较
2119 5
|
前端开发 数据可视化 Java
OneCode 低代码引擎元数据设计
前言: 在百度百科中,元数据被定义为:描述数据的数据,对数据及信息资源的描述性信息。在低代码平台中元数据的使用也是非常广泛,从前端可视化的组件的prop 属性定义,后端OR Maping数据库表映射,以及支撑系统模块关联关系,权限分配支撑等等都是基础性的元数据。而对于低代码平台及工具而言,其最主要的一个功能也是配置管理低代码组件的元数据信息。在业务组件发生需求变更时尽量通过修改元数配置的方式来改变组件的业务特性。
|
编解码
数字基带传输系统 3
数字基带传输系统
336 0
|
移动开发 小程序 JavaScript
vue、小程序、uniapp的总结详情
vue、小程序、uniapp的总结详情
C#面向对象知识
C#面向对象知识
87 0
|
Java Linux Go
知识分享之Golang——Gin学习之Router路由的使用(二)
知识分享之Golang篇是我在日常使用Golang时学习到的各种各样的知识的记录,将其整理出来以文章的形式分享给大家,来进行共同学习。欢迎大家进行持续关注。 知识分享系列目前包含Java、Golang、Linux、Docker等等。
418 0
知识分享之Golang——Gin学习之Router路由的使用(二)
|
SQL 存储 安全
JDBC的API详解
JDBC的API详解
300 0
|
SQL Web App开发 监控
PostgreSQL培训系列直播—第四章:应用开发者指南
内容概要 1、基本SQL语句用法2、数据类型、操作符3、数据库对象类型4、内置函数5、自定义函数sql, plpgsql6、高级SQL用法与应用场景7、事务隔离级别8、锁9、触发器、事件触发器、规则10、分区表11、异步消息 目标 1、学习数据库的使用,数据类型、操作符、对象类型内置函数,高级SQL用法、事务隔离级别和锁。
3574 0
|
jenkins Java 测试技术
Jenkins 踩坑(四)|基于接口自动化测试完成 Jenkins+GitHub+Allure 的结合
Jenkins 踩坑(四)|基于接口自动化测试完成 Jenkins+GitHub+Allure 的结合