二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分 。
二叉树是n个有限元素集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点。(以上来自百度对二叉树的解释)
二叉树是一种极其重要的数据结构,在生活和生产的很多方面都会使用到此数据结构。二叉树也是一种图,它也具有许多图所共有的性质,作为一种特殊的图,也作为一种极为特殊的树,二叉树存储数据有着得天独厚的优势。
树的概念
结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6
树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6
叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点
双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点;
根结点:一棵树中,没有双亲结点的结点;
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
树的高度或深度:树中结点的最大层次;
非终端结点或分支结点:度不为0的结点;
兄弟结点:具有相同父结点的结点互称为兄弟结点;
堂兄弟结点:双亲在同一层的结点互为堂兄弟;
结点的祖先:从根到该结点所经分支上的所有结点;
子孙:以某结点为根的子树中任一结点都称为该结点的子孙。
森林:由m(m>=0)棵互不相交的树组成的集合称为森林
二叉树的概念
1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 (i>0)个结点
2. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 (k>=0)
3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+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,否则无右孩子
二叉树的代码实现
二叉树的基本操作:
二叉树的创建:二叉树的创建在这里采用了简单的左右节点的创作方法,在之后的二叉树oj题解文章中,会进行二叉树的先序遍历,后序遍历,中序遍历的二叉树的创建。
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;
public int size(TreeNode root){
if(root == null){
return 0;
}
int left = size(root.left);
int right = size(root.right);
return left + right + 1;
}
二叉树的叶子节点的个数
也可以看作是子问题的思路
根节点的左侧和右侧如果全是null 那么说明它就是一个叶子节点,返回一,最后是左树的叶子节点加上右树的叶子节点就是所有的叶子节点。
int getLeafNodeCount(TreeNode root){
if(root == null){
return 0;
}
if(root.right == null && root.left == null){
return 1;
}
int left = getLeafNodeCount(root.left);
int right = getLeafNodeCount(root.right);
return left + right;
}
二叉树k层节点的个数
子问题的思路,k==1 即第一层的时候然会一个节点,当k 不等于第一层的时候,返回左边子树k层的节点加上右边子树k层的节点。
int getKLevelNodeCount(TreeNode root, int k){
if(root == null){
return 0;
}
if(k == 1){
return 1;
}
return getKLevelNodeCount(root.left,k - 1) + getKLevelNodeCount(root.right,k - 1);
}
二叉树的高度:
二叉树的高度就是取最大的高度,子问题思路就是每次比较左边子树和右边子树的高度,取得最大的高度加上根节点原来的高度1就是此层二叉树的高度。
int getHeight(TreeNode root){
if(root == null){
return 0;
}
int left = getHeight(root.left);
int right = getHeight(root.right);
return left > right ? left + 1 : right + 1;
}
检查元素value的值是否存在
子问题的思路,看一看左子树是不是有这个值,有的返回true,没有的话继续看右子树,如果右子树有,返回true,如果没有返回false。
TreeNode find(TreeNode root, int val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
TreeNode left = find(root.left,val);
if(left != null){
return left;
}
TreeNode right = find(root.right,val);
if(right != null){
return right;
}
return null;
}
层序遍历:
一层一层的遍历,运用循环和队列,使用的是迭代法
把每层的节点入队列,然后再依次出队列,根据队列的先入后出的原理,可以根据层序遍历的顺序让节点一次入队
public void levelOrder(TreeNode root){
if(root == null){
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
TreeNode node = queue.poll();
System.out.println(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
}
二叉树判断一棵树是否是一颗完全二叉树
与层序遍历相似,不同的点就是只要root不是null,就把左边和右边全部入队列,当队列不为空时,比较里面有没有除了null的值,有的话就跳出,不是完全二叉树
boolean isCompleteTree(TreeNode root){
if(root == null) {
return true;
}
Queue<TreeNode> qu = new LinkedList<>();
qu.offer(root);
while(!qu.isEmpty()){
TreeNode cur = qu.poll();
if(cur != null){
qu.offer(cur.left);
qu.offer(cur.right);
}else{
break;
}
}
while(!qu.isEmpty()){
TreeNode cur = qu.poll();
if(cur != null){
return false;
}
}
return true;
}
全部代码:
import com.sun.source.tree.Tree;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class TreeBuilder {
public 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;
}
// 获取树中节点的个数
/**
* 子问题思路,左边的节点的个数加上右边的节点的个数加上本身节点的个数就是当前树的高度
* 终止条件就是递归到根节点是空
* @param root
* @return
*/
public int size(TreeNode root){
if(root == null){
return 0;
}
int left = size(root.left);
int right = size(root.right);
return left + right + 1;
}
// 获取叶子节点的个数
/**
* 叶子节点的左边是null 右边也是null 这就是终止的条件
* 可以看成是子问题的思路,一棵树的叶子节点包括了左子树的叶子节点加上右子树的叶子节点
* @param root
* @return
*/
int getLeafNodeCount(TreeNode root){
if(root == null){
return 0;
}
if(root.right == null && root.left == null){
return 1;
}
int left = getLeafNodeCount(root.left);
int right = getLeafNodeCount(root.right);
return left + right;
}
// 子问题思路-求叶子结点个数
// 获取第K层节点的个数
int getKLevelNodeCount(TreeNode root, int k){
if(root == null){
return 0;
}
if(k == 1){
return 1;
}
return getKLevelNodeCount(root.left,k - 1) + getKLevelNodeCount(root.right,k - 1);
}
// 获取二叉树的高度
int getHeight(TreeNode root){
if(root == null){
return 0;
}
int left = getHeight(root.left);
int right = getHeight(root.right);
return left > right ? left + 1 : right + 1;
}
// 检测值为value的元素是否存在
TreeNode find(TreeNode root, int val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
TreeNode left = find(root.left,val);
if(left != null){
return left;
}
TreeNode right = find(root.right,val);
if(right != null){
return right;
}
return null;
}
//层序遍历
public void levelOrder(TreeNode root){
if(root == null){
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
TreeNode node = queue.poll();
System.out.println(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
}
// 判断一棵树是不是完全二叉树
boolean isCompleteTree(TreeNode root){
if(root == null) {
return true;
}
Queue<TreeNode> qu = new LinkedList<>();
qu.offer(root);
while(!qu.isEmpty()){
TreeNode cur = qu.poll();
if(cur != null){
qu.offer(cur.left);
qu.offer(cur.right);
}else{
break;
}
}
while(!qu.isEmpty()){
TreeNode cur = qu.poll();
if(cur != null){
return false;
}
}
return true;
}
public void preOrder(TreeNode root){
if(root == null){
return;
}
System.out.println(root.val);
preOrder(root.left);
preOrder(root.right);
}
/* public void preorder_feidigui(TreeNode root){
if(root == null){
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null){
System.out.println(cur.val);
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
}*/
public void inOrder(TreeNode root){
if(root == null){
return;
}
inOrder(root.left);
System.out.println(root.val + " ");
inOrder(root.right);
}
public void postOrde(TreeNode root){
if(root == null){
return;
}
postOrde(root.left);
postOrde(root.right);
System.out.println(root.val);
}
}