二叉树 Binary Tree

二叉树的基本概念

1. 什么是二叉树

  • 二叉树: 是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树
  • 二叉搜索树:
  1. 是一棵空树,
  2. 或者是具有下列性质的二叉树:
  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • . 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
  • 如何判断一颗树是二叉搜索树
  1. 二叉树的中序一定是递增关系
  2. 二叉树的左子树范围 – (root.val, -infinity)
    二叉树的右子树范围 – (root.val, infinity)

2. 二叉树的优点和缺点

优点

  • 二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势
  • 二叉树遍历的时间复杂度为O(N)
  • 二叉搜索树的查找时间复杂度为O(logN)

缺点

  • 在某些情况下,二叉树会成为一个链表
    在这里插入图片描述

3. 二叉树的基本名词

  • 根(root)
  • 高度(height): 最远处叶子节点深度为0,往上递增
  • 深度(depth): root深度为0,往下递增
  • 层数: root为第一层,往下递增)
  • 节点的度(degree):一个节点拥有子树的数量
  • 节点(叶子节点,分支节点)
    1. 叶子节点(leaf):没有子树的节点(度为0)
    2.分支节点(inner node): 度不为0的节点
  • 子树: 树中任何一个节点作为根,它下面的所有节点为子树。所以一个二叉树有O(n)个子树

4. 二叉树的性质

性质1: 二叉树的第i层最多有 2(i-1)个节点 (i >= 1)

证明:
Base Case:
第一层: 2(1-1)= 1.
第二层: 2(2-1)= 2
Assumption:
假设第k-1层最多有节点2(k-2),则需证明第k层最多有2(k-1) 个节点
Proof:
因为每个节点最多有两个子节点, 则第k层最多有节点 2*2(k-2) = 2 (k-1)

性质2: 深度为k(k>=0)的二叉树最少有k个结点,最多有2k- 1个结点

证明:
在具有相同深度的二叉树中,仅当每一层都含有最大结点数时,其树中结点数最多。因此利用性质1可得,深度为k的二叉树的结点数至多为:
20+21+…+ 2(k-1) = 2k-1

所以如果一个二叉树有N个节点, 则最少有 log(N+1)层
2k+1 = N
2k = N+1
k = log(N+1)

性质3:在任意-棵二叉树中,若终端结点(叶子节点)的个数为n0,度为2的结点数为n2,则n0=n2+1。

证明*:

  • 因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)应等于0度结点数、1度结点(记为n1)和2度结点数之和: n=n0+n1+n2 (eq1) (eq1)
  • 另一方面,1度结点有一个孩子,2度结点有两个孩子,故二叉树中孩子结点总数是: n1+2*n2
  • 树中只有根结点不是任何结点的孩子,故二叉树中的结点总数又可表示为:n=n1+2*n2+1 (eq2)
  • 由式子1和式子2得到:n0=n2+1

5. 特别的二叉树

满二叉树 (Full Binary Tree)

国际定义:A binary tree in which each node has exactly zero or two children.In other words,every node is either a leaf or has two children
在这里插入图片描述

国内定义:
1、一个层数为k 的满二叉树总结点数为:2k-1
2、第i层上的结点数为:2i-1
3、一个层数为k的满二叉树的叶子结点个数(也就是最后一层):2k-1
在这里插入图片描述

完全二叉树 (Complete Binary Tree)

若设二叉树的深度为k,除第 k 层外,其它各层 (1~k-1) 的结点数都达到最大个数,第k 层所有的结点都连续集中在最左边,这就是完全二叉树。
在这里插入图片描述
下图就不是完全二叉树
在这里插入图片描述

平衡二叉树 (Balance Binary Tree)

平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1
在这里插入图片描述
下图就不是平衡二叉树
在这里插入图片描述

判断是否是平衡二叉树

// An highlighted block
class Solution {
    boolean isBalanced = true; 
    public boolean isBalanced(TreeNode root) {
        helper(root, 0);
        return isBalanced;
    }

    public int helper(TreeNode root, int count) {
        if(root == null) {
            return 0; //代表已经走到尽头,高度返回为0
        }
        
        int leftCount = helper(root.left, count); //计算左子树的高度
        int rightCount = helper(root.right, count); //计算右子树的高度
        if (leftCount - rightCount > 1 || leftCount - rightCount < -1) {
        //如果左右子树的高度相差超过1,则说明不是平衡二叉树,将isBalanced设置为false
            isBalanced = false; 
            
        }
        //选出左右子树中最高的子树,然后加1得出目前节点的高度
        count = (leftCount > rightCount) ? leftCount + 1 : rightCount + 1; 
        return count;
    }
}

二叉树的基本方法

1.插入方法

递归

   public TreeNode insertHelper(TreeNode root, int target) {
        if(root == null) {
            return new TreeNode(target);
        }

        if(target < root.val) {
            root.left = insertHelper(root.left, target);
        }
         
        if(target > root.val) {
             root.right = insertHelper(root.right, target);
        }

        return root;
    }

