目录
一. 树
1.1 树的定义
1.2 树的概念
二. 二叉树
2.1 二叉树的定义
2.2 二叉树的性质
2.3 二叉树的特殊类型
2.4 二叉树的遍历
层序遍历(广度优先遍历)
前序遍历(深度优先遍历)
中序遍历(深度优先遍历)
后序遍历(深度优先遍历)
前中后序遍历的两种视角
2.5 二叉树的基本操作
1. 计算二叉树结点个数
2. 计算二叉树叶子结点个数
3. 二叉树 k 层的结点个数
4. 二叉树的高度
5. 二叉树查找 val 是否存在
6. 判断是不是平衡二叉树
7. 判断两棵树是否相同
8. 判断两棵树是否互为镜像
9. 判断两棵树是否有包含关系
树是n个结点的有限集合,有且仅有一个根结点,其余结点可分为m个根结点的子树。
树(tree)是包含 n(n≥0)个结点,当 n=0 时,称为空树,非空树中条边的有穷集,在非空树中:(1)每个元素称为结点(node)。
(2)有一个特定的结点被称为根结点或树根(root)。
结点的度: 一个结点拥有子树的个数称为度。比如A的度为3,B的度为2,C的度为0。度为0的结点称为叶子结点(C,E,G,H,I)。树的度是树中所有结点的度的最大值,此树的度为3。
树的深度(高度):树中结点的最大层次称为树的深度或高度。此树的深度为4。
父结点和子结点:父节点A的子结点B,C,D;B,C,D也是兄弟结点。
森林:树的集合称为森林,树和森林之间有着密切的关系,删除一个树的根结点,其所有原来的子树都是树,构成森林,用一个结点连接到森林的所有树的根结点就构成树。
空树:空集合也是树,称为空树。空树中没有结点。
二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
性质1:二叉树的第 i 层上至多有 2^(i-1)(i≥1)个结点。
性质2:深度为 h 的二叉树中至多含有 2^h - 1 个结点。
性质3:若在任意一棵二叉树中,有 n0 个叶子节点,有 n2 个度为2的节点,则必有 n0=n2+1。
性质4:具有 n 个节点的满二叉树深为 log2n+1。
1、满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树。(高度为h,由2^h-1个节点构成的二叉树称为满二叉树)
2、完全二叉树:深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树。
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点 所做的操作依赖于具体的应用问题(比如:打印节点内容)。
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
this.left = null;
this.right = null;
}
}
按二叉树从上到下,从左到右依次打印每个节点中存储的数据。
import java.util.LinkedList;
import java.util.Queue;
public class 层序遍历 {
public void levelorder(TreeNode root){
if(root == null){
return;
}
Queue queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
TreeNode node = queue.poll();
System.out.printf("%c ", node.val);
if (node.left != null){
queue.offer(node.left);
}
if (node.right != null){
queue.offer(node.right);
}
}
System.out.println();
}
}
前序(preorder)遍历:根——>左——>右
import sun.reflect.generics.tree.Tree;
public class 前序遍历 {
//(前序遍历)采用递归方法的形式进行编写;
public static void preorder(TreeNode root){
//空树;
//跟的左右子树为空树;
//跟的左子树为空 && 跟的右子树不为空树;
//跟的右子树为空 && 跟的左子树不为空树;
//跟的左子树不为空 && 跟的右子树不为空树;
if(root == null){
//空树直接返回;
return;
}
if(root.left == null && root.right == null){
System.out.printf("%c ",root.val);
return;
}
if(root.left == null && root.right != null){
System.out.printf("%c ",root.val);
preorder(root.right);
return;
}
if(root.left != null && root.right == null){
System.out.printf("%c ",root.val);
preorder(root.left);
return;
}
if(root.left != null && root.right != null){
System.out.printf("%c ",root.val);
preorder(root.left);
preorder(root.right);
}
}
public static void preorder1(TreeNode root){
if(root != null){
System.out.printf("%c ",root.val);
preorder1(root.left);
preorder1(root.right);
}
//空树直接返回;
}
}
中序(inorder)遍历:左——>根——>右
public class 中序遍历 {
public static void inorder(TreeNode root){
if(root != null){
inorder(root.left);
System.out.printf("%c ", root.val);
inorder(root.right);
}
}
}
后序(postorder)遍历:左——>右——>根
public class 后序遍历 {
public static void postorder(TreeNode root){
if(root != null){
postorder(root.left);
postorder(root.right);
System.out.printf("%c ", root.val);
}
}
}
视角1:递归视角
前序遍历:【跟】【左子树】【右子树】
中序遍历:【左子树】【跟】【右子树】
后续遍历:【左子树】【右子树】【跟】
视角2:非递归视角
无论是前,中,后序,都是沿着这个图中的虚线在前进,会经过所有结点。观察树中的每个结点,在这个过程都会被路过3次。
前序遍历:在第一次经过结点时,打印该结点;
中序遍历:在第二次经过结点时,打印该结点;
后序遍历:在第三次经过结点时,打印该结点;
public class Tree {
//通过前序遍历的方式,计算一棵树的结点个数;
private static int nodeCount;
public static void calcNodeCountVersion1(TreeNode root){
if (root != null){
nodeCount++;
calcNodeCountVersion1(root.left);
calcNodeCountVersion1(root.right);
}
}
public static int calcNodeCountVersion2(TreeNode root){
if(root == null){
return 0;
}
//分别用递归的方法,去计算左右子树的结点个数;
int leftCount = calcNodeCountVersion2(root.left);
int rightCount = calcNodeCountVersion2(root.right);
return leftCount + rightCount + 1;
}
}
public class Tree {
//方法1---遍历统计
private static int leafCount;
private static void calcLeafCountVersion1(TreeNode root){
if(root !=null){
//判断当前经过的结点,是不是叶子结点
if(root.left ==null && root.right == null){
leafCount++;
}
calcLeafCount(root.left);
calcLeafCount(root.right);
}
}
//方法2:化简为小问题的方式
private static int calcLeafCountVersion2(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
int leftLeafCount = calcLeafCountVersion2(root.left);
int rightLeafCount = calcLeafCountVersion2(root.right);
return leftLeafCount + rightLeafCount;
}
}
public class Tree {
public static int calcKLevelNodeCount(TreeNode root,int k){
if(root == null){
return 0;
}
if(k ==1){
return 1;
}
int leftCount = calcKLevelNodeCount(root.left,k - 1);
int rihgtCount = calcKLevelNodeCount(root.right,k - 1);
return leftCount + rihgtCount;
}
}
public class Tree {
public static int calcHeight(TreeNode root){
if(root == null){
return 0;
}
int leftHeight = calcHeight(root.left);
int rightHeight = calcHeight(root.right);
return Integer.max(leftHeight,rightHeight) + 1;
}
}
public class Tree {
/**
* 在以 root 为跟的二叉树中,进行 val 的查找
* 前提:二叉树中的 val 不会出现重复
* @param root 二叉树的跟结点
* @param val 待查找的值
* @return val 所在的结点。
*/
public static TreeNode find(TreeNode root,int val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
TreeNode node = find(root.left,val);
if(node != null){
return node;
}
//返回右子树的查找结果
return find(root.right,val);
}
}
public class Tree {
private int height(TreeNode root) {
if (root == null) {
return 0;
}
return Integer.max(height(root.left), height(root.right)) + 1;
}
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
int left = height(root.left);
int right = height(root.right);
int diff = left - right;
// 高度差的绝对值 <= 1 <=> -1 <= diff && diff <= 1
if (diff < -1 || diff > 1) {
return false;
}
return isBalanced(root.left) && isBalanced(root.right);
}
}
public class Solution {
public boolean isSameTree(TreeNode p, TreeNode q){
if (p == null && q == null){
return true;
}
if (p == null || q == null){
return false;
}
return p.val == q.val && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
public class Solution {
public boolean isMirrorTree(TreeNode p, TreeNode q){
if (p == null && q == null){
return true;
}
if (p == null || q == null){
return false;
}
return p.val == q.val && isMirrorTree(p.left,q.right) && isMirrorTree(p.right,q.left);
}
public boolean isSymmetric(TreeNode root){
return isMirrorTree(root.left,root.right);
}
}
public class Solution {
private boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
// 保证:subRoot != null
private boolean find(TreeNode root, TreeNode subRoot) {
if (root == null) {
return false;
}
if (isSameTree(root, subRoot)) {
return true;
}
if (find(root.left, subRoot)) {
return true;
}
return find(root.right, subRoot);
}
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (subRoot == null) {
return true;
}
return find(root, subRoot);
}
}