算法之dfs

1)之前涉及dfs,用递归比较多,但是若递归次数非常多,可能栈溢出,那么就需要用非递归的方式解决了,递归是jvm自动分配方法栈,那非递归可以基于自定义栈实现最终逻辑;

二叉树的中序遍历
方法一基于递归(递归次数多了,可能会引起栈溢出)

    public static List<Integer> zhongxuBianli(TreeNode root){
        List<Integer> res = new ArrayList();
        inorder(root,res);
        System.out.println(res.toString());
        return res;
    }
          //力扣,94 二叉树的中序遍历 子方法
    public static void inorder(TreeNode root ,List<Integer> list){
        if(root == null)return;
        inorder(root.left,list);
        list.add(root.val);
        inorder(root.right,list);
    }

方法二基于自定义栈(不存在栈溢出问题)

    public static List<Integer> zhongxuBianliSelfStack(TreeNode root){
        LinkedList<TreeNode> linkedList = new LinkedList();
        ArrayList<Integer> list = new ArrayList();
        while (root!=null || !linkedList.isEmpty()){
            while (root!=null){
                linkedList.push(root);
                root=root.left;
            }
            root = linkedList.pop();
            list.add(root.val);
            root=root.right;
        }
        return list;
    }

公共类

 public static class TreeNode{
        int val;
        TreeNode left;
        TreeNode right;

        public TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
        public TreeNode(int val) {
            this.val = val;
        }
        public TreeNode() {
        }
    }

2)上述方法2稍作修改,就可以判断一棵树是否是二叉查找树,因为上边是中序遍历,如果是二叉查找树则满足升序排列,所以可以将当前pop出的一个数与前一个pop的数比较,若当前数小或等于前一个值,则直接返回false,表明当前不是二叉查找树;

 public static boolean panDuanErChaChaZhaoShu(TreeNode root){
        LinkedList<TreeNode> linkedList = new LinkedList();
        long beforeVal=Long.MIN_VALUE;
        while (root!=null || !linkedList.isEmpty()){
            while (root!=null){
                linkedList.push(root);
                root=root.left;
            }
            root = linkedList.pop();
            if(root.val <= beforeVal){
                return false;
            }
            beforeVal=root.val;
            root=root.right;
        }
        return true;
    }

3)上述方法继续变化,可以应对求二叉树最大深度问题

public static int erChaShuMaxDepth(TreeNode root){
        LinkedList<TreeNode> linkedList = new LinkedList();
        int max=0;
        TreeNode pop=null;
        while (root!=null || !linkedList.isEmpty()){
            while (root!=null){
                linkedList.push(root);
                root=root.left;
                max=Math.max(max,linkedList.size());
            }
            //这里用peek主要是如果右边有子节点则需要先看下右边子树的深度,而不能一概pop
            TreeNode peek = linkedList.peek();
            //第一个条件意思是如果右边无子节点,则直接pop当前节点
            // 第二个条件意思是如果右边是刚弹出的子节点,则表明是已经计算过深度的,可以直接pop当前节点
            if(peek.right ==null || peek.right == pop){
                pop = linkedList.pop();
            }else{
            //如果右边子节点存在且还没有遍历过,则更新root,准备下一轮循环放入栈中
                root=peek.right;
            }
        }
        return max;
    }

4)上述erChaShuMaxDepth方法其实是基于后序遍历,稍作修改即可实现二叉树的后序遍历,代码如下

  public static List<Integer> houXuBianLi(TreeNode root){
        LinkedList<TreeNode> linkedList = new LinkedList();
        ArrayList<Integer> list = new ArrayList();
        TreeNode pop=null;
        while (root !=null || !linkedList.isEmpty()){
            while (root!=null){
                linkedList.push(root);
                root=root.left;
            }
            TreeNode peek = linkedList.peek();
            if(peek.right==null || peek.right==pop){
                pop=linkedList.pop();
                list.add(pop.val);
            }else{
             //如果右节点存在且还未遍历,则准备进行处理
                root=peek.right;
            }
        }
        return list;
    }

或者不用peek方法,直接pop,但需要记录子节点pre,需要通过pre确认已经遍历过右子节点

			root = stack.pop();
            if (root.right == null || root.right == prev) {
                res.add(root.val);
                prev = root;
                root = null;
            } else {
                stack.push(root);
                root = root.right;
            }

前序遍历也是做一些修改

 public static List<Integer> qianXuBianLi(TreeNode root){
        LinkedList<TreeNode> linkedList = new LinkedList();
        ArrayList<Integer> list = new ArrayList();
        TreeNode pop=null;
        while (root !=null || !linkedList.isEmpty()){
            while (root!=null){
            	list.add(root.val);
                linkedList.push(root);
                root=root.left;
            }
                root=linkedList.pop();
                root=root.right;
        }
        return list;
    }

5)深度优先算法的回溯是一个常用方式,如二叉树从根到叶子,每到一个叶子,接着回溯即倒退,才能前往下一个叶子,当同一深度的节点遍历完了,再次倒退到上一深度,才能遍历另一边;
力扣第113题 路径总和 II

	LinkedList list1 = new LinkedList<List<Integer>>();
    LinkedList list2 = new LinkedList();
    public  List<List<Integer>> pathSum(TreeNode root,int targetSum){
        dfs113(root,targetSum);
        return list1;
    }
    public void dfs113(TreeNode root,int targetSum){
        if(root==null)return;
        list2.offerLast(root.val);
        targetSum-=root.val;
        if(root.left==null && root.right==null && targetSum==0){
            list1.add(new LinkedList<>(list2));
        }
        dfs113(root.left,targetSum);
        dfs113(root.right,targetSum);
        //回溯
        list2.pollLast();
    }

