题库练习总结(二)

32位处理器是指处理器的数据总线是32位的

数组从栈中分配空间,链表从堆中分配空间

因为数组是连续的空间
链表不用连续

HTTP状态码分类

1** :临时响应并需要请求者继续执行操作

2**:请求成功。操作被成功接收接收并处理

3**:重定向代码,需要进一步的操作以完成请求

4**:客户端错误,请求包含语法错误或者无法完成请求

5**:服务器错误,服务器在处理请求的过程中发生错误

常见的HTTP状态码:

2开头 (请求成功)表示成功处理了请求的状态代码。

200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
201 (已创建) 请求成功并且服务器创建了新的资源。
202 (已接受) 服务器已接受请求,但尚未处理。
203 (非授权信息) 服务器已成功处理了请求,但返回的信息可能来自另一来源。
204 (无内容) 服务器成功处理了请求,但没有返回任何内容。
205 (重置内容) 服务器成功处理了请求,但没有返回任何内容。
206 (部分内容) 服务器成功处理了部分 GET 请求。

3开头 (请求被重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。

300 (多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
305 (使用代理) 请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。
307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。

4开头 (请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。

400 (错误请求) 服务器不理解请求的语法。
401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
403 (禁止) 服务器拒绝请求。
404 (未找到) 服务器找不到请求的网页。
405 (方法禁用) 禁用请求中指定的方法。
406 (不接受) 无法使用请求的内容特性响应请求的网页。
407 (需要代理授权) 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
408 (请求超时) 服务器等候请求时发生超时。
409 (冲突) 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
410 (已删除) 如果请求的资源已永久删除,服务器就会返回此响应。
411 (需要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
412 (未满足前提条件) 服务器未满足请求者在请求中设置的其中一个前提条件。
413 (请求实体过大) 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414 (请求的 URI 过长) 请求的 URI(通常为网址)过长,服务器无法处理。
415 (不支持的媒体类型) 请求的格式不受请求页面的支持。
416 (请求范围不符合要求) 如果页面无法提供请求的范围,则服务器会返回此状态代码。
417 (未满足期望值) 服务器未满足"期望"请求标头字段的要求。

5开头(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。

500 (服务器内部错误) 服务器遇到错误,无法完成请求。
501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。

final关键字

在这里插入图片描述

在java中,final关键字可以用来修饰类,方法和变量

  • 修饰类
    当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
  • 修饰方法
    如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。即父类的final方法是不能被子类所覆盖的,也就是说子类是不能够存在和父类一模一样的方法的。但是如果父类的方法被private和final同时修饰,表示私有的,这时候子类继承不到这个方法,所以子类中可以定义相同的方法名和参数,编译不会报错;但是如果是public或protected和final修饰,则可以继承,此时子类中不能存在相同的方法。
  • 修饰变量
    当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。

StringBuilder,StringBuffer,String都是final的。

setDate()

d.setDate(n);
n表示一个月中的一天的一个数值(1 ~ 31):
0 为上一个月的最后一天
-1 为上一个月最后一天之前的一天
如果当月有 31 天:
32 为下个月的第一天
如果当月有 30 天:
32 为下一个月的第二天;
40 为下一个月的第9天;
setMonth(n),这里是0-11分别对应1-12月

OSI参考模型与TCP/IP参考模型

在这里插入图片描述

图像渲染

有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。

给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。

为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。

最后返回经过上色渲染后的图像。
在这里插入图片描述
大概思路就是,将初始元素的位置添加到队列中,变换颜色,然后取出,再将该元素的相邻且颜色与初始颜色相同的元素存入队列中,变换颜色,然后再取出一个,以此类推
使用bfs
利用队列实现bfs

class Solution {
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
    	//保存初始颜色
        int currColor = image[sr][sc];
        if (currColor == newColor) {
            return image;
        }
        int m = image.length;//行
        int n = image[0].length;//列
        Queue<int[]> queue = new LinkedList<int[]>();
        //将初始位置存入队列
        queue.offer(new int[]{sr, sc});
        //渲染颜色
        image[sr][sc] = newColor;
        while (!queue.isEmpty()) {
        	//取出队列中第一个值
        	//再将这个位置上下左右的其他位置添加到队列中
        	//以此类推,每取出一个值,就将这个值上下左右的位置添加到队列中
            int[] cell = queue.poll();
            int x = cell[0];
            int y = cell[1];
            //注意这种获取上下左右位置的元素的写法
            //用上了数组
            int[] dx = {1, 0, 0, -1};
            int[] dy = {0, 1, -1, 0};
            for (int i = 0; i < 4; i++) {
                int mx = x + dx[i], my = y + dy[i];
                if (mx >= 0 && mx < m && my >= 0 && my < n && image[mx][my] == currColor) {
                    queue.offer(new int[]{mx, my});
                    image[mx][my] = newColor;
                }
            }
        }
        return image;
    }
}

使用dfs
利用递归实现dfs

class Solution {
    int[] dx = {1, 0, 0, -1};
    int[] dy = {0, 1, -1, 0};

    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        int currColor = image[sr][sc];
        if (currColor != newColor) {
            dfs(image, sr, sc, currColor, newColor);
        }
        return image;
    }

    public void dfs(int[][] image, int x, int y, int currColor, int newColor) {
        image[x][y]=newColor;
        for (int i = 0; i < 4; i++) {
            int mx = x + dx[i], my = y + dy[i];
            //深搜条件
            if (mx >= 0 && mx < image.length && my >= 0 && my < image[0].length&&image[mx][my]==currColor) {
                dfs(image, mx, my, currColor, newColor);
            }
        }
    }
}

扫雷游戏

给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。

现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:

如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。
在这里插入图片描述

DFS

/**
 * @param {character[][]} board
 * @param {number[]} click
 * @return {character[][]}
 */
var updateBoard = function(board, click) {
    var x=click[0];
    var y=click[1];
    if(board[x][y]=='M'){
        board[x][y]='X';
    }else{
        dfs(board,x,y);
    }
    return board;
};
function dfs(board,x,y){
    //var count=0;
    var xlen=board.length;//行
    var ylen=board[0].length;//列
    var dx=[-1,0,0,1,-1,1,-1,1];
    var dy=[0,-1,1,0,-1,-1,1,1];
    var mcount=0;
    for(var i=0;i<8;i++){
        var mx=x+dx[i];
        var my=y+dy[i];
        //注意这里是my<ylen不是my<xlen!!!my要小于列数 目前还不知道为什么
        if(my>=0&&my<ylen&&mx>=0&&mx<xlen&&board[mx][my]=='M'){
            mcount++;
        }
    }
    if(mcount>0){
        board[x][y]=mcount.toString();
        return;
    }else{
        board[x][y]='B';
        for(var i=0;i<8;i++){
            var mx=x+dx[i];
            var my=y+dy[i];
            if(my>=0&&my<ylen&&mx>=0&&mx<xlen&&board[mx][my]=='E'){
                dfs(board,mx,my);
            }
        }
    }
}

BFS
与dfs差不多但是遇到不知道什么问题还没解决

从上到下打印二叉树-BFS应用

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
在这里插入图片描述
这里的关键是,怎么知道各层元素的数量。在将元素添加到queue中后,要保存queue中元素的个数大小,再进行循环。不能在循环中直接判断i<queue.size(),因为后面元素还会添加,这个size值会越来越大,因此会发生错误

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
    	//声明队列
        Queue<TreeNode> queue = new LinkedList<>();
        //存储结果
        List<List<Integer>> res = new ArrayList<>();
        if(root != null) queue.offer(root);
        while(!queue.isEmpty()) {
       		//保存每一层的元素 最后再将这个list添加到整体的list中
            List<Integer> list = new ArrayList<>();
            //注意这里 要先保存size!!!
            //因为后面的元素在不断的添加 所以size值会变 这样就会发生错误
            int size=queue.size();
            for(int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                //将这一层的元素添加到list中
                list.add(node.val);
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            //将list添加到结果集中
            res.add(list);
        }
        return res;
    }
}

以下是DFS的应用

求二叉树的最大深度

在这里插入图片描述
用DFS
树的深度等于左子树的深度和右子树的深度中的最大值+1.
这里用递归实现,本质上是对树做后序遍历

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }else{
        	//int left=maxDepth(root.left);
        	//int right=maxDepth(root.right);
            return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
        }
    }
}

