本文实现代码地址:
https://github.com/helloWorldchn/DataStructure
一、树
树(Tree)是一种抽象数据类型(Abstract Data Type,ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
- 每个节点都只有有限个子节点或无子节点;
- 没有父节点的节点称为根节点;
- 每一个非根节点有且只有一个父节点;
- 除了根节点外,每个子节点可以分为多个不相交的子树;
- 树里面没有环路(cycle)
树的一些基本概念
(1)结点之间的关系描述
节点的祖先:从根到该节点所经分支上的所有节点;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
(2)结点、树的属性描述
结点的层次(深度):该节点从上往下数位于的层数
结点的高度:该节点从下往上数位于的层数
树的高度(深度):总共多少层
结点的度:有几个孩子(分支)
树的度:各结点的度的最大值
二、二叉树
二叉树(Binary Tree),是每个节点最多只有两个分支的树结构。通常分支被称作“左子树”和“右子树”。二叉树的分支具有左右次序,不能随意颠倒。二叉树示意图如下:
1.二叉树的性质
二叉树的第 i 层至多拥有
2
(
i
−
1
)
2^{(i−1)}
2(i−1) 个节点;
深度为 h 的二叉树至多总共有
2
(
h
+
1
)
−
1
2^{(h+1)}−1
2(h+1)−1个节点
2.常见的二叉树
完全二叉树(Complete Binary Tree):对于一棵二叉树,假设其深度为 d(d>1)。除了第 d 层外,其它各层的节点数目均已达最大值,且第 d 层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;
满二叉树(Full Binary Tree):所有叶节点都在最底层的完全二叉树;
二叉搜索树(Binary Search Tree):也称二叉查找树,有序二叉树(Ordered Binary Tree)、排序二叉树(Sorted Binary Tree)。其特性为,对于任意节点,其左子树节点都小于该节点的值,并且其右子树节所有节点的值都大于该节点的值
平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
3.二叉树的创建(Java)
创建一个TreeNode类代表节点
TreeNode代表节点,每个TreeNode对象表示一个节点,left存放的是左子树,right存放的是右子树,val存放的是该节点的值。
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) {
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
4.二叉树的遍历
以下图为例说明二叉树的遍历
4.1 二叉树的先序遍历(深度优先遍历)
先序遍历为根节点、左子树节点、右子树节点的顺序遍历,采用递归的方式。
如第一节图中的二叉搜索树所示的二叉树先序遍历为:7 1 0 5 3 2 4 6 9 8
public void preOrderTraversal (TreeNode root) {
if (root == null) {
return;
}
System.out.print(root.val + " "); //先输出当前节点(初始的时候是root节点)
preOrderTraversal(root.left); // 如果左子节点不为空,则递归继续前序遍历
preOrderTraversal(root.right); // 如果右子节点不为空,则递归继续前序遍历
}
4.2 二叉树的中序遍历(深度优先遍历)
中序遍历为左子树节点、根节点、右子树节点的顺序遍历,采用递归的方式。
如第一节图中的二叉搜索树所示的二叉树中序遍历为:0 1 2 3 4 5 6 7 8 9
public void inOrderTraversa (TreeNode root) {
if (root == null) {
return;
}
inOrderTraversa(root.left); // 如果当前节点的左子节点不为空,则递归中序遍历
System.out.print(root.val + " "); // 输出当前节点
inOrderTraversa(root.right); // 如果当前的右子节点不为空,则递归中序遍历
}
4.3 二叉树的后序遍历(深度优先遍历)
后序遍历为左子树节点、右子树节点、根节点的顺序遍历,采用递归的方式。
如第一节图中的二叉搜索树所示的二叉树后序遍历为:0 2 4 3 6 5 1 8 9 7
public void postOrderTraversal (TreeNode root) {
if (root == null) {
return;
}
postOrderTraversal(root.left); // 如果当前节点的左子节点不为空,则递归后序遍历
postOrderTraversal(root.right); // 如果当前节点的右子节点不为空,则递归后序遍历
System.out.print(root.val + " "); // 输出当前节点
}
4.4 二叉树的层次遍历(广度优先遍历)
层次遍历,即广度优先遍历,采用队列的方式实现。
如第一节图中的二叉搜索树所示的二叉树层次遍历为:7 1 9 0 5 8 3 6 2 4
public void levelOrderTraversal(TreeNode root) {
if(root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>(); // 存放每层操作的根节点
queue.offer(root);
while (!queue.isEmpty()) {
int queueSize = queue.size();
for (int i = 0; i < queueSize; i++) { // 用for循换可以隔离开每一层的遍历
TreeNode rootNode = queue.poll(); // 开始操作后将其从队列移除
System.out.print(rootNode.val + " ");
if (rootNode.left != null) {
TreeNode leftNode = rootNode.left; // 左节点存入队列,下一层遍历它就成了新根节点
queue.offer(leftNode);
}
if (rootNode.right != null) {
TreeNode rightNode = rootNode.right; // 右节点存入队列,下一层遍历它就成了新根节点
queue.offer(rightNode);
}
}
}
}
三、案例完整代码
1.代码
BinaryTree 类:
import java.util.LinkedList;
import java.util.Queue;
public class BinaryTree {
// 树的先序遍历
public void preOrderTraversal (TreeNode root) {
if (root == null) {
return;
}
System.out.print(root.val + " "); //先输出当前节点(初始的时候是root节点)
preOrderTraversal(root.left); // 如果左子节点不为空,则递归继续前序遍历
preOrderTraversal(root.right); // 如果右子节点不为空,则递归继续前序遍历
}
// 树的中序遍历
public void inOrderTraversa (TreeNode root) {
if (root == null) {
return;
}
inOrderTraversa(root.left); // 如果当前节点的左子节点不为空,则递归中序遍历
System.out.print(root.val + " "); // 输出当前节点
inOrderTraversa(root.right); // 如果当前的右子节点不为空,则递归中序遍历
}
// 树的后序遍历
public void postOrderTraversal (TreeNode root) {
if (root == null) {
return;
}
postOrderTraversal(root.left); // 如果当前节点的左子节点不为空,则递归后序遍历
postOrderTraversal(root.right); // 如果当前节点的右子节点不为空,则递归后序遍历
System.out.print(root.val + " "); // 输出当前节点
}
// 广度优先遍历,即树的层次遍历,借用队列实现
public void levelOrderTraversal(TreeNode root) {
if(root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>(); // 存放每层操作的根节点
queue.offer(root);
while (!queue.isEmpty()) {
int queueSize = queue.size();
for (int i = 0; i < queueSize; i++) { // 用for循换可以隔离开每一层的遍历
TreeNode rootNode = queue.poll(); // 开始操作后将其从队列移除
System.out.print(rootNode.val + " ");
if (rootNode.left != null) {
TreeNode leftNode = rootNode.left; // 左节点存入队列,下一层遍历它就成了新根节点
queue.offer(leftNode);
}
if (rootNode.right != null) {
TreeNode rightNode = rootNode.right; // 右节点存入队列,下一层遍历它就成了新根节点
queue.offer(rightNode);
}
}
}
}
public static void main(String[] args) {
TreeNode root;
TreeNode node4 = new TreeNode(4);
TreeNode node5 = new TreeNode(5);
TreeNode node6 = new TreeNode(6);
TreeNode node7 = new TreeNode(7);
TreeNode node2 = new TreeNode(2, node4, node5);
TreeNode node3 = new TreeNode(3, node6, node7);
TreeNode node1 = new TreeNode(1, node2, node3);
root = node1;
BinaryTree binaryTree = new BinaryTree();
System.out.print("先序遍历:");
binaryTree.preOrderTraversal(root);
System.out.println();
System.out.print("中序遍历:");
binaryTree.inOrderTraversa(root);
System.out.println();
System.out.print("后序遍历:");
binaryTree.postOrderTraversal(root);
System.out.println();
System.out.print("层次遍历:");
binaryTree.levelOrderTraversal(root);
System.out.println();
}
}
TreeNode类
//Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) {
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
2.代码构建二叉树的模型
上面程序创建了如下图的二叉树:
上面二叉树的遍历结果依次是:
先序遍历:1 2 4 5 3 6 7
中序遍历:4 2 5 1 6 3 7
后序遍历:4 5 2 6 7 3 1
层次遍历:1 2 3 4 5 6 7