剑指 offer 12 矩阵中的路径(深度优先收缩+剪枝)

1. 题目描述

 

2. 算法分析

深度优先搜索:往一个方向一直搜,一直搜到条件不符,返回false,然后再向上进行递归。

递归分析:

  • 递归参数:当前元素的索引i 和 j,以及目标字符在字符串中的索引 k
  • 递归的终止条件:
    • 当匹配目标字符串,返回true;
    • 返回false
      • ​​​​​​​1. 行或列索引越界,也就是到达边界了
      • 2. 当前字符和目标字符不匹配
      • 3. 当前字符已经被访问过了(本题使用的是,访问过一遍,就将该字符修改为 ' / ',然后合并到2中进行判断了。

​​​​​​​递归流程:

  1. 先将当前的元素进行标记,然后将字符值存入临时变量temp,然后将当前board[i][j] = ' / ',代表当前已经被访问。(剪枝的过程)
  2. 然后再讲temp和目标字符进行对比
    1. ​​​​​​​符合,就继续按照 上- 下- 左- 右 递归
    2. 不符合,直接返回false
  3. 在当前字符递归结束后,就讲temp的值,还给board[i][j]
    1. ​​​​​​​为啥需要还回去呢?
    2. 递归搜索匹配字符串过程中,需要 board[i][j] = '/' 来防止 ”走回头路“ 。当匹配字符串不成功时,会回溯返回,此时需要board[i][j] = tmp 来”取消对此单元格的标记”。 在DFS过程中,每个单元格会多次被访问的, board[i][j] = '/'只是要保证在当前匹配方案中不要走回头路。​​​​​​​

3. 代码

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                if(dfs(board,words,i,j,0))
                    return true;
            }
        }
        return false;
    }

    public boolean dfs(char[][] board,char[] word,int i,int j,int k){
        if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]){
            //到边界,或者不匹配目标字符
            return false;
        }
        if(k == word.length - 1) //当匹配完了word,返回true
            return true; 
        char temp = board[i][j];
        board[i][j] = '/';
        //开始递归,上 下 左 右
        boolean res = dfs(board,word,i+1,j,k+1) || dfs(board,word,i-1,j,k+1) 
                        || dfs(board,word,i,j+1,k+1) || dfs(board,word,i,j-1,k+1);
        board[i][j] = temp;
        return res;
    }
}

4. 测试结果

 

 

2. 系统设计 1.用到的抽象数据类型的定义 图的抽象数据类型定义: ADT Graph{ 数据对象V:V是具有相同特性的数据元素的集合,称为顶点集 数据关系R: R={VR} VR={<v,w>|v,w∈V且P(v,w),<v,w>表示从v到w的弧, 谓词P(v,w)定义了弧<v,w>的意义或信息} 基本操作P: CreatGraph(&G,V,VR) 初始条件:V是图的顶点集,VR是图弧的集合 操作结果:按V和VR的定义构造图G DestroyGraph(&G) 初始条件:图G存在 操作结果:销毁图G InsertVex(&G,v) 初始条件:图G存在,v和图顶点有相同特征 操作结果:在图G增添新顶点v …… InsertArc(&G,v,w) 初始条件:图G存在,v和w是G两个顶点 操作结果:在G增添弧<v,w>,若G是无向的则还增添对称弧<w,v> …… DFSTraverse(G,Visit()) 初始条件:图G存在,Visit是顶点的应用函数 操作结果:对图进行深度优先遍历,在遍历过程对每个顶点调用函数Visit一次且仅一次。一旦Visit()失败,则操作失败 BFSTraverse(G,Visit()) 初始条件:图G存在,Visit是顶点的应用函数 操作结果:对图进行广度优先遍历,在遍历过程对每个顶点调用函数Visit一次且仅一次。一旦Visit()失败,则操作失败 }ADT Graph 栈的抽象数据类型定义: ADT Stack{ 数据对象:D={ai|ai∈ElemSet,i=1,2,…,n,n≥0} 数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,…,n} 约定an端为栈顶,ai端为栈底 基本操作: InitStack(&S) 操作结果:构造一个空栈S DestroyStack(&S) 初始条件:栈S已存在 操作结果:将S清为空栈 StackEmpty(S) 初始条件:栈S已存在 操作结果:若栈S为空栈,则返回TRUE,否则FALSE …… Push(&S,e) 初始条件:栈S已存在 操作结果:插入元素e为新的栈顶元素 Pop(&S,&e) 初始条件:栈S已存在且非空 操作结果:删除S的栈顶元素,并用e返回其值 StackTraverse(S,visit()) 初始条件:栈S已存在且非空 操作结果:从栈底到栈顶依次对S的每个数据元素调用函数visit(),一旦visit()失败,则操作失效 }ADT Stack 队列的抽象数据类型定义: ADT Queue{ 数据对象:D={ai|ai∈ElemSet,i=1,2,…,n,n≥0} 数据关系:Rl={<ai-1,ai>|ai-1,ai∈D,i=2,…,n} 约定其ai端为队列头,an端为队列尾。 基本操作: InitQueue(&Q) 操作结果:构造一个空队列Q DestroyQueue(&Q) 初始条件:队列Q已存在 操作结果:队列Q被销毁,不再存在 QueueEmpty(Q) 初始条件:队列Q已存在 操作结果:若Q为空队列,则返回TRUE,否则FALSE …… EnQueue(&Q,e) 初始条件:队列Q已存在 操作结果:插入元素e为Q的新的队尾元素 DeQueue(&Q,&e) 初始条件:Q为非空队列 操作结果:删除Q的队头元素,并用e返回其值 }ADT Queue 2.主程序的流程: 调用CreateDN函数创建图的邻接表G; 调用PrintDN函数输出邻接表G; 调用DFSTraverse函数深度优先遍历图; 调用BFSTraverse函数广度优先遍历图
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值