用BFS
每遍历一层,层数+1

class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }else{
            Queue<TreeNode> queue=new LinkedList<>();
            int res=0;
            queue.offer(root);
            //一层一层遍历
            while(!queue.isEmpty()){
                res++;
                int size=queue.size();
                for(int i=0;i<size;i++){
                    TreeNode node=queue.poll();
                    if(node.left!=null){
                        queue.offer(node.left);
                    }
                    if(node.right!=null){
                        queue.offer(node.right);
                    }
                }
            }
            return res;
        }
    }
}

求二叉树的最小深度

用DFS

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var minDepth = function(root) {
    if(root==null){
        return 0;
    }else if(root.left==null&&root.right==null){
        //左右子树都为空的话 只剩下根节点 返回深度1
        return 1;
    }else{
        var left=minDepth(root.left);
        var right=minDepth(root.right);
        if(root.left==null||root.right==null){
            return left+right+1;
        }else{
            return (left<right?left:right)+1;
        }
    }
};

用BFS

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var minDepth = function(root) {
    if(root == null){
        return 0;
    }else{
        var queue=[];
        //因为根节点已经存在 所以初始值为1
        var count=1;
        queue.push(root);
        while(queue.length){
            var size=queue.length;
            for(var i=0;i<size;i++){
                //注意这里是用var声明 不需要用treenode声明!!!
                var node=queue.shift();
                //一旦碰到左右子树都为空的,表示已经到了最小深度了,直接返回
                //这是遍历结束条件 不能在外面判断!!!
                if(node.left==null&&node.right==null){
                    return count;
                }
                //注意这里不能用else if!!!!
                if(node.left!=null){
                    queue.push(node.left);
                }
                if(node.right!=null){
                    queue.push(node.right);
                }
            }
            count++;
        }
        return count;
    }
};

