1.树的介绍以及树的基本概念和性质
2.二叉树介绍以及二叉树的性质
3.二叉树的构建:穷举创建,递归创建
4.二叉树的基本操作
之前我们介绍了顺序表,链表,以及栈和队列,这几种数据结构都属于线性结构,而我们接下来要讲的树属于非线性结构~
树的数据结构相当于一颗倒挂的树,由n个节点组成,最顶部的节点叫做根节点,最底下的节点称为叶子节点
1.根节点没有前驱节点的信息
2.每个节点都包含1个或者多个节点的后继(比如说根节点A分叉为两个后继节点B和C,也就是说A节点包含B和C节点的信息)
3.树是递归实现的
一棵树包含多个子树,每个子树又有自己的根节点,比如说A节点两边是左树B和右树C,左树B两边也有左树D和右树E……以此类推
接下来来认识一下树的一些常用概念性质(结合下图来认识)
(1)结点的度:一个结点含有子树的个数称为该结点的度;简单来说就是看结点分了多少个叉,A节点分了两个叉,那么A节点的度为6,D结点的度为1,F结点的度为3……
(2)树的度:就是这一整棵树多个子树的度中的最大度,A的度为6,F的度为3……度为6是最大的,所以该树的最大度为6
(3)叶子结点(终端结点):叶子结点也叫终端结点,表示度为0的结点,度为0的结点就是该结点没有分叉,那么B,C,H,I,P,Q,K,L,M,N这些度为0的结点就统称为叶子结点
(4)父结点(双亲结点):父结点也叫做双亲结点,某个结点的前驱就是该结点的父结点,比如说B的前驱是A,那么A就是B结点的父结点,B就是A结点的子结点
(5)孩子结点(子结点):子结单就是某个结点的后继,比如A的后继有B,C,D,E,F,G 这些结点都属于A的子结点,因此!!父结点和子结点是相对而言的~
(6)根结点:一棵树中,没有双亲结点的结点;如上图:A(也就是一颗完整的树的第一个结点)
(7)结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推,因此上图这颗树一共有4层~ A在第一层,BCDEFG在第二层,HIJKLMN在第三层,PQ在第四层
(8)树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
树的以下概念只需了解,在看书时只要知道是什么意思即可:
(1)非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等节点为分支结点
(2)兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
(3)堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
(4)结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
(5)子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
(6)森林:由m(m>=0)棵互不相交的树组成的集合称为森林
接下来说说二叉树,二叉树才是重点
什么是二叉树?顾名思义,就是两个分叉~简单来说,二叉树的每个结点最多只能分两个叉~
棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成
意思就是说,一个空结点可以是二叉树,没有结点也可以叫二叉树,然后二叉树分为左右两棵子树,每个结点都可以分为左右两颗子树
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
(图片来源:比特高博)
然后还有两种特殊的二叉树要介绍一下
(1)满二叉树:简单来说,就是除了叶子结点不分叉以外,其余的所有结点都尽它们所能分成两个叉
(2) 完全二叉树:这树的意思就是,你给一整棵树编号,同时从上到下,从左到右去数编号,结点是连在一起的
看绿框里面的两颗二叉树,第一颗是满二叉树,给它编号后,结点是完美的连起来的(从上到下,从左到右),第二棵树是完全二叉树,从1~6,也是连起来的,至于黄色框的树,本来从5要到6的,结果6没了,意思就是说,从5要到达3的左孩子结点,而不是右孩子结点,要连起来,不能空一个结点,或者说是隔着一个结点~
接下来说说二叉树的性质~(重点)
推导
(1)求第i层的结点,求不出来,但是第i层最大的结点数为 2的i-1次方
(2)求的是一整颗深度为k的二叉树的结点数最大为2的k次方-1个
(3)接下来推到一下 n0 = n2 +1 怎么来的
设结点数为N,n0为叶子结点,n1为度为1的结点,n2为度为2的结点
那么 N = n0 + n1 + n2
一颗二叉树的边为 N-1(有多少个结点,该数就有多少条-1条边)
然后度为1的结点n1提供一条边,度为2的结点n2提供两条边
因此,N-1 = n1 + 2*n2 意思就是有n1条边加上两倍n2条边,就是整棵树的边数
之后联立
1) N = n0 + n1 + n2
2) N - 1 = n1 + 2 * n2
可求得 n0 = n2 + 1;
(4)求一棵树的深度,用 log 2为底,幂为(n+1),求的的结果向上取整,意思就是说,一辆车能载10个小朋友,但是25个小朋友就要用3辆车,而不是两辆车
接下来使用枚举来创建一颗二叉树
public class BinaryTree {
static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val){
this.val = val;
}
}
//二叉树的根节点
public TreeNode root;
//穷举创建二叉树
public void 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;
this.root = A;
}
//前序遍历
void preOrder(TreeNode root){
if(root == null){
return;
}
System.out.print(root.val + " ");
preOrder(root.left);
preOrder(root.right);
}
//中序遍历
void midOrder(TreeNode root){
if(root == null){
return;
}
midOrder(root.left);
System.out.print(root.val + " ");
midOrder(root.right);
}
//后序遍历
void postOrder(TreeNode root){
if(root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val + " ");
}
//获取树中节点个数(左树节点个数加上右树节点个数)
//子问题思路
int size1(TreeNode root){
int count = 0;
if(root == null){
return 0;
}else {
count++;
}
int leftNum = size1(root.left);
count += leftNum;
int rightNum = size1(root.right);
count += rightNum;
return count;
}
int size2(TreeNode root){
if(root == null) return 0;
return size2(root.left) + size2(root.right) + 1;
}
//遍历思路
public static int nodeCount;
void size3(TreeNode root){
if(root == null){
return;
}
nodeCount++;
size3(root.left);
size3(root.right);
}
/**
* 获取叶子节点个数
*/
//子问题思路
int getLeafNodeCount1(TreeNode root){
int count = 0;
if(root == null){
return 0;
}else if(root.left == null && root.right == null){
count++;
}
int leftCount = getLeafNodeCount1(root.left);
count += leftCount;
int rightCount = getLeafNodeCount1(root.right);
count += rightCount;
return count;
}
//遍历思路
public static int leafCount;
void getLeafCount(TreeNode root){
if(root == null){
return;
}
if(root.left == null && root.right == null){
leafCount++;
}
getLeafCount(root.left);
getLeafCount(root.right);
}
/**
* 获取第k层节点个数
*/
int KCount(TreeNode root, int k){
int count = 0;
if(root == null){
return 0;
}
if(k == 1){
count++;
}
int left = KCount(root.left,k-1);
count += left;
int right = KCount(root.right,k-1);
count += right;
return count;
}
int KCount2(TreeNode root, int k){
if(root == null) return 0;
if(k == 1){
return 1;
}
return KCount2(root.left, k-1) + KCount2(root.right,k-1);
}
/**
* 获取树的高度
*/
int Height(TreeNode root){
if(root == null) return 0;
return Math.max(Height(root.left),Height(root.right)) + 1;
}
/**
* 遍历看看树中是否存在value值
*/
TreeNode find(TreeNode root, char key){
if(root == null) return null;
if(root.val == key){
return root;
}
TreeNode ret1 = find(root.left, key);
if(ret1 != null){
return ret1;
}
TreeNode ret2 = find(root.right, key);
if(ret2 != null){
return ret2;
}
return null;
}
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
return maxDepth(root) >= 0;
}
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int leftHeight = maxDepth(root.left);
int rightHeight = maxDepth(root.right);
if(leftHeight >= 0 && rightHeight <= 0 && Math.abs(leftHeight-rightHeight) <= 1){
return Math.max(leftHeight,rightHeight) + 1;
}else {
return -1;
}
}
//层序遍历
void levelOrder(TreeNode root){
if(root == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
TreeNode cur = queue.poll();
System.out.print(cur.val + " ");
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
}
遍历分为4种遍历,都是用递归写的
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
层序遍历:从上到下,从左到右,依次读取