二叉树(binary tree)是一棵树,其中每个节点都不能有多于两个的儿子。
下图显式一颗由一个根和两颗字数组成的二叉树,子树TL和TR均可能为空。
二叉树的一个性质是一颗平均二叉树的深度要比节点个数N小得多,这个性质有时很重要。分析表明,其平均深度为O(\(\sqrt{N}\)),而对于特殊类型的二叉树,即二叉查找树(binary search tree),其深度的平均值是O(\(\log{N}\))。
二叉树的遍历
对于二叉树来讲最主要、最基本的运算是遍历。
遍历二叉树,是指以一定的次序访问二叉树中的每个节点。所谓访问节点是指节点进行各种操作的简称。例如,查询节点数据域的内容,或输出它的值,或找出节点位置,或是执行对节点的其他操作。遍历二叉树的过程实质是把二叉树的节点进行线性排列的过程。假设遍历二叉树时访问节点的操作就是输出节点数据域的值,那么遍历的结果得到一个线性序列。
从二叉树的递归定义可知,一颗非空的二叉树由根节点及左、右子树这三个基本部分组成。因此,在任意给定节点上,可以按某种次序执行三个操作:
- 访问节点本身(N)
- 遍历该节点的左子树(L)
- 遍历该节点的右子树(R)
以上三种操作有六种执行次序:
NLR、LNR、LRN、NRL、RNL、RLN。
注意:前三种次序与后三种次序对称,故只讨论先左后右的前三种次序。
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
实现
因为一个二叉树节点最多有两个子节点,所以我们可以保存直接连接道它们的链。树节点的声明在结构上类似于双链表的声明,在声明中,节点就是由element(元素)的信息加上两个到其他节点的引用(left和right)组成的结构。
import java.util.LinkedList;
/**
* @author: Tu9ohost
*/
public class binaryTree {
// 二叉树的节点数据结构
private class TreeNode{
private int key = 0;
private String data = null;
private boolean isVisted = false;
private TreeNode leftChild = null;
private TreeNode rightChild = null;
public TreeNode(){}
public TreeNode(int key, String data){
this.key = key;
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
}
private TreeNode root = null;
public binaryTree(){
root = new TreeNode(1,"rootNode(A)");
}
// 创建一颗二叉树
public void createBinTree(TreeNode root){
TreeNode newNodeB = new TreeNode(2,"B");
TreeNode newNodeC = new TreeNode(3,"C");
TreeNode newNodeD = new TreeNode(4,"D");
TreeNode newNodeE = new TreeNode(5,"E");
TreeNode newNodeF = new TreeNode(6,"F");
root.leftChild = newNodeB;
root.rightChild = newNodeC;
root.leftChild.leftChild = newNodeD;
root.leftChild.rightChild = newNodeD;
root.rightChild.rightChild = newNodeF;
}
public boolean isEmpty(){
return root == null;
}
// 树的高度
public int height(){
return height(root);
}
// 节点个数
public int size(){
return size(root);
}
private int height(TreeNode subTree){
if (subTree == null){
return 0;
}
else {
int i = height(subTree.leftChild);
int j = height(subTree.rightChild);
return (i < j) ? (j + 1) : (i + 1);
}
}
private int size(TreeNode subTree){
if (subTree == null){
return 0;
}
else{
return 1 + size(subTree.leftChild)
+ size(subTree.rightChild);
}
}
public TreeNode parent(TreeNode element){
return (root == null || root == element) ? null : parent(root,element);
}
public TreeNode parent(TreeNode subTree,TreeNode element){
if (subTree == null){
return null;
}
if (subTree.leftChild == element || subTree.rightChild == element){
// 返回父节点地址
return subTree;
}
TreeNode p;
// 先在左子树中找,如果左子树中没有找到,才到右子树去找
if ((p = parent(subTree.leftChild,element)) != null){
// 递归在左子树中搜索
return p;
}else {
// 递归在右子树中搜索
return parent(subTree.rightChild,element);
}
}
public TreeNode getLeftChildNode(TreeNode element){
return element != null ? element.leftChild : null;
}
public TreeNode getRightChildNode(TreeNode element){
return element != null ? element.rightChild : null;
}
public TreeNode getRoot(){
return root;
}
// 在释放某个结点时,该结点的左右子树都已经释放,
// 所以应该采用后续遍历,当访问某个结点时将该结点的存储空间释放
public void destroy(TreeNode subTree){
// 删除根为subTree的子树
if (subTree != null){
// 删除左子树
destroy(subTree.leftChild);
// 删除右子树
destroy(subTree.rightChild);
// 删除根节点
subTree = null;
}
}
public void traverse(TreeNode subTree){
System.out.println("key:" + subTree.key + "--name:" + subTree.data);
traverse(subTree.leftChild);
traverse(subTree.rightChild);
}
public void visted(TreeNode subTree){
subTree.isVisted = true;
System.out.println("key:" + subTree.key + "--name" + subTree.data);
}
// 递归的前序遍历
// 根结点 ---> 左子树 ---> 右子树
public void preOrder(TreeNode subTree){
if (subTree != null){
visted(subTree);
preOrder(subTree.leftChild);
preOrder(subTree.rightChild);
}
}
// 递归的中序遍历
// 左子树---> 根结点 ---> 右子树
public void inOrder(TreeNode subTree){
if (subTree != null){
inOrder(subTree.leftChild);
visted(subTree);
inOrder(subTree.rightChild);
}
}
// 递归的后序遍历
// 左子树 ---> 右子树 ---> 根结点
public void postOrder(TreeNode subTree){
if (subTree != null){
postOrder(subTree.leftChild);
postOrder(subTree.rightChild);
visted(subTree);
}
}
// 无递归的前序遍历
public void nonRecPreOrder(TreeNode p){
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode node = p;
while (node != null || !stack.isEmpty()){
while (node != null){
visted(node);
stack.push(node);
node = node.leftChild;
}
while (!stack.isEmpty()){
node = stack.pop();
node = node.rightChild;
}
}
}
// 无递归的中序遍历
public void nonRecInOrder(TreeNode p){
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode node = p;
while (node != null || !stack.isEmpty()){
while (node != null){
stack.push(node);
node = node.leftChild;
}
if (!stack.isEmpty()){
node = stack.pop();
visted(node);
node = node.rightChild;
}
}
}
// 无递归的后序遍历
public void nonRecPostOrder(TreeNode p){
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode node = p;
while (p != null){
for (; p.leftChild != null; p = p.leftChild){
stack.push(p);
}
while (p != null && (p.rightChild == null || p.rightChild == node)){
visted(p);
node = p;
if (stack.isEmpty()) {
return;
}
p = stack.pop();
}
stack.push(p);
p = p.rightChild;
}
}
// 深度优先遍历
public void depthFirst(TreeNode p){
if (p == null){
return;
}
LinkedList<TreeNode> stack = new LinkedList<>();
stack.push(p);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
System.out.println(node.data + " ");
if (node.rightChild != null){
stack.push(node.rightChild);
}
if (node.leftChild != null){
stack.push(node.leftChild);
}
}
}
// 层序遍历
public void levelFirst(TreeNode p){
if (p == null) {
return;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(p);
while (!queue.isEmpty()){
TreeNode node = queue.poll();
System.out.println(node.data + " ");
if (node.leftChild != null){
queue.offer(node.leftChild);
}
if (node.rightChild != null){
queue.offer(node.rightChild);
}
}
}
}
参考链接:https://blog.csdn.net/wuwenxiang91322/article/details/12231657
参考书籍:
《数据结构与算法分析》