文章目录
一、树的基本使用
1、树的概念
2、结点的度与树的度
结点的度为:结点拥有子树的个数为结点的度。例如 A 的度为 3;K 的度为 0。
度为 0 的结点称为叶子结点,或终端结点。度不为0的结点称为非终端结点,或分支结点,除根之外的分支结点还可称为内部结点。
树的度为:此树中结点的最大度为树的度。
3、结点的层次和树的深度
其中还有父亲、儿子、兄弟、祖先、子孙、堂兄弟等概念。
4、有序树、N叉树、森林
-
有序树:如果将一颗树看出是从左到右是有次序的,那么称为有序树。
无序树:将一颗树从左到右看出是无序的,称为无序树。
扫描二维码关注公众号,回复: 11367401 查看本文章
-
N叉树:树的度为N的树称为N叉树
-
森林:是有多个不相交的树组成。
二、二叉树
1、二叉树概念
二叉树中每个节点的孩子树只能为0、1、2,并且是有序的。
以左边孩子为根的树称为左子树,以右孩子为根的子树称为右子树。
2、满二叉树和完全二叉树
满二叉树:高度为 k 并且右2^(k+1) -1 节点。
完全二叉树:在一颗满二叉树中,在最下层从最右侧起去掉若干个相邻的叶子结点,得到是树就是完全二叉树。
简单来说就是最下层叶子结点从左到右必须连续。
3、二叉树的性质
(1) 在非空二叉树
2^(i-1) , i>=1;
(2) 深度为h的二叉树最多有
2^h -1 个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
(4) 具有n个结点的完全二叉树
[log2^n] + 1
(注:[ ]表示向下取整)
(5) 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
若I为结点编号则 如果I>1,则其父结点的编号为I/2;
如果2I<=N,则其左孩子(即左子树的根结点)的编号为2I;若2*I>N,则无左孩子;
如果2I+1<=N,则其右孩子的结点编号为2I+1;若2*I+1>N,则无右孩子。
(6) 给定N个结点,能构成h(N)种不同的二叉树。
h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。
(7) 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i
三、二叉树代码实现。
1、存储结构
- 顺序存储:顺序存储适合满二叉树和完全二叉树。不浪费空间。
-
链式存储:不同的结构,可以使用不同的链式存储结构。
-
一般我们在实践中使用链式存储,设计三个域,一个用来存储数据,另外两个用来存储指向左右孩子的指针域。
2、二叉树遍历
概念:遍历就是按照某种策略访问树中的所有结点,且每个结点恰好只访问一次。
遍历可分为先序,中序,后序,层次遍历,除了层次遍历,先,中,后都可以使用递归遍历,也是我们平常实践中使用的最多的。
1、先序遍历
首先访问根,再先序遍历左(右)子树,最后先序遍历右(左)子树 。
2、中序遍历
首先中序遍历左(右)子树,再访问根,最后中序遍历右(左)子树 。
3、后序遍历
首先后序遍历左(右)子树,再后序遍历右(左)子树,最后访问根 。
3、二叉树接口
package com.gwz.datastructure.btree;
/**
* 二叉树结构
* 可以有不同的实现类,并且每个类可以使用不用的存储结构
* 顺序、链式存储都可以。
*/
public interface BinaryTree {
/**
* 是否是空树
* @return
*/
boolean isEmpty();
/**
* 树中结点数量
* @return
*/
int size();
/**
* 获取树的高度
* @return
*/
int getHeight();
/**
* 查询指定值的结点
* @param value
* @return
*/
Node findKey(int value);
/**
* 前序遍历
*/
void preOrderTraverse();
/**
* 中序遍历
*/
void inOrderTraverse();
/**
* 后序遍历
*/
void nextOrderTraverse();
/**
* 后序遍历
*/
void nextOrderTraverse(Node node);
/**
* 前序遍历,非递归操作
*/
void preOrderByStack();
/**
* 中序遍历,非递归操作
*/
void inOrderByStack();
/**
* 后序遍历,非递归操作
*/
void nextOrderByStack();
/**
* 后序遍历非递归
*/
void nextOrderByStack(Node node);
/**
* 层次遍历,非递归。
*/
void levelOrderByQueue();
}
4、二叉树实现类
package com.gwz.datastructure.btree;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
public class LinkedBinaryTree implements BinaryTree{
private Node root; // 根结点。
public LinkedBinaryTree() {
}
public LinkedBinaryTree(Node root) {
this.root = root;
}
@Override
public boolean isEmpty() {
return root == null;
}
@Override
public int size() {
System.out.print("二叉树结点个数:");
return this.size(root);
}
private int size(Node root) {
if (root == null) {
return 0;
} else {
// 左、右子树之和 + 1;
int l = size(root.leftChild);
int r = size(root.rightChild);
return l + r + 1;
}
}
@Override
public int getHeight() {
System.out.println("\n二叉树的高度:");
return getHeight(root);
}
private int getHeight(Node node) {
if(node == null) {
return 0;
} else {
// 获取左子树高度
int l = getHeight(node.leftChild);
// 获取右子树高度
int r = getHeight(node.rightChild);
// 返回左、右子树中最大值 + 1
return r > l ? r+1 : l+1;
}
}
@Override
public Node findKey(int value) {
return this.findKey(value, root);
}
private Node findKey(Object value, Node root) {
if (root == null) {
return null;
} else if (root != null && root.value != value) {
return root;
} else {
Node node1 = this.findKey(value, root.leftChild);
Node node2 = this.findKey(value, root.rightChild);
if (node1 != null && node1.value == value) {
return node1;
} else if (node2 != null && node2.value == value){
return node2;
} else {
return node2;
}
}
}
// 先序递归遍历 根、左、右
@Override
public void preOrderTraverse() {
// 输出根结点
if (root != null) {
// 输出根节点
System.out.print(root.value+" | ");
// 遍历左子树,先创建左子树
BinaryTree leftTree = new LinkedBinaryTree(root.leftChild);
leftTree.preOrderTraverse();
// 遍历右子树、先创建右子树
BinaryTree rightTree = new LinkedBinaryTree(root.rightChild);
rightTree.preOrderTraverse();
}
}
// 这里相对先序遍历做了改进,为了不让用用户单独答应遍历策略
// 同时为了简化编码
// 中序递归遍历 左、根、右
@Override
public void inOrderTraverse() {
System.out.println("\n中序遍历!");
inOrderTraverse(root);
}
// 可以不向外部暴露
private void inOrderTraverse(Node node) {
if (node != null) {
inOrderTraverse(node.leftChild);
System.out.print(node.value+" | ");
inOrderTraverse(node.rightChild);
}
}
// 后序递归遍历
@Override
public void nextOrderTraverse() {
System.out.println("\n后续遍历");
nextOrderTraverse(root);
}
@Override
public void nextOrderTraverse(Node node) {
if (node != null) {
nextOrderTraverse(node.leftChild);
nextOrderTraverse(node.rightChild);
System.out.print(node.value+" | ");
}
}
@Override
public void preOrderByStack() {
}
// 非递归中序遍历,和深度优先遍历很想
@Override
public void inOrderByStack() {
System.out.println("\n中序非递归遍历");
Deque<Node> stack = new LinkedList<Node>();
Node current = root;
while (current != null || !stack.isEmpty()) {
// 这里的意思是一直遍遍历完左子树。
while (current != null) {
stack.push(current);
current = current.leftChild;
}
if (!stack.isEmpty()) {
current = stack.pop(); // 弹栈,并把指针往回指
System.out.println(current + " | ");
current = current.rightChild;
}
}
System.out.println();
}
@Override
public void nextOrderByStack() {
}
@Override
public void nextOrderByStack(Node node) {
}
// 层次遍历,利用队列去完成。
@Override
public void levelOrderByQueue() {
if (root == null) return;
Queue<Node> queue = new LinkedList<Node>();
queue.add(root); // 存入根结点
while (queue.size() != 0) {
int len = queue.size();
for (int i = 0; i < len; i++) {
Node temp = queue.poll();
System.out.print(temp.value+" | ");
if (temp.leftChild != null) queue.add(temp.leftChild);
if (temp.rightChild != null) queue.add(temp.rightChild);
}
}
System.out.println();
}
}
5、二叉树测试
构建的二叉树。
package com.gwz.datastructure.btree;
public class TestBinaryTree {
public static void main(String[] args) {
// 创建一个二叉树
Node node5 = new Node(5,null,null);
Node node4 = new Node(4,null,node5);
Node node3 = new Node(3,null,null);
Node node7 = new Node(7,null,null);
Node node6 = new Node(6,null,node7);
Node node2 = new Node(2,node3,node6);
Node node1 = new Node(1,node4,node2);
BinaryTree btree = new LinkedBinaryTree(node1);
// 判断二叉树是否为空
System.out.println(btree.isEmpty());
// 前序遍历(递归) 1 4 5 2 3 6 7
System.out.println("先序遍历!");
btree.preOrderTraverse();
// 中序遍历(递归) 4 5 1 3 2 6 7
btree.inOrderTraverse();
// 后序遍历(递归) 5 4 3 7 6 2 1
btree.nextOrderTraverse();
// 中序遍历(非递归,借助栈)
btree.inOrderByStack();
// 层次遍历(非递归,借助队列)
btree.levelOrderByQueue();
// 在二叉树中查找某个值
System.out.println(btree.findKey(1));
// 获取二叉树的高度
System.out.println(btree.getHeight());
// 获取二叉树结点个数
System.out.println(btree.size());
}
}