求N叉树的深度

这道题和上面的类似,只是每个节点的子节点数不确定,所以这里要用到循环,以及用list保存每个节点的深度,最后返回最大的深度

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    public int maxDepth(Node root) {
        if(root==null){
            return 0;
        }else if(root.children.isEmpty()){
            return 1;
        }else{
            List<Integer> list=new ArrayList<>();
            for(int i=0;i<root.children.size();i++){
                
                list.add(maxDepth(root.children.get(i)));
                
            }
            return Collections.max(list)+1;
        }
    }
}

判断平衡二叉树

方法一:先序遍历+判断深度 (从顶至底)
思路是通过比较某子树的左右子树的深度差 abs(maxDepth(root.left) - depth(root.right)) <= 1 是否成立,来判断某子树是否是二叉平衡树。若所有子树都平衡,则此树平衡。

算法流程
isBalanced(root) 函数: 判断树 root 是否平衡
特例处理: 若树根节点 root 为空,则直接返回 true ;
返回值: 所有子树都需要满足平衡树性质,因此以下三者使用与逻辑&& 连接;
abs(self.maxDepth(root.left) - self.maxDepth(root.right)) <= 1 :判断 当前子树 是否是平衡树;
self.isBalanced(root.left) : 先序遍历递归,判断 当前子树的左子树 是否是平衡树;
self.isBalanced(root.right) : 先序遍历递归,判断 当前子树的右子树 是否是平衡树;

maxDepth(root) 函数: 计算树 root 的深度
终止条件: 当 root 为空,即越过叶子节点,则返回高度 0 ;
返回值: 返回左 / 右子树的深度的最大值 +1 。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root!=null){
            //该树是平衡二叉树的条件是
            //左子树是平衡二叉树 右子树是平衡二叉树 且左右子树的深度差小于等于1
            return Math.abs(maxDepth(root.left)-maxDepth(root.right))<=1&&isBalanced(root.left)&&isBalanced(root.right);
        }else{
            return true;
        }

    }

    //求二叉树的深度
    public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }else{
            return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
        }
    }


}

方法二:后续遍历+剪枝 (从底至顶)
思路是对二叉树做后序遍历,从底至顶返回子树深度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。

算法流程
recur(root) 函数
返回值
当节点root 左 / 右子树的深度差≤1 :则返回当前子树的深度,即节点 root 的左 / 右子树的深度最大值 +1 ( max(left, right) + 1 );
当节点root 左 / 右子树的深度差 > 2 :则返回 -1 ,代表 此子树不是平衡树 。
终止条件
当 root 为空:说明越过叶节点,因此返回高度 0 ;
当左(右)子树深度为 -1 :代表此树的 左(右)子树 不是平衡树,因此剪枝,直接返回 -1 ;

isBalanced(root) 函数
返回值: 若 recur(root) != -1 ,则说明此树平衡,返回 true ; 否则返回 false。

class Solution {
    public boolean isBalanced(TreeNode root) {
        return dfs(root)==-1?false:true;
    }

    //求二叉树的深度
    public int dfs(TreeNode root) {
        if(root==null){
            return 0;
        }
        //从底到顶 求左子树的深度
        //注意这里不能+1!!!!
        int left=dfs(root.left);
        //如果发现左子树深度为-1 即不是平衡树 则立即返回 不再进行遍历
        if(left==-1){
            return -1;
        }
        //从底到顶 求右子树的深度
        int right=dfs(root.right);
        if(right==-1){
            return -1;
        }
        //判断根节点的左子树和右子树是否为平衡树
        return Math.abs(left-right)>1?-1:Math.max(left,right)+1;
    }
}

将有序数组转换为二叉搜索树

二叉搜索树中序遍历即是一个升序的
取数组最中间的位置作为根节点,左边的作为左子树,右边的作为右子树,这样构造的就能保证一定是平衡树

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return dfs(0,nums.length-1,nums);
    }
    public TreeNode dfs(int left,int right,int[] nums){
    	//结束递归条件
        if(left>right){
            return null;
        }else{
            int mid=(left+right)/2;
            TreeNode root=new TreeNode(nums[mid]);
            root.left=dfs(left,mid-1,nums);
            root.right=dfs(mid+1,right,nums);
            return root;
        }
    }
}

将有序链表转换二叉搜索树

思路与上面的相同,只是链表求中心节点的方法复杂一点。
第一种方法,可以遍历链表将其值存入List中,之后的与上面一题相同,直接根据索引求中间值即可
第二种方法,使用快慢指针法求链表的中心节点

求单向链表的中心结点-快慢指针法

初始时,快指针fast 和慢指针slow 均指向链表的左端点left。我们将快指针fast 向右移动两次的同时,将慢指针slow 向右移动一次,直到快指针到达边界(即快指针到达右端点或快指针的下一个节点是右端点)。此时,慢指针对应的元素就是中位数。

