目录
一 树的基本了解
1.树的定义:
树是一种非线性的结构,由一个根节点和M个子树组成。在树型结构中,子树之间不能有交集。
2.树的概念:
- 节点的度:一个节点含有的子树的个数。如A的度为3
- 树的度:所有节点度的最大值。如上图树的度为3
- 叶子节点:度为0的节点。如EFGHIJ
- 父节点/双亲结点:一个节点有子节点,称这个节点为子节点的父节点。如A是B的父节点
- 子节点/孩子节点:一个节点有父节点,称这个节点为父节点的子节点。如E是B的子节点
- 根节点:没有父亲节点的节点。如A
- 节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推。
- 树的高度:最大的节点层次。如图中树的高度为3
- 兄弟节点:具有相同的父亲节点。如EFG
- 堂兄弟节点:双亲在同一层的节点。如EH
- 节点的祖先:从根到该节点所经分支上的所有节点。如A是所有节点的祖先
- 子孙:以某节点为根的子树任意分支上的节点。如所有节点都是A的子孙
- 森林:m棵互不相交的树组成。
二叉树
1.二叉树简介
- 二叉树的概念
一棵二叉树由根节点加上左子树和右子树组成,二叉树不存在度大于2的节点,子树有左右之分,次序不能颠倒。
- 特殊二叉树
满二叉树:一棵二叉树的每层节点都达到最大值,也就是说满二叉树的层数为k,节点个数为
。
完全二叉树:完全二叉树是由满二叉树引出来的。对于深度为k、有n个节点的二叉树,当且仅当每一个节点都与深度为k的满二叉树中编号为0-n-1的节点对应,称为完全二叉树。(从上到下,从左到右)
- 二叉树的性质
1.若根节点层数为1,则一棵非空二叉树的第 i 层最多有
个节点
2.深度为k的二叉树的最大节点数为
(k>=0)
3.叶子结点个数为n0,度为2的节点个数为n2,则n0=n2+1
(节点为n的树有n-1条边)
4.n个节点的完全二叉树的深度k在
上取整
5.具有n个节点的二叉树,按照从上到下从左到右的顺序对所有的节点从0开始编号,对于序号为i的节点:
① i>0 双亲序号:(i - 1)/2;i=0 i为根节点序号
②2i+1<n 左子树序号:2i+1
③2i+2<n 右子树序号:2i+2
练习:
- 二叉树的存储
二叉树的存储结构分为:顺序存储和链式存储
// 孩子表示法 class Node { int val; // 数据域 Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树 Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树 }
2、二叉树的操作
❤️创建一棵简单的二叉树
public class BinaryTree {
//节点
static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val){
this.val = val;
}
}
public TreeNode createTree(){
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
TreeNode H = new TreeNode('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
C.left = F;
C.right = G;
E.right = H;
return A;
}
}
❤️二叉树的遍历
1、前序遍历:根-左-右
2、中序遍历:左-根-右
3、后序遍历:左-右-根
4、层序遍历:自上而下,自左向右
练习:
❤️二叉树的基本操作
1、获取树中节点的个数
遍历思路:遍历一个节点计数一次
子问题思路:左子树节点个数+右子树节点个数+1
2、获取叶子节点个数
遍历思路:判断左右节点是否为空,为空的就是叶子节点
子问题思路:求当前root有多少个叶子节点。叶子节点=左树叶子节点+右树叶子节点
3、获取第k层节点个数
思路:root这棵树的第k层 = root.left第k-1层 + root.right第k-1层
4、获取二叉树高度
思路:求出左右子树最大高度+1
5、检测值为value的元素是否存在
6、层序遍历
//2.二叉树的分层遍历
public List<List<TreeNode>> levelOrder2(TreeNode root){
List<List<TreeNode>> ret = new ArrayList<>();
if(root == null){
return ret;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
//求当前队列大小
int size = queue.size();
List<TreeNode> tmp = new ArrayList<>();
while(size != 0){
//出队列4次
TreeNode cur = queue.poll();
tmp.add(cur);
size--;
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(root.right);
}
}
ret.add(tmp);
}
return ret;
}
7、判断完全二叉树
/*判断是否为完全二叉树*/
public boolean isCompleteTree(TreeNode root){
if(root == null){
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
if(cur != null){
queue.offer(cur.left);
queue.offer(cur.right);
}else {
break;
}
}
while (!queue.isEmpty()){
TreeNode cur = queue.poll();
if(cur != null){
return false;
}
}
return true;
}
三 二叉树的练习
1.检查两棵树是是否相同
https://leetcode-cn.com/problems/same-tree/
/*检查两棵树是否相同(结构上、节点上的值*/
public boolean isSameTree(TreeNode p, TreeNode q){
//一个为空另一个不为空
if(p == null && q != null || p != null && q == null){
return false;
}
//两个都为空
if(p == null && q == null){
return true;
}
//两个都不为空但值不相同
if(p.val != q.val){
return false;
}
//两个都不为空且值相同
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
2.检测二叉树root中是否包含subRoot
https://leetcode-cn.com/problems/subtree-of-another-tree/
/*检验二叉树root中是否包含subRoot*/
public boolean isSubTree(TreeNode root, TreeNode subRoot){
if(root == null) return false;
if(isSameTree(root, subRoot)){
return true;
}
if(isSameTree(root.left, subRoot)){
return true;
}
if(isSameTree((root.right, subRoot)){
return true;
}
return false;
}
3.翻转二叉树
https://leetcode-cn.com/problems/invert-binary-tree/
//翻转二叉树
public TreeNode invertTree(TreeNode root){
if(root == null){
return null;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
4.平衡二叉树判断
https://leetcode-cn.com/problems/balanced-binary-tree/
/*平衡二叉树的判断:每个节点的左右子树高度差不大于1*/
public boolean isBalanced(TreeNode root){
if(root == null){
return false;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.left);
return Math.abs(leftHeight-rightHeight)<=1
&& isBalanced(root.left)
&& isBalanced(root.right);
}
5.对称二叉树
https://leetcode-cn.com/problems/symmetric-tree/
/*判断对称二叉树*/
public boolean isSymmetric(TreeNode root){
if(root == null){
return true;
}
return isSymmetricChild(root.left, root.right);
}
public boolean isSymmetricChild(TreeNode p, TreeNode q){
//2.左右子树有一边为空,不对称
if(p == null && q != null || p != null && q == null){
return false;
}
//2.只有一个根节点,是对称的
if(p == null && q == null){
return true;
}
//3.左右子树都存在但是值不同
if(p.val != q.val){
return false;
}
//4.左右子树都存在并且值也相同
return isSymmetricChild(p.left, q.left)
&& isSymmetricChild(p.right, q.right);
}
6.遍历二叉树
/*遍历二叉树*/
//1.使用队列
public void levelOrder1(TreeNode root){
if(root == null){
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
System.out.println(cur.val+" ");
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(root.right);
}
}
}
7.非递归前序遍历二叉树
/*二叉树前序非递归遍历*/
public void preOrderNor(TreeNode root){
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode top = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
System.out.println(cur.val + " ");//放进去,打印
cur = cur.left;
}
top = stack.pop();
cur = top.right;
}
}
8.非递归中序遍历二叉树
/*二叉树中序非递归遍历*/
public void inOrderNor(TreeNode root){
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode top = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
top = stack.pop();
System.out.println(top.val + " ");
cur = top.right;
}
}
9.非递归后序遍历二叉树
/*二叉树后序非递归遍历*/
public void postOrderNor(TreeNode root){
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode top = null;
TreeNode prev = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
top = stack.peek();
if(top.right == null || top.right == prev){
stack.pop();
System.out.println(top.val + " ");
prev = top;//当前节点被打印了
}else{
cur = top.right;
}
}
}