tree

//1.前序遍历

//使用递归的前序遍历
void preOrder(TreeNode root,List ans){
// 边界处理:如果树为空,那么不需要处理
if(root != null){
// 先访问根结点
ans.add(root.val);
// 再分别访问左子树
preOrder(root.left,ans);
// 再访问右子树
preOrder(root.right,ans);
}
}

//使用栈完成前序遍历
class Solution {
public List preorderTraversal(TreeNode root) {
// 用来进行递归的栈
Stack s = new Stack<>();
// 用来存放遍历的结果,不算在空间复杂度里面
List ans = new ArrayList<>();
// 开始利用栈来进行遍历
while(root != null || !s.empty()){
// 模拟递归的压栈过程
while(root != null){
s.push(root);
ans.add(root.val);
root = root.left;
}
// 当无法压栈的时候,将root.right进行压栈
root = s.peek();
s.poo();
root = root.right;
}
return ans;
}
}

//例 1:验证二叉搜索树
// 【题目】二叉搜索树有以下特点:

// 根结点的值大于所有的左子树结点的值
// 根结点的值小于所有的右子树结点的值
// 左右子树也必须满足以上特性

// 现给定一棵二叉树,判断是否是二叉搜索树。
//二叉搜索树的定义,本质上就是一个前序遍历。

class Solution {
private boolean ans = true;
private void preOrder(TreeNode root, Long l, Long r) {
// 1. 如果为空树
// 2. 如果已经有结点不满足BST的要求了
if(root == null ||!ans){
return;
}
// 检查当前结点是不是在影子二叉树的区间里面
// 这里相当于在检查两棵二叉树相同位置的结点
if(!(l<root.val && rot.val<r)){
ans = false;
return ;
}
// 这里同时遍历左子树,(l, root.val)就是影子二叉树的左子结点
preOrder(root.left,l,Long.valueOf(root.val));
// 这里同时遍历右子树,(root.val, r)就是影子二叉树的右子结点
preOrder(root.right,Long.valueOf(root.val),r);
}
public boolean isValidBST(TreeNode root){
ans = true;
preOrder(root,Long.MIN_VALUE,Long.MAX_VALUE);
return ans;
}
}

//例 2:目标和的所有路径
// 【题目】给定一棵二叉树,一个目标值。请输出所有路径,需要满足根结点至叶子结点之和等于给定的目标值。

// 输入:target = 9
//输出:[[5,4], [5,3,1]]
class Solution {
private List<List> ans = new ArrayList<>();
private void backtrace(TreeNode root,
List path, int sum, int target) {
// 如果已经是空树,那么没有必要看
if(root == null){
return ;
}
// 前序遍历,加上累计的和
sum += root.val;
// 将结点添加到路径中,相当于压栈一样
path.add(root.val);
if(root.left == null && root.right == null){
// 如果已经形成了一个有效路径!
if(sum == target){
// 添加到ans中
ans.add(new ArrayList<>(path));
}
}else{
// 回溯,分别再看子情况。
backtrace(root.left,path,sum,target);
backtrace(root.right,path,sum,target);
}
// 函数结束的时候弹栈,也要把结点从路径最后扔掉!
path.remove(path.size()-1);
}
public List<List> pathSum(TreeNode root,int sum){
// 路径
List path = new ArrayList<>();
backtrace(root,path,0,sum);
return ans;
}
}
//回溯,只不过借用了二叉树这个皮。反过来,在二叉树上进行回溯的代码模板,你也需要熟练掌握。

public class Solution {
 ArrayList<ArrayList<Integer>> res = new ArrayList<>(); 
/**
 * 
 * @param root TreeNode类 
 * @param sum int整型 
 * @return int整型ArrayList<ArrayList<>>
 */
public ArrayList<ArrayList<Integer>> pathSum (TreeNode root, int sum) {
    // write code here
    ArrayList<Integer> list = new ArrayList<Integer>();
    paths(root, sum, list);
    return res;
}

private void paths(TreeNode root, int sum, ArrayList list){
if(root == null)
return;
if(root.left == null && root.right == null && sum - root.val == 0){
list.add(root.val);
res.add(new ArrayList(list));
list.remove(list.size() - 1);
return;
}
list.add(root.val);
paths(root.left, sum - root.val, list);
paths(root.right, sum - root.val, list);
list.remove(list.size() - 1);
}
}

