一、基本概念
1、二叉树
-
二叉树(Binary tree)是树形结构的一个重要类型,二叉树(binary tree)是指树中节点的度不大于2的树。
-
关于二叉树有以下术语需要了解:
①结点:包含一个数据元素及若干指向子树分支的信息。
②结点的度:一个结点拥有子树的数目称为结点的度。
③叶子结点:也称为终端结点,没有子树的结点或者度为零的结点 。
④分支结点:也称为非终端结点,度不为零的结点称为非终端结点。
⑤树的度:树中所有结点的度的最大值。
⑥结点的层次:从根结点开始,假设根结点为第1层,根结点的子节点为第2层,依此类推,如果某一个结点位于第L层,则其子节点位于第L+1层。
⑦树的深度:也称为树的高度,树中所有结点的层次最大值称为树的深度。
⑧有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树。
⑨无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树。
⑩森林:由m(m≥0)棵互不相交的树构成一片森林。如果把一棵非空的树的根结点删除,则该树就变成了一片森林,森林中的树由原来根结点的各棵子树构成。 -
二叉树中包含两种特殊的二叉树:
1、满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
2、完全二叉树:深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k,有n个结点的满二叉树中编号从1到n的结点一一对应时,称为完全二叉树。完全二叉树的特点是叶子结点只可能出现在层序最大的两层上,并且某个结点的左分支下子孙的最大层序与右分支下子孙的最大层序相等或大1。 -
二叉树具有以下性质:
性质1:二叉树的第i层上至多有2i-1(i≥1)个节点。
性质2:深度为h的二叉树中至多含有2h-1个节点。
性质3:若在任意一棵二叉树中,有n个叶子节点,有n2个度为2的节点,则必有n0=n2+1。
性质4:具有n个节点的完全二叉树深为log2x+1(其中x表示不大于n的最大整数) 。
性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
当i=1时,该节点为根,它无双亲节点 。
当i>1时,该节点的双亲节点的编号为i/2。
若2i≤n,则有编号为2的左叶子,否则没有左叶子。
若2+1≤n,则有编号为2i+1的右叶子,否则没有右叶子。
2、二叉树的遍历
- 前序遍历
若二叉树非空,则依次执行如下操作:
⑴ 访问根结点;
⑵ 遍历左子树;
⑶ 遍历右子树。 - 中序遍历
若二叉树非空,则依次执行如下操作:
⑴遍历左子树;
⑵访问根结点;
⑶遍历右子树。 - 后序遍历
若二叉树非空,则依次执行如下操作:
⑴遍历左子树;
⑵遍历右子树;
⑶访问根结点。
注意:这里说的前序、中序、后序都是相对根节点来说的
二、思路分析
1、二叉树的遍历(以前序遍历为例)
二叉树的前序遍历是指从根节点开始对于每一个子树,先对根节点进行遍历,再对左右孩子节点进行遍历。
来看一个例子:
上图所示的二叉树以前序遍历:
【1】首先对于整个二叉树,先遍历根节点为F。
【2】然后遍历以F为根节点的左子树,对于左子树,先遍历根节点为C,再遍历以C为根节点的子树的左子树,只有叶子节点A,然后遍历以为根节点的子树的右子树,对于右子树,根节点为D,然后遍历以D为根节点的子树的左子树,只有叶子节点B。这样就完成了以F为根节点的左子树的遍历。
【3】最后遍历以F为根节点的右子树,对于右子树,先遍历根节点为E,再遍历以E为根节点的子树的左子树,只有叶子节点H,然后遍历以为根节点的子树的右子树,对于右子树,根节点为G,然后遍历以G为根节点的子树的左子树,只有叶子节点M。这样就完成了以F为根节点的右子树的遍历。
【4】得到遍历序列:FCADBEHGM
在用程序实现时,可以使用递归来进行这样一个重复的操作。
2、二叉树的查找
二叉树的查找与遍历类似,只在遍历的基础上将要查找的节点与遍历得到的节点进行对比即可。
三、代码实现
- 首先构建一个二叉树,并定义相关方法。
定义节点类
class TreeNode {
private int no;
private String name;
private TreeNode left;//默认为null
private TreeNode right;//默认为null
public TreeNode(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
@Override
public String toString() {
return "TreeNode{" + "no=" + no + ", name='" + name + '\'' + '}';
}
//前序遍历
public void preOrder() {
System.out.println(this);//先输出父节点
if (this.left != null) {//递归向左子树前序遍历
this.left.preOrder();
}
if (this.right != null) {//递归向右子树前序遍历
this.right.preOrder();
}
}
/**
*
* @param no 按查找
* @return 如果找到返回该Node,否则返回null
*/
public TreeNode preOrderSearch(int no) {
if (this.no == no) {//比较当前节点
return this;
}
TreeNode resNode = null;
if (this.left != null) {//判断左节点是否为空,不为空则递归前序查找
resNode = this.left.preOrderSearch(no);
}
if (resNode != null) {//向左递归找到了
return resNode;
}
if (this.right != null) {//向左递归未找到,判断右节点是否为空,不为空则递归前序查找
resNode = this .right.preOrderSearch(no);
}
return resNode;
}
}
定义二叉树
class BinaryTree {
private TreeNode root;
public void setRoot(TreeNode root) {
this.root = root;
}
//前序遍历
public void preOrder() {
if (this.root != null) {
this.root.preOrder();
} else {
System.out.println("二叉树为空!");
}
}
//前序遍历查找
public TreeNode preOrderSearch(int no) {
if (root != null) {
return root.preOrderSearch(no);
} else {
return null;
}
}
}
- 写一个Demo来进行测试
public class BinaryTreeDemo {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
TreeNode root = new TreeNode(1, "张三");
TreeNode node2 = new TreeNode(2, "李四");
TreeNode node3 = new TreeNode(3, "王五");
TreeNode node4 = new TreeNode(4, "小明");
TreeNode node5 = new TreeNode(5, "小红");
//手动创建二叉树
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binaryTree.setRoot(root);
//测试
System.out.println("前序遍历:");
binaryTree.preOrder();
}
System.out.println("前序遍历查找:");
TreeNode resNode = binaryTree.preOrderSearch(3);
if (resNode != null) {
System.out.println(resNode);
}
- 测试结果