6)有的题目需要上下左右都进行搜索,如力扣第130题和力扣第200题就几乎一样;

力扣第130题 被围绕的区域

 public static void slove(char[][] board){
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; i++) {
            dfs130(board,i,0);
            dfs130(board,i,n-1);
        }
        for (int i = 1; i < n-1; i++) {
            dfs130(board,0,i);
            dfs130(board,m-1,i);
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if(board[i][j]=='A'){
                    board[i][j]='O';
                }else if(board[i][j]=='O'){
                    board[i][j]='X';
                }
            }
        }
    }
     //力扣 130 子方法
    public static void dfs130(char[][] board,int m,int n){
        if(m<0 || m>=board.length|| n<0 ||n>=board[0].length || board[m][n]!='O'){
            return;
        }
        board[m][n]='A';
        dfs130(board,m-1,n);
        dfs130(board,m+1,n);
        dfs130(board,m,n-1);
        dfs130(board,m,n+1);
    }

力扣第200题

    public int numIslands(char[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int count=0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if(grid[i][j]=='1'){
                    count++;
                    dfs200(grid,i,j);
                }
            }
        }
        return count;
    }
    //力扣 200 子方法
    public void dfs200(char[][] grid ,int i,int j){
        if(i<0 ||j<0 ||i>=grid.length ||j>=grid[0].length || grid[i][j]=='0'){
            return;
        }
        grid[i][j]='0';
        dfs200(grid,i-1,j);
        dfs200(grid,i+1,j);
        dfs200(grid,i,j-1);
        dfs200(grid,i,j+1);
    }

力扣第417题,也套用第130题解题框架,但结果有点不对,目前还未找到原因

//成员变量
 int[][] heights417;
 int m417, n417;
 public List<List<Integer>> pacificAtlantic(int[][] heights) {
        this.heights417 = heights;
        this.m417 = heights.length;
        this.n417 = heights[0].length;
        boolean[][] pacific = new boolean[m417][n417];//默认值为false,数组只要分配内存就会给默认值,即使作为局部变量;
        boolean[][] atlantic = new boolean[m417][n417];
        for (int i = 0; i < m417; i++) {
            dfs417(i, 0, pacific,0);
        }
        for (int i = 1; i < n417; i++) {
            dfs417(0, i, pacific,0);
        }
        for (int i = 0; i < m417; i++) {
            dfs417(i, n417-1, atlantic,0);
        }
        for (int i = 0; i < m417-1; i++) {
            dfs417(m417-1, i, atlantic,0);
        }
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        for (int i = 0; i < m417; i++) {
            for (int j = 0; j < n417; j++) {
                if (pacific[i][j] && atlantic[i][j]) {
                    List<Integer> cell = new ArrayList<Integer>();
                    cell.add(i);
                    cell.add(j);
                    result.add(cell);
                }
            }
        }
        return result;
    }
        //417. 太平洋大西洋水流问题  子方法
    public void dfs417(int row,int col,boolean[][] ocean,int diff){
        if(row < 0 || row>=m417 || col < 0  || col>=n417  || ocean[row][col] || diff < 0){
            return;
        }
        ocean[row][col]=true;
        if(row-1>=0){
            dfs417(row-1,col,ocean,heights417[row-1][col]-heights417[row][col]);
        }
        if(row+1 < m417){
            dfs417(row+1,col,ocean,heights417[row+1][col]-heights417[row][col]);
        }
        if(col-1 >=0){
            dfs417(row,col-1,ocean,heights417[row][col-1]-heights417[row][col]);
        }
        if(col+1 <n417){
            dfs417(row,col+1,ocean,heights417[row][col+1]-heights417[row][col]);
        }
    }
测试用例
new int[][]{{19,12,0,19,17,4,13,10,6,1,7,18,2,17,0,18,0,18,8,9,19,4,5,4,12,18,17,8,3,15,12,4,11},
             {5,9,10,19,4,8,5,4,9,14,16,5,7,16,6,19,12,9,1,5,18,8,3,19,8,11,15,11,15,19,13,13,14},
                {4,1,18,13,7,1,16,11,11,18,5,7,10,8,9,17,10,12,17,11,13,3,18,10,17,6,3,16,13,13,5,7,12},
                {11,5,19,15,16,19,14,15,6,10,17,5,19,19,17,16,12,12,19,13,3,18,0,4,7,3,12,2,16,6,17,19,18},
                {4,7,16,12,9,11,15,10,16,5,2,4,12,6,1,14,12,18,7,4,3,17,17,11,5,1,19,19,1,0,4,4,7},
                {19,8,16,9,14,5,15,11,15,12,5,10,19,14,10,11,13,15,8,8,2,14,6,2,2,7,1,5,19,10,14,3,4}}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

orcharddd_real

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

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

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

打赏作者

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

抵扣说明:

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

余额充值