//递归中序遍历
void preOrder(TreeNode root, List ans) {
if(root != null){
// 先遍历左子树
preOrder(root.left,ans);
// 然后遍历中间的根结点
ans.add(root.val);
// 最后遍历右子树
preOrder(root.right,ans);
}
}

//使用栈完成中序遍历
class Solution {
    public List inorderTraversal(TreeNode root) {
Stack s= new Stack<>();
ArrayList and = new ArrayList<>();
// 注意这里的判断条件,需要root和stack都不空
while(root != null || !s.empty()){
// 往左边走,连续入栈,直到不能再走为止
while(root != null){
s.push(root);
root = root.left;
}
// 到达了最左边,把结点弹出来,进行遍历
root = s.peek();
s.pop();
ans.add(root.val);
// 转向右子树
root = root.right;
}
// 返回遍历的结果
return ans;
}
}

//例 3:验证二叉搜索树
//尝试用中序遍历解决这道题目
class Solution {
boolean ans = true;
Long preValue = Long.MIN_VALUE;
private void midOrder(TreeNode root) {
if(!ans){
return;
}
if(root != null){
midOrder(root.left);
// 只需要在中序遍历的时候,
// 与前面的值进行一下比较就可以了。
if(preValue>=root.val){
ans = false;
return;
}
// 比较完成之后,更新一下前面结点的值
preValue = Long.valueOf(root.val);
midOrder(root.right);
}
}
public boolean isValidBST(TreeNode root){
ans = true;
// 注意,为了防止root.val取到最小值INT_MIN
// 这里需要初始化为64位的最小值。
preValue = Long.MIN_VALUE;
midOrder(root);
return ans;
}
}

//删除二叉搜索树的结点
class Solution {
private void swapValue(TreeNode a, TreeNode b) {
//交换值
int t = a.val;
a.val = b.val;
b.val = t;
}
public TreeNode deleteNode(TreeNode root,int key){
// case 1空树。如果树是空树,那么只需要返回 null 即可。
if(root ==null){
return null;
}
if(key < root.val){
// case 2 如果 value 比根结点小,那么去左子树里面删除相应结点,
root.left = deleteNode(root.left,key);
}else if(key>root.val){
// case 3 如果 value 比根结点大,那么需要去右子树里面删除相应结点
root.right = deleteNode(root.right,key);
}else{
// case 4. case4又分为4种小情况,最后一种可以被合并掉。
// 所以这里只处理了三种。

        // 当前树只有一个结点,那么直接返回null
        if(root.left == null && root.right == null){
            return null;
        }else if(root.left != null){
            // 当前结点还有左子树
            // 那么需要从左子树中找个较大的值。如果被删除的结点有左子树。那么需要从左子树中找到最大值,然后与当前结点进行值交换。最后再在左子树中删除 value。
            TreeNode large = root.left;
            while(large.right != null){
                large = large.right;
            }
             // 交换再删除
            swapValue(root,large);
            root.left = deleteNode(root.left,key);
        }else if(root.right != null){
             // 当前结点还有右子树  如果要删除的结点只有右子树。那么需要从右子树中找到最小值,然后与当前结点进行值交换。然后再在右子树中删除 value。
            TreeNode small = root.right;
            // 那么需要从右子树中找个较小的值
            while(small.left != null){
                small = small.left;
            }
            // 交换再删除
            swapValue(root,small);
            root.right = deleteNode(root.right,key);
        }
    }
    return root;
}

}

//后序遍历
//递归后序遍历
void postOrder(TreeNode root, List ans) {
if(root != null){
// 先遍历左子树
postOrder(root.left,ans);
// 最后遍历右子树
postOrder(root.right,ans);
// 然后遍历中间的根结点
ans.add(root.val);
}
}

