目录
一、什么是二叉树
二叉树是一种特殊的树,那么特殊之处在哪呢:
-
每个结点最多只能有两个子结点(后文中我用孩子来代替子结点)
-
二叉树是有序树(孩子的前后关系才是我们需要注意的)
这里我们要知道左图中:B是A的左孩子;右图中:C是A的左孩子。
二叉树的所有形态
上图给出了几种特殊的二叉树形态,从左往右依次是:空树、只有根节点的二叉树、节点只有左子树、节点只有右子树、节点的左右子树均存在,一般二叉树都是由上述基本形态结合而形成的。
二、满二叉树
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k-1,则它就是满二叉树。
三、完全二叉树
对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
注意:满二叉树是一种特殊的完全二叉树。
四、二叉树的遍历
4.1 树的创建
为了方便后续代码的实现,需要先构建二叉树,本篇博客代码以如图所示二叉树实现。
public class BuildTree {
public static TreeNode buildTree1() {
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');
TreeNode i = new TreeNode('I');
TreeNode j = new TreeNode('J');
a.left = b;
a.right = c;
b.left = d;
d.right = g;
g.left = j;
c.left = e;
c.right = f;
f.left = h;
f.right = i;
return a;
}
}
4.2 深度优先搜索算法DFS
深度优先搜索算法(Depth-First-Search,缩写为 DFS),是一种利用递归实现的搜索算法。
首先我们要明确是怎么遍历二叉树的。二叉树的前中后序遍历,真的真的很好理解,以下图为例:
从根结点出发的箭头,沿着虚线遍历整个二叉树,最终回到了根节点。我们会发现,在遍历途中,每个结点其实被经过了三次。前序遍历就是第一次碰到结点的顺序(标号1),中序遍历就是第二次碰到结点的顺序(标号2),后序遍历就是第三次碰到结点的顺序(标号3)。这样讲是不是很容易理解。
所以我们明确了前 / 中 / 后序遍历顺序的不同之处:
前序遍历:【根】【左子树】【右子树】
中序遍历:【左子树】【根】【右子树】
后序遍历:【左子树】【右子树】【根】
了解到了这些,对于后文的代码就清晰明了了。
前 / 中 / 后序遍历(递归实现)
public class TreeTraversal {
/**
*前序遍历
*/
public static void preTraversal(TreeNode root) {
//处理前提
if(root != null) {
//处理根节点
System.out.print(root.value);
//根据前序的方式,递归处理该结点的左子树
preTraversal(root.left);
//根据前序的方式,递归处理该结点的右子树
preTraversal(root.right);
}
}
/**
*中序遍历
*/
public static void inTraversal(TreeNode root) {
if(root != null) {
inTraversal(root.left);
System.out.print(root.value);
inTraversal(root.right);
}
}
/**
*后序遍历
*/
public static void postTraversal(TreeNode root) {
if(root != null) {
postTraversal(root.left);
postTraversal(root.right);
System.out.print(root.value);
}
}
public static void main(String[] args) {
TreeNode root = BuildTree.buildTree1();
System.out.println("前序遍历:");
preTraversal(root);
System.out.println();
System.out.println("中序遍历:");
inTraversal(root);
System.out.println();
System.out.println("后序遍历:");
postTraversal(root);
}
}
class TreeNode {
char value;
TreeNode left;
TreeNode right;
public TreeNode(char value) {
this.value = value;
}
}
运行结果:
前 / 中 / 后序遍历(非递归实现)
/**
* 前序遍历
*/
public static void preOrder(TreeNode root) {
Deque<TreeNode> stacks = new LinkedList<>();
TreeNode current = root;
while (!stacks.isEmpty() || current != null) {
while (current != null) {
System.out.print(current.value);
stacks.push(current);
current =current.left;
}
TreeNode top = stacks.pop();
current = top.right;
}
}
/**
* 中序遍历
*/
public static void inOrder(TreeNode root) {
Deque<TreeNode> stacks = new LinkedList<>();
TreeNode current = root;
while (!stacks.isEmpty() || current != null) {
while (current != null) {
stacks.push(current);
current =current.left;
}
TreeNode top = stacks.pop();
System.out.print(top.value);
current = top.right;
}
}
/**
* 后序遍历
*/
public static void postOrder(TreeNode root) {
Deque<TreeNode> stacks = new LinkedList<>();
TreeNode current = root;
TreeNode last = null;
while (!stacks.isEmpty() || current != null) {
while (current != null) {
stacks.push(current);
current =current.left;
}
TreeNode top = stacks.peek();
if(top.right == null) {
stacks.pop();
last = top;
System.out.print(top.value);
} else if(top.right == last) {
stacks.pop();
last = top;
System.out.print(top.value);
} else {
current = top.right;
}
}
}
public static void main(String[] args) {
TreeNode root = BuildTree.buildTree1();
System.out.println("前序遍历:");
preOrder(root);
System.out.println();
System.out.println("中序遍历:");
inOrder(root);
System.out.println();
System.out.println("后序遍历:");
postOrder(root);
}
}
运行结果:
4.3 广度优先搜索算法BFS
广度优先搜索算法(Breadth-First-Search,缩写为 BFS),是一种利用队列实现的搜索算法。
层序遍历
是否是完全二叉树
层序遍历和层号
import java.util.LinkedList;
import java.util.Queue;
public class TreeLevelOrder {
/**
* 层序遍历
*/
public static void levelTraversal(TreeNode root) {
if(root == null) {
return;
}
//队列的元素类型是树的结点
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode node = queue.remove();
//这个node就是我们层序遍历经过的结点
System.out.print(node.value);
if(node.left != null) {
queue.add(node.left);
}
if(node.right != null) {
queue.add(node.right);
}
}
}
/**
* 是否是完全二叉树
*/
public static boolean isCompleteTree(TreeNode root){
if(root == null || (root.left == null && root.right == null)){
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
TreeNode node = null;
while(!queue.isEmpty()){
node = queue.peek();
if(node.left != null){
queue.add(node.left);
}else{
break;
}
if(node.right != null){
queue.add(node.right);
}else{
break;
}
queue.poll();
}
if(queue.peek().right == null){
queue.poll();
}
while(!queue.isEmpty()){
node = queue.poll();
if(node.left != null || node.right != null){
return false;
}
}
return true;
}
/**
* 层序遍历+层数
*/
public static void levelOrderWithLevel(TreeNode root) {
if(root == null) {
return;
}
Queue<NL> queue = new LinkedList<>();
queue.add(new NL(root,1));
while (!queue.isEmpty()) {
NL nl = queue.remove();
TreeNode node = nl.node;
int level = nl.level;
System.out.print(level);
System.out.print(node.value);
if(node.left != null) {
queue.add(new NL(node.left,level+1));
}
if(node.right != null) {
queue.add(new NL(node.right,level+1));
}
}
}
static class NL {
TreeNode node;
int level;
NL(TreeNode node,int level) {
this.node = node;
this.level = level;
}
}
public static void main(String[] args) {
TreeNode root = BuildTree.buildTree1();
System.out.print("层序遍历:");
levelTraversal(root);
System.out.println();
System.out.print("层序遍历和层号:");
levelOrderWithLevel(root);
System.out.println();
System.out.println("是否是完全二叉树:"+isCompleteTree(root));
}
}
运行结果:
五、问题求解
二叉树中结点个数
二叉树中叶子结点个数
二叉树第K层的结点个数
二叉树的高度
二叉树中是否包含某个值
二叉树中包含某值所在的节点
结点是不是以root为根的二叉树上的结点
public class SomeMethod {
private static int n = 0;
/**
* 返回root为根的树中,有多少个结点
*/
private static void preOrder(TreeNode root) {
//1.一个树的结点 2.没有结点
if(root != null) {
n++;
preOrder(root.left);
preOrder(root.right);
}
}
public static int sumTreeNodeSize(TreeNode root) {
preOrder(root);
return n;
}
public static int sumTreeNodeSize2(TreeNode root) {
if(root != null) {
int rootNodeSize = 1;
int leftSubTreeNodeSize = sumTreeNodeSize2(root.left);
int rightSubTreeNodeSize = sumTreeNodeSize2(root.right);
return rightSubTreeNodeSize+leftSubTreeNodeSize+rootNodeSize;
}
return 0;
}
/**
* 求叶子结点个数
*/
private static int m = 0;
private static void preOrder2(TreeNode root) {
if(root != null) {
if(root.left == null && root.right == null) {
m++;
}
preOrder2(root.left);
preOrder2(root.right);
}
}
public static int sumTreeLeafNodeSize(TreeNode root) {
m = 0;//保证下次打印
preOrder2(root);
return m;
}
public static int sumTreeLeafNodeSize2(TreeNode root) {
if(root == null) {
return 0;
} else if(root.left == null && root.right == null) {
return 1;
} else {
int leftSubTreeNodeSize = sumTreeLeafNodeSize2(root.left);
int rightSubTreeNodeSize = sumTreeLeafNodeSize2(root.right);
return rightSubTreeNodeSize + leftSubTreeNodeSize;
}
}
/**
*求二叉树第K层的结点个数
*/
public static int sumKLevelNodeSize(TreeNode root,int k) {
if(root == null) {
return 0;
} else if(k == 1) {
return 1;
} else {
int leftSubTreeK_1 = sumKLevelNodeSize(root.left,k-1);
int rightSubTreeK_1 = sumKLevelNodeSize(root.right,k-1);
return leftSubTreeK_1 + rightSubTreeK_1;
}
}
/**
* 求二叉树的高度
*/
public static int getHeight(TreeNode root) {
if(root == null) {
return 0;
} else if(root.left == null && root.right == null) {//可以和下面else合并
return 1;
} else {
int leftTreeHeight = getHeight(root.left);
int rightTreeHeight = getHeight(root.right);
return Math.max(leftTreeHeight,rightTreeHeight)+1;
}
}
/**
* 二叉树中是否包含某个数
*/
public static boolean containsTree(TreeNode root,char v) {
if(root == null) {
return false;
} else {
if(root.value == v) {
return true;
} else {
boolean leftSubTreeContains = containsTree(root.left, v);
if(leftSubTreeContains) {
return true;
} else {
boolean rightSubTreeContains = containsTree(root.right,v);
if(rightSubTreeContains) {
return true;
} else {
return false;
}
}
}
}
}
public static boolean containsTree2(TreeNode root,char v) {
if(root == null) {
return false;
}
if(root.value == v) {
return true;
}
boolean left = containsTree2(root.left,v);
if(left) {
return true;
}
return containsTree2(root.right,v);
}
/**
* 返回包含v所在的节点
*/
public static TreeNode containsTree3(TreeNode root,char v) {
if(root == null) {
return null;
}
if(root.value == v) {
return root;
}
TreeNode leftContains = containsTree3(root.left, v);
if(leftContains != null) {
return leftContains;
}
return containsTree3(root.right,v);
}
/**
* 返回node是不是以root为根的二叉树上的一个结点
* 空树一律返回false,即使找的是null
*/
public static boolean containsTree4(TreeNode root,TreeNode node) {
if(root == null) {
return false;
}
if(root == node) {
return true;
}
boolean leftVontains = containsTree4(root.left, node);
if(leftVontains) {
return true;
}
return containsTree4(root.right,node);
}
public static void main(String[] args) {
TreeNode root = BuildTree.buildTree1();
System.out.println("二叉树的结点个数:"+sumTreeNodeSize(root));//10
System.out.println("二叉树的结点个数:"+sumTreeNodeSize2(root));//10
System.out.println("二叉树的叶子结点个数:"+sumTreeLeafNodeSize(root));//4
// System.out.println("二叉树的叶子结点个数:"+sumTreeLeafNodeSize(root));//8
System.out.println("二叉树的叶子结点个数:"+sumTreeLeafNodeSize2(root));//4
System.out.println("二叉树的第一层有:"+sumKLevelNodeSize(root, 1)+"个结点");
System.out.println("二叉树的第四层有:"+sumKLevelNodeSize(root, 4)+"个结点");
System.out.println("二叉树的高度:"+getHeight(root));
System.out.println("二叉树中是否包含F:"+containsTree(root,'F'));
System.out.println("二叉树中是否包含F:"+containsTree2(root,'F'));
System.out.println("二叉树中包含F的结点:"+containsTree3(root, 'F').value);
System.out.println("结点是否是以root为根的二叉树上的节点:"+containsTree4(root, root));
}
}
运行结果: