树——23、24、26、68

前序遍历

public TreeNode pruneTree (TreeNode root) {
 //前序
 doSomeThing();
root.left =pruneTree(root.left);
 root.right= pruneTree(root.right);
}

中序遍历

public TreeNode pruneTree (TreeNode root) {
root.left =pruneTree(root.left);
 //中序
 doSomeThing();
 root.right= pruneTree(root.right);
}

后续遍历

public TreeNode pruneTree (TreeNode root) {
root.left =pruneTree(root.left);
 root.right= pruneTree(root.right);
//后续
 doSomeThing();
}

剪枝 一般配合dfs

假设有一棵树,最上层的是root节点,而父节点会依赖子节点。
如果现在有一些节点已经标记为无效,我们要删除这些无效节点。
如果无效节点的依赖的节点还有效,那么不应该删除,如果无效节点和它的子节点都无效,则可以删除。
剪掉这些节点的过程,称为剪枝,目的是用来处理二叉树模型中的依赖问题。

回溯算法会应用「剪枝」技巧达到以加快搜索速度。有些时候,需要做一些预处理工作(例如排序)才能达到剪枝的目的。预处理工作虽然也消耗时间,但能够剪枝节约的时间更多

比较深入的介绍了 回溯 dfs

23、二叉搜索树的后序遍历

题解很棒

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }
    boolean recur(int[] postorder, int i, int j) {
        if(i >= j) return true;
        int p = i;
        while(postorder[p] < postorder[j]) p++;
        int m = p;
        while(postorder[p] > postorder[j]) p++;
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
    }
}

这种做法非常漂亮 采用这个

class Solution {
    // 要点:二叉搜索树中根节点的值大于左子树中的任何一个节点的值,小于右子树中任何一个节点的值,子树也是
    public boolean verifyPostorder(int[] postorder) {
        //if (postorder.length < 2) return true;
        if (postorder.length == 1) return true;//只有一个根节点 是二叉搜索树
        if (postorder.length == 0) return false;//空的 不是二叉搜索树
        return verify(postorder, 0, postorder.length - 1); 
    }

    // 递归实现
    private boolean verify(int[] postorder, int left, int right){
        if (left >= right) return true; // 当前区域不合法的时候直接返回true就好

        int rootValue = postorder[right]; // 当前树的根节点的值

        int k = left;
        while (k < right && postorder[k] < rootValue){ // 从当前区域找到第一个大于根节点的,说明后续区域数值都在右子树中
            k++;
        }

        for (int i = k; i < right; i++){ // 进行判断后续的区域是否所有的值都是大于当前的根节点,如果出现小于的值就直接返回false
            if (postorder[i] < rootValue) return false;
        }

        // 当前树没问题就检查左右子树
        if (!verify(postorder, left, k - 1)) return false; // 检查左子树

        if (!verify(postorder, k, right - 1)) return false; // 检查右子树

        return true; // 最终都没问题就返回true
    }
}

24、二叉搜索树中和为某一值的路径

回溯就是暴力试探法+不满足回溯法,树的前序遍历就是试探,树的后序遍历就是回溯

递归过程必定会产生回溯

题解 很清晰

class Solution {
    LinkedList<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> path = new LinkedList<>(); 
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        recur(root, sum);
        return res;
    }
    void recur(TreeNode root, int tar) {
        if(root == null) return;
        path.add(root.val);//根节点添加到path中
        tar -= root.val;//目标值逐渐减小
        if(tar == 0 && root.left == null && root.right == null)
            res.add(new LinkedList(path));//将此路径 path 加入 res 。
            //因为 path 是对象类型(区别于基本数据类型),res.add(path) 的意思是,把 path 的「地址」添加到 res 中,path 在深度优先遍历以后为空列表,所以您最后会看到 res 中是一个一个的空列表,它们都指向一块内存。
			//new ArrayList<>() 的作用是复制,把遍历到叶子结点的时候 path 的样子复制出来,到一个新的列表,这样写得话,最后 res 里保存的就是不同的变量了
        recur(root.left, tar); //左节点遍历
        recur(root.right, tar);//右节点遍历
        path.removeLast();//回溯时,将当前路径从节点中删除   这一步是执行完一条路径后才执行的
    }
}

26、二叉搜索树与双向链表

二叉搜索树的中序遍历为 递增序列

题解详细

class Solution {
    Node head, pre;
    public Node treeToDoublyList(Node root) {
        if(root==null) return null;
        dfs(root);

        pre.right = head;
        head.left =pre;//进行头节点和尾节点的相互指向,这两句的顺序也是可以颠倒的
        
        return head;
        
    }

    public void dfs(Node cur){
        if(cur==null) return;
        dfs(cur.left);

        //pre用于记录双向链表中位于cur左侧的节点,即上一次迭代中的cur,当pre==null时,cur左侧没有节点,即此时cur为双向链表中的头节点
        if(pre==null) head = cur;
        //反之,pre!=null时,cur左侧存在节点pre,需要进行pre.right=cur的操作。
        else pre.right = cur;
       
        cur.left = pre;//pre是否为null对这句没有影响,且这句放在上面两句if else之前也是可以的。

        pre = cur;//pre指向当前的cur,修改双向节点引用
        dfs(cur.right);//全部迭代完成后,pre指向双向链表中的尾节点,递归右子树
    }
}

解法也非常好

先遍历右子树 再根节点 再左子树 得到的就是升序链表

在正常的中序遍历完成时,pre是最右边/最大值的结点;也就是正序的表尾; 而系统又把pre当作正序表头,这样得到的双向链表pre先从左往右走,只有自己;再从右往左走,符合。 所以我们要么 按中序的反序 右-左-根;要么得到pre之后,再找到正序的表头(存下来)

public class Solution {
    TreeNode pre=null;
    public TreeNode Convert(TreeNode pRootOfTree) {
        if (pRootOfTree==null)
            return null;
        Convert(pRootOfTree.right);
        if (pre!= null){
            pRootOfTree.right=pre;
            pre.left=pRootOfTree;
        }
        pre=pRootOfTree;
        Convert(pRootOfTree.left);
        return pre;
    }
}

68、二叉搜索树的最近公共祖先

简单的二叉搜索树题解

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        while(root != null) {
            if(root.val < p.val && root.val < q.val) // p,q 都在 root 的右子树中
                root = root.right; // 遍历至右子节点
            else if(root.val > p.val && root.val > q.val) // p,q 都在 root 的左子树中
                root = root.left; // 遍历至左子节点
            else break;
        }
        return root;
    }
}

递归

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root.val < p.val && root.val < q.val)
            return lowestCommonAncestor(root.right, p, q);
        if(root.val > p.val && root.val > q.val)
            return lowestCommonAncestor(root.left, p, q);
        return root;
    }
}

二叉树的最近公共祖先

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null; // 如果树为空,直接返回null
        if(root == p || root == q) return root; // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
        TreeNode left = lowestCommonAncestor(root.left, p, q); // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
        TreeNode right = lowestCommonAncestor(root.right, p, q); // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
        if(left == null) return right; // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else if(right == null) return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else return root; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
    }
}
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null)
            return null;
        
        if(p == root || q == root)
            return root;
        
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        if(left != null && right != null)
            return root;
        return left == null ? right : left;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值