迭代

 public TreeNode insertIntoBST(TreeNode root, int val) {
        TreeNode node = root;
        //如果树为空
        if(root == null) {
            TreeNode temp = new TreeNode(val);
            root = temp;
            return root;
        }
        
        //正常情况
        while(node.left != null || node.right != null) {
            if(val < node.val) {
                if (node.left == null) {
                    TreeNode temp = new TreeNode(val);
                    node.left = temp;
                    return root;
                }
                node = node.left;
            }
            else {
                if (node.right == null) {
                    TreeNode temp = new TreeNode(val);
                    node.right = temp;
                    return root;
                }
                node = node.right;
            }
        }
  
		//如果只有root时
        TreeNode temp = new TreeNode(val);
        if(val < node.val) {
            node.left = temp;
        }
        else {
            node.right = temp;
        }
        return root;
    }

2. 查找方法

递归

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

class Solution {
	
    public TreeNode queryBST(TreeNode root, int target) {
        return queryHelper(root, target);
    }

    public TreeNode queryHelper(TreeNode root, int target) {
		if(root == null) {
			return null
		}
		
        if(root.val == target) {
            return root
        }

        else if(target < root.val) {
            root = queryHelper(root.left, target);
        }
         
        else if(target > root.val) {
            root = queryHelper(root.right, target);
        }

        return root;
    }
}


3. 删除方法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        return helper(root, key);
    }

    public TreeNode helper(TreeNode node, int key) {
        if(node == null) {
            return node;
        }
        //找到目标
        if(node.val == key) {
        	//如果没有子节点,返回null 
        	//目标node = null实现删除
            if(node.left == null && node.right == null) {
                return null;
            }
            //如果左节点为空,右节点不为空,返回node.right
            //目标node = node.right 实现删除
            if(node.left == null) {
                return node.right;
            }
            //如果右节点为空,左节点不为空,返回node.left
            //目标node = node.left 实现删除
            if(node.right == null) {
                return node.left;
            }
            //如果左右节点都不为空  
            else {
                //在目标节点的左子树中找到最大值
                TreeNode temp = findMax(node.left);
                //将目标节点的值替换为左子树中的最大值
                node.val = temp.val;
                //然后将左子树中最大值节点删除
                node.left = helper(node.left, temp.val);
                return node;
            }
        }
		
		//没有找到目标,继续查找
        if(key < node.val) {
            node.left = helper(node.left, key);
        }
        else {
            node.right = helper(node.right, key);
        }
        return node;
    }
	
	//找到最大值
    public TreeNode findMax(TreeNode node) {
        if (node.right == null) {
            return node;
        }
        return findMax(node.right);
    }
}

二叉树的时间复杂度分析

普通二叉树平衡二叉树
查找O(N)O(logN)
插入O(N)O(logN)
删除O(N)O(logN)

Note 1: 普通二叉树在最坏情况下是一个链表,所以时间复杂度是O(N)
Note 2: 平衡二叉树在最坏情况下操作次数不超过树的深度,时间复杂度是O(logN).

二叉树的遍历

1. 二叉树的遍历

前序 (DFS):根左右

  • 前序的第一个元素是根
  • 下图前序顺序:62, 41,28,19,17,16,15,30,22,13
    在这里插入图片描述

中序: 左根右

  • 二叉搜索树的中序遍历是有序递增线性结构
  • 二叉树搜索树的中序的倒序是有序递减线性结构
  • 下图中序顺序:19, 28, 17, 41, 15, 16, 62, 22, 30, 13
    在这里插入图片描述

后序: 左右根

  • 后序的最后一个元素是根
  • 下图后序顺序:19,17,28,15, 16, 41 22, 13, 30, 62
    在这里插入图片描述

层序 (BFS): 按层输出

  • 下图层序顺序:[62], [41,30], [28,16,22,13], [19,17,15]
    在这里插入图片描述

2. 二叉树遍历的实现

递归方法 (时间复杂度O(N), 空间复杂度O(h))

前序, 中序,后序

其中前序是二叉树的DFS
在这里插入图片描述

层序

递归方法使用DFS, 迭代方法使用BFS
在这里插入图片描述

迭代方法

前序

在这里插入图片描述
在这里插入图片描述

中序

在这里插入图片描述

后序

一般方法为:根左右(前序)-> 根右左 -> 左右根(后序)
在这里插入图片描述

层序

在这里插入图片描述

Morris 遍历(意义不大,不推荐使用)

特性
  • 递归和迭代中都借助了额外的空间(栈空间)
  • Morris遍历可以不使用额外的空间
  • 改变了原有的数据,在实践中不推荐使用
Morris 前序遍历
  • 右子树挂在左子树最右节点
  • morris前序遍历可以将树转换为链表

在这里插入图片描述

Morris 中序遍历
  • 根节点挂在左子树的最右节点
  • Morris中序不能将树转换为链表

在这里插入图片描述

Morris 后序遍历
  • 思路和递归方法一样,从根左右 — 根右左 — 左右根
  • 左子树挂在右子树最左节点 ----然后倒序输出
public class Solution {
    /**
     * @param root: A Tree
     * @return: Postorder in ArrayList which contains node values.
     */
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> nums = new ArrayList<>();
        TreeNode cur = null;
        while (root != null) {
            if (root.right != null) {
                cur = root.right;
                while (cur.left != null && cur.left != root) {
                    cur = cur.left;
                }
                if (cur.left == root) {
                    cur.left = null;
                    root = root.left;
                } else {
                    nums.add(root.val);
                    cur.left = root;
                    root = root.right;
                }
            } else {
                nums.add(root.val);
                root = root.left;
            }
        }
        Collections.reverse(nums);
        return nums;
    } 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值