//使用栈完成后序遍历
class Solution {
public List postorderTraversal(TreeNode t) {
// 存放遍历的结果
ArrayList ans = new ArrayList<>();
// pre表示遍历时前面一个已经遍历过的结点
TreeNode pre = null;
Stack s = new Stack<>();
// 如果栈中还有元素,或者当前结点t非空
while(t != null || !s.isEmpty()){
// 顺着左子树走,并且将所有的元素压入栈中
while(t != null){
s.push(t);
t = t.left;
}
// 当没有任何元素可以压栈的时候
// 拿栈顶元素,注意这里并不将栈顶元素弹出
// 因为在迭代时,根结点需要遍历两次,这里需要判断一下
// 右子树是否遍历完毕
t = s.peek();
// 如果要遍历当前结点,需要确保右子树已经遍历完毕
// 1. 如果当前结点左子树为空,那么右子树没有遍历的必要
// 需要将当前结点放到ans中
// 2. 当t.right == pre时,说明右子树已经被打印过了
// 那么此时需要将当前结点放到ans中
if(t.right == null || t.right == pre){
// 右子树已经遍历完毕,放到ans中
ans.add(t.val);
// 弹栈
s.pop();
// 因为已经遍历了当前结点,所以需要更新pre结点
pre = t;
// 已经打印完毕。需要设置为空,否则下一轮循环
// 还会遍历t的左子树。
t = null;
}else{
// 第一次走到t结点,不能放到ans中,因为t的右子树还没有遍历。
// 需要将t结点的右子树遍历
t = t.right;
}
}
return ans;
}
}

//例 5:验证二叉搜索树
class Solution {
// 用来存放一棵树里面数值的区间
class Range {
public Long min = Long.MAX_VALUE;
public Long max = Long.MIN_VALUE;
public Range() {}
public Range(Long l, Long r) {
min = l;
max = r;
}
}
private boolean ans = true;
private Range emptyRange = new Range();
private Range postOrder(TreeNode root) {
// 如果是空树,或者已经判断不是一棵二叉搜索树了
// 那么就不需要再继续遍历了。
if (root == null || !ans) {
return emptyRange;
}
Range l = postOrder(root.left);
Range r = postOrder(root.right);
if (!(l.max < root.val && root.val < r.min)) {
ans = false;
// 当不符合的时候,返回任意区间都是可以的
return emptyRange;
}
// 需要取左子树最小值与当前结点的最小值。
// 需要取右子树最大值与当前结点的最大值
return new Range(Math.min(l.min, root.val),
Math.max(r.max, root.val));
}
public boolean isValidBST(TreeNode root) {
ans = true;
postOrder(root);
return ans;
}
}

//例 6:最低公共祖先
//【题目】给定一棵二叉树,和两个在树上的结点,返回这两个结点的最低公共祖先。比如 [5,3] 两个结点的最低公共祖先是结点 6。
class Solution {
private TreeNode ans = null;
private int postOrder(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return 0;
}
int lcnt = postOrder(root.left,p,q);
int rcnt = postOrder(root.right,p,q);

    if(lcnt ==1 && rcnt == 1){
        ans = root;
    }else if(lcnt == 1 || rcnt == 1){
        if(root == p || root == q){
            ans = root;
        }
    }
    return lcnt+rcnt+((root == p || root ==q)?1:0);

}
public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){
ans = null;
postOrder(root,p,q);
return ans;
}
}
//现在很多业务在解决性能问题的时候都会采用节流和防抖的方案,那么它们到底是解决了页面加载哪个阶段的问题?
//个人理解,防抖本质上是不允许某一行为的发生,节流的本质是允许某一行为的发生,但是发生的频率不能那么高。如果行为涉及到UI变更,那么他们两个都可以减少回流和重绘,优化的也就是在页面的渲染阶段;如果行为还涉及到网络请求的处理的话,那就减少了http请求数,也优化了页面请求的阶段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值