关于树的介绍
可参考树的基本知识,以下是对其的简述
树是一些结点的集合。若非空树,则由根节点和0个或多个非空的子树组成
路径是结点和另一个结点之间边和结点的序列
结点的度指的是结点拥有的子树数,树的度是树内各结点的度的最大值
结点的高度指的是该结点到叶子结点最长路径边的总和
结点的深度指的是该结点到根节点最长路径边的总和
二叉树
二叉树的基本概念
二叉树是一个每个根节点最多只能有两个分支的树,左边的分支称之为左子树,右边的分支称之为右子树。二叉树节点的度最大也就是2,而普通的树,节点的度是没有限制的。
二叉树的分类
完美/满二叉树(Perfect Binary Tree)
完美二叉树满足两个特性:
- 所有的结点都包含两个子结点
- 所有的叶子节点的Height或者Level都相等
完全二叉树(Complete Binary Tree)
- 完全二叉树是除了最后一层都是满的(都有两个子节点),并且最后一层的节点是从左往右排列的。
- 如果最后一层排满了就是完美二叉树,没有满则是完全二叉树。
- 完美二叉树一定是完全二叉树,完全二叉树不一定是完美二叉树。
一个完全二叉树可以高效的使用数组来表示。
完满二叉树(Full Binary Tree)
每个节点都有两个子节点。它比完美二叉树少了所有叶子结点的高度相等这个条件。
二叉树的性质
- 二叉树的第i层上至多有2^(i-1)个结点
- 深度为k的二叉树至多有2^k-1个结点
- 对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
- 具有n个结点的完全二叉树的深度为floor(log2n)+1
- 如果对一棵有n个结点的完全二叉树(其深度为floor(log2n)+1)的结点按层序编号(从第1层到第floor(log2n)+1层,每层从左到右)对任一结点i,有:
(1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点floor(i/2)
(2)如果2i>n,则结点i无左孩子(结点i是叶子结点),否则其左孩子是结点2i
(3)如果2i+1>n,则结点i无右孩子(结点i是叶子结点),否则其右孩子是结点2i+1
Java实现
二叉树的顺序存储
用数组实现一个简单的二叉树:
public class ArrayBinaryTree<T> {
private static final int DEFAULT_DEEP = 5;
private int deep;
private int capacity;
private Object[] nodes;
public ArrayBinaryTree() {
this(DEFAULT_DEEP);
}
public ArrayBinaryTree(int deep) {
this.deep = deep;
nodes = new Object[capacity = (int) Math.pow(2, deep) - 1];
}
public ArrayBinaryTree(int deep, T rootData) {
this(deep);
nodes[0] = rootData;
}
public void add(int parentIndex, T data, boolean left) {
if (data == null) {
throw new NullPointerException();
}
if (nodes[parentIndex] == null) {
throw new NoSuchElementException();
}
if (left) {
nodes[parentIndex * 2 + 1] = data;
} else {
nodes[parentIndex * 2 + 2] = data;
}
}
public boolean isEmpty() {
return nodes[0] == null;
}
/**
* 获取索引为index节点的父节点
*
* @param index
* @return
*/
public T getParent(int index) {
if (index == 0) {
return null;
}
return (T) nodes[(index - 1) / 2];
}
/**
* 获取索引为index的右节点
*
* @param index
* @return
*/
public T getRight(int index) {
if (2 * index + 2 >= capacity) {
return null;
}
return (T) nodes[index * 2 + 2];
}
/**
* 获取索引为index的左节点
*
* @param index
* @return
*/
public T getLeft(int index) {
if (2 * index + 1 >= capacity) {
return null;
}
return (T) nodes[2 * index + 1];
}
public T getRoot() {
return (T) nodes[0];
}
public int indexOf(T data) {
for (int i = 0; i < capacity; i++) {
if (nodes[i].equals(data)) {
return i;
}
}
return -1;
}
}
这种顺序存储结构仅适用于完全二叉树。因为最坏情况下,一个深度为k且只有k个结点的单支树需要2^k-1的一维数组,不存在的结点用0表示。
二叉树的链式存储
用二叉链表实现一个简单的二叉树。链表的每个Node包含三个域:left和right指针域以及数据域
public class TwoLinkedBinaryTree<T> {
class Node {
Object data;
Node left;
Node right;
public Node() {
}
public Node(Object data) {
this.data = data;
}
public Node(Object data, Node left, Node right) {
this.data = data;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return data + "";
}
}
private Node root;
public TwoLinkedBinaryTree() {
this.root = new Node();
}
public TwoLinkedBinaryTree(T data) {
this.root = new Node(data);
}
public Node addNode(Node parent, T data, boolean left) {
if (data == null) {
throw new NullPointerException();
}
if (parent == null) {
throw new IllegalStateException("节点为null,不能添加子节点");
}
if (left && parent.left != null) {
throw new IllegalStateException(parent + "节点已经存在左节点");
}
if (!left && parent.right != null) {
throw new IllegalStateException(parent + "节点已经存在右节点");
}
Node node = new Node(data);
if (left) {
parent.left = node;
} else {
parent.right = node;
}
return node;
}
public boolean isEmpty() {
return root.data == null;
}
public Node getRoot() {
return root;
}
public T getLeft(Node parent) {
return parent == null ? null : (T) parent.left.data;
}
public T getRight(Node parent) {
return parent == null ? null : (T) parent.right.data;
}
public int deep() {
return deep(root);
}
public int deep(Node node) {
if (node == null) {
return 0;
}
if (node.left == null && node.right == null) {
return 1;
}
int left = deep(node.left);
int right = deep(node.right);
//记录其所有左、右子树较大的深度
int max = left > right ? left : right;
//返回其左右子树中较大深度+1
return max + 1;
}
public static void main(String[] args) {
TwoLinkedBinaryTree<String> binaryTree = new TwoLinkedBinaryTree<>("0");
TwoLinkedBinaryTree<String>.Node left = binaryTree.addNode(binaryTree.getRoot(), "1", true);
TwoLinkedBinaryTree<String>.Node right = binaryTree.addNode(binaryTree.getRoot(), "2", false);
binaryTree.addNode(left, "3", true);
binaryTree.addNode(left, "4", false);
binaryTree.addNode(right, "5", true);
binaryTree.addNode(right, "6", false);
System.out.println("tree deep = " + binaryTree.deep());
System.out.println(left + "的左节点: " + binaryTree.getLeft(left));
System.out.println(left + "的右节点: " + binaryTree.getRight(left));
}
}
用三叉链表实现一个简单的二叉树。链表的每个Node包含四个域:left、right和parent指针域以及数据域
public class ThreeLinkedBinaryTree<T> {
public static class Node {
Object data;
Node left;
Node right;
Node parent;
public Node() {
}
public Node(Object data) {
this.data = data;
}
public Node(Object data, Node left, Node right, Node parent) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
}
@Override
public String toString() {
return data + "";
}
}
private Node root;
public ThreeLinkedBinaryTree() {
this.root = new Node();
}
public ThreeLinkedBinaryTree(T data) {
this.root = new Node(data);
}
public Node addNode(Node parent, T data, boolean left) {
if (data == null) {
throw new NullPointerException();
}
if (parent == null) {
throw new IllegalStateException("节点为null,不能添加子节点");
}
if (left && parent.left != null) {
throw new IllegalStateException(parent + "节点已经存在左节点");
}
if (!left && parent.right != null) {
throw new IllegalStateException(parent + "节点已经存在右节点");
}
Node node = new Node(data);
if (left) {
parent.left = node;
} else {
parent.right = node;
}
node.parent = parent;
return node;
}
public boolean isEmpty() {
return root.data == null;
}
public T getParent(Node node) {
if (node == null) {
throw new NullPointerException();
}
return (T) node.parent.data;
}
public T getLeft(Node parent) {
if (parent == null) {
throw new NullPointerException();
}
return (T) parent.left.data;
}
public T getRight(Node parent) {
if (parent == null) {
throw new NullPointerException();
}
return (T) parent.right.data;
}
public Node getRoot() {
return root;
}
public int deep() {
return deep(root);
}
public int deep(Node node) {
if (node == null) {
return 0;
}
if (node.left == null && node.right == null) {
return 1;
}
int left = deep(node.left);
int right = deep(node.right);
//记录其所有左、右子树较大的深度
int max = left > right ? left : right;
//返回其左右子树中较大深度+1
return max + 1;
}
public static void main(String[] args) {
ThreeLinkedBinaryTree<String> binaryTree = new ThreeLinkedBinaryTree<>("0");
Node left = binaryTree.addNode(binaryTree.getRoot(), "1", true);
Node right = binaryTree.addNode(binaryTree.getRoot(), "2", false);
binaryTree.addNode(left, "3", true);
binaryTree.addNode(left, "4", false);
binaryTree.addNode(right, "5", true);
binaryTree.addNode(right, "6", false);
System.out.println("tree deep = " + binaryTree.deep());
System.out.println(left + "的左节点: " + binaryTree.getLeft(left));
System.out.println(left + "的右节点: " + binaryTree.getRight(left));
System.out.println(left + "的父节点: " + binaryTree.getParent(left));
}
}
二叉树的遍历
树的遍历主要有两种,一个是深度优先遍历,一个是广度优先遍历。
深度优先遍历又有三种:先序、中序、后序遍历。先序、中序、后序遍历区别在于访问根节点和遍历左右子树的先后关系
先序遍历:先访问根结点,再先序遍历左子树,最后先序遍历右子树
private void preorder(Node<T> node) {
if (node == null) {
return;
}
System.out.println(node.value);//先访问根结点
preorder(node.left);
preorder(node.right);
}
中序遍历:先中序遍历左子树,再访问根节点,最后中序遍历右子树
private void inorder(Node<T> node) {
if (node == null) {
return;
}
inorder(node.left);
System.out.println(node.value);//中间访问根节点
inorder(node.right);
}
后序遍历:后序遍历左子树,再后序遍历右子树,最后访问根结点
private void postorder(Node<T> node) {
if (node == null) {
return;
}
postorder(node.left);
postorder(node.right);
System.out.println(node.value);//最后访问根节点
}
广度优先遍历也叫按层遍历(一层一层的遍历节点):
public void levelorder() {
if (root == null)
return;
Deque<Node<T>> queue = new ArrayDeque<>();
queue.addLast(root);
while (!queue.isEmpty()) {
Node<T> node = queue.removeFirst();
System.out.println(node.value);
if (node.left != null) {
queue.addLast(node.left);
}
if (node.right != null) {
queue.addLast(node.right);
}
}
}
C实现
#include<iostream>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
using namespace std;
typedef struct BiTNode
{
char data;
struct BiTNode *lchild , *rchild; //左、右孩子指针
} BiTNode , *BiTree;
bool CreateBiTree(BiTree &T)
{
//按先序序列输入二叉树中的节点值,空格代表空字符
char ch; //接收输入的字符
cout<<"先序输入字符"<<endl;
cin>>ch;
if(ch == ' ') T = NULL;
else
{
if(! (T = (BiTNode*) malloc (sizeof(BiTNode)))) return false;
T->data = ch;
CreateBiTree(T->lchild) ; //构造左子树
CreateBiTree(T->rchild) ;//构造右子树
}
return true;
}
/**********************这里构造一个栈 , 中序遍历需要栈 ********************************************/
typedef struct
{
BiTree *base;
BiTree *top;
int stacksize; //当前以分配的存储空间 , 以元素为单位
}SqStack;
bool InitStack(SqStack s)
{
s.base = (BiTree *) malloc( STACK_INIT_SIZE * sizeof(BiTree));
if(!s.base) return false; //存储分配失败
s.top = s.base;
s.stacksize = STACK_INIT_SIZE;
return true;
}
bool Push(SqStack &s , BiTree e)
{
if(s.top - s.base >= s.stacksize) { //栈满,追加存储空间
s.base = (BiTree *) realloc(s.base , (s.stacksize + STACKINCREMENT) * sizeof(BiTree));
if(!s.base) return false;
s.top = s.base + s.stacksize;
s.stacksize += STACKINCREMENT;
}
*s.top ++ = e;
return true;
}
bool Pop(SqStack &s , BiTree &e)
{
if(s.top == s.base ) return false; //栈空
e = * --s.top;
return true;
}
bool StackEmpty(SqStack s)
{
if(s.base == s.top) return true;
else
{
return false;
}
}
bool visit(BiTree e) //将二叉树的节点显示到控制台上
{
cout<<e->data;
return true;
}
SqStack s; //定义栈头结点
/*******************以上栈的构造完毕 ****************************/
//中序遍历
bool InOrderTraverse(BiTree T , bool (*visit)(BiTree e)) //后一个参数为接收visit函数指针
{
cout<<"中序遍历二叉树结果如下:"<<endl;
InitStack(s);
BiTree p ; p=T;
while(p || !StackEmpty(s))
{
if(p)
{
Push(s , p) ; p = p->lchild; //根指针进栈 , 遍历左子树
}
else
{
//根指针退栈, 访问根节点 ,遍历右子树
Pop(s , p);
cout<<p->data;
p = p->rchild;
}
}
return true;
}
//先序遍历
bool PreOrderTracerse(BiTree T , bool (*visit)(BiTree e))
{
if(T)
{
if( (*visit)(T))
if(PreOrderTracerse(T->lchild , visit))
if(PreOrderTracerse(T->rchild , visit)) return true;
return false;
}
else return true;
}
//后序遍历
bool PosOrderTracerse(BiTree T , bool (*visit)(BiTree e))
{
if(T)
{
if(PosOrderTracerse(T->lchild , visit))
if(PosOrderTracerse(T->rchild ,visit))
if((*visit)(T)) return true;
return false;
}
else return true;
}
//层次遍历
void LevelTracerse(BiTree T , bool (*visit)(BiTree e))
{
queue<BiTree> q;
if(T==NULL)
{
return;
}
q.push(T);
while(!q.empty()){
BiTree front=q.front();
if(front->lchild){
q.push(front->lchild)
}
if(front->rchild){
q.push(front->rchild)
}
cout<<front->data;
}
}
int main()
{
//定义一个visit函数指针
bool (*visit_point) (BiTree e);
visit_point = visit;
BiTree T;
CreateBiTree(T) ;
cout<<"二叉树构造完成!"<<endl<<endl<<endl;
InOrderTraverse(T , visit_point);
cout<<endl;
cout<<"先序遍历结果如下:"<<endl;
PreOrderTracerse(T , visit_point) ;
cout<<endl;
cout<<"后序遍历结果如下:"<<endl;
PosOrderTracerse(T , visit_point);
return true;
}