public ListNode getMedian(ListNode head) {
    ListNode fast = head;
    ListNode slow = head;
    while (fast != right && fast.next != right) {
        fast = fast.next;
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

将二叉搜索树转换成平衡二叉搜索树

在这里插入图片描述
先中序遍历搜索树,形成一个有序的数组,再对这个数组构造成平衡树

class Solution {
    public TreeNode balanceBST(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        getnum(root,list);
        return buildBST(0,list.size()-1,list);
    }
    public void getnum(TreeNode node,List<Integer> list){
        //中序遍历二叉搜索树
        //在list中保存
        if(node!=null){
            //注意这一段中序遍历的递归代码!!!
            //先序遍历和后序遍历同理!!!
            getnum(node.left,list);
            list.add(node.val);
            getnum(node.right,list);
        }
    }
    //将list中的有序的内容转换为平衡二叉树
    public TreeNode buildBST(int left,int right,List<Integer> list){
        if(left>right){
            return null;
        }else{
            int mid=(left+right)/2;
            TreeNode node=new TreeNode(list.get(mid));
            node.left=buildBST(left,mid-1,list);
            node.right=buildBST(mid+1,right,list);
            return node;
        }
    }
}

重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

/* function TreeNode(x) {
    this.val = x;
    this.left = null;
    this.right = null;
} */
function reConstructBinaryTree(pre, vin)
{
    // write code here
    if(pre==null||pre.length==0){
        return null;
    }else{
    	//存储后序遍历的值和索引
        let map=new Map();
        let length=pre.length;
        for(let i=0;i<length;i++){
            map.set(vin[i],i);
        }
        //构建二叉树
        let root=buildTree(pre, 0, length - 1, vin, 0, length - 1, map);
        return root;
    }
}

function buildTree(pre,preStart,preEnd,vin,vinStart,vinEnd,map){
    if(preStart>preEnd){
        return null;
    }else{
        //在前序遍历数组中获取根节点
        //第一个值就是根节点
        let rootval=pre[preStart];
        let root=new TreeNode(rootval);
        //如果只有一个根节点,直接返回
        if(preStart==preEnd){
            return root;
        }else{
            //找到根节点在vin数组中的索引
            let rootIndex=map.get(rootval);
            //左子树的个数
            let leftNodes=rootIndex-vinStart;
            //右子树的个数
            let rightNodes=vinEnd-rootIndex;
            //找到左子树和右子树的根节点
            let leftTree=buildTree(pre,preStart+1,preStart+leftNodes,vin,vinStart,rootIndex-1,map);
            let rightTree=buildTree(pre,preStart+leftNodes+1,preEnd,vin,rootIndex+1,vinEnd,map);
            //加到根节点上
            root.left=leftTree;
            root.right=rightTree;
            
            return root;
        }
    }
}

矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]

但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
在这里插入图片描述
思路
1.既然要在矩阵中匹配一个单词word,那么我们自然地想到,应该先遍历矩阵找到单词的开头。

2.找到开头后,有必要深入下去尝试匹配剩余字符。

3.深入匹配有4个方向可以选择,只要有1个方向匹配成功了,就说明找到了;只有4个方向都无法匹配出word,才说明这个开头不合适,应该继续遍历矩阵找新的开头。

4.找到新的开头,继续重复以上匹配过程。

5.由于匹配过程不能使用已经用过的字符,所以应当标记匹配过的字符。原始的想法是借助一个和board同样大小的矩阵boolean[][] visit = new boolean[r][c],r表示board的行数,c表示board的列数;

6.但是我们可以通过巧妙的方法,避免这个空间开销。具体方法是,如果我们已经访问过这个字符,那么先把这个字符赋值为一个矩阵中不可能存在的字符,比如’#’,就可以表示已访问过了。当这条路走不通了,恢复它本来的样子就可以了。(当然必须说明,这种取巧方法是靠运气的,因为题目并没有说明矩阵中字符的种类,只是恰好用到了一个测试案例里没有的字符作为标记)

7.为什么要恢复本来的样子?因为当前路走不通,只是因为开头不对。找到新的开头后,以前旧的开头访问过的字符对于新开头来说仍然是可以访问的。比如说,在以下矩阵匹配word = “aabc”

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
let dx = [1, 0, 0, -1];
let dy = [0, 1, -1, 0];
let sx = 0;
let sy = 0;
let isThrough = []; //记录坐标是否被走过
var exist = function(board, word) {
    let r=board.length;
    let c=board[0].length;
    let words=word.split('');
    //遍历矩阵,找到开头字符,当找到一个开头字符后,深度优先搜索匹配剩余字符
    for(let i=0;i<r;i++){
        for(let j=0;j<c;j++){
            if(board[i][j]==words[0]&&dfs(board,words,i,j,0)){
                return true;
            }
        }
    }
    return false;
};
function dfs(board,words,i,j,k){
    //越界或字符不匹配
    if(i<0||i>=board.length||j<0||j>=board[0].length||board[i][j]!=words[k]){
        return false;
    }
    //最后一个字符都匹配到了
    if(k==words.length-1){
        return true;
    }
    let temp = board[i][j];
    board[i][j] = '#';// 用这样的形式标记表示已访问
    // 只要一个方向匹配到了就可以
    let res = dfs(board, words, i - 1, j, k + 1)||dfs(board, words, i + 1, j, k + 1)||dfs(board, words, i, j-1, k + 1)||dfs(board, words, i, j+1, k + 1);
    board[i][j] = temp;      // 恢复未访问
    return res;
}

回文子串-动态规划

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
在这里插入图片描述
暴力解法

/**
 * @param {string} s
 * @return {number}
 */
var countSubstrings = function(s) {
    var count=0;
    for(var i=0;i<s.length;i++){
        for(var j=i+1;j<s.length;j++){
        	//截取字符串
            if(isHuiwen(s.substring(i,j+1))){
                count++;
            }
        }
    }
    return  count+s.length;

};
//判断是否是回文字符
function isHuiwen(str){
    var=0;
    var j=str.length-1;
    while(i<j){
        if(str.charAt(i)==str.charAt(j)){
            i++;
            j--;
        }else{
            return false;
        }
    }
    return true;
}

动态规划
在这里插入图片描述

/**
 * @param {string} s
 * @return {number}
 */
var countSubstrings = function(s) {
    var count=0;
    var dp=new Array(s.length);
    for(var i=0;i<dp.length;i++){
        dp[i]=new Array(s.length).fill(false);
    }

    //注意这里的循环 动态规划是从上到下来执行的!!!!
    for(var j=0;j<s.length;j++){
        for(var i=0;i<=j;i++){
            if(j-i==0){
                dp[i][j]=true;
                count++;
            }else if(j-i==1&&s[i]==s[j]){
                dp[i][j]=true;
                count++;
            }else if(j-i>1&&s[i]==s[j]&&dp[i+1][j-1]){
                dp[i][j]=true;
                count++;
            }
        }
    }
    return count;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值