二叉树定义:二叉树由结点的有限集合构成。
二叉树的五种形态:
a:空二叉树,b只有一个根节点的二叉树,cd只左子树点或右子树的树,e有2个子树的树。
特殊的二叉树:1)满二叉树,2)完全二叉树 ,3)扩充二叉树。
1)满二叉树。
一颗二叉树的任何节点,或者是树叶,或者恰好有2棵非空子树。
完全二叉树:
只有最下面的2层的节点度数可以小于2
最下面一层的结点都集中在该曾最左边的若干位置上。
扩充二叉树:
当二叉树出现空的子树时,就增加新的特殊的结点,空树叶。扩充二叉树是满二叉树。
二叉树的性质。
二叉树第i曾(根为0层)最多有2^i个结点。
高度为k的二叉树,最多有2^k-1个结点。
二叉树的数据类型抽象(采用链式结构)。
结点类型:
//树的节点
public class BinaryTreeNode<T extends Comparable<T>> {
public static final int LEFT_FLAG = 0;
public static final int RIGHT_FLAG = 1;
//左指针
private BinaryTreeNode<T> left;
//右指针
private BinaryTreeNode<T> right;
//数据
private T data;
//标志位 用于后序遍历
private int tag;
public BinaryTreeNode(){
}
}
树的接口定义:
public interface Tree<T extends Comparable<T>> {
boolean isEmpty();
int size();
//先根遍历
void preOrder();
//中根遍历
void inOrder();
//后根遍历
void postOrder();
/**
* 广度优先遍历
*/
void levelOrder();
BinaryTreeNode<T> root();
BinaryTree<T> createTree(BinaryTreeNode<T> root);
}
二叉树的遍历算法。
1)递归法,2)非递归法。
这2中方法中又分为1)先根遍历,2)中根根遍历,3)后跟遍历
先根遍历,先访问根结点,再依次访问左右结点
中根遍历,先访问左结点,再访问根结点,最后访问右结点。
后根遍历,先访依次访问左右子结点,最后访问根结点。
1)递归法
1.1)先根遍历
private void preOrder(BinaryTreeNode<T> p) {
if (p == null)
return;
visit(p);
preOrder(p.leftChild());
preOrder(p.rightChild());
}
1.2)中根遍历
private void inOrder(BinaryTreeNode<T> p) {
if (p == null)
return;
inOrder(p.leftChild());
visit(p);
inOrder(p.rightChild());
}
1.3)后根遍历
private void postOrder(BinaryTreeNode<T> p) {
if (p == null)
return;
postOrder(p.leftChild());
postOrder(p.rightChild());
visit(p);
}
每个结点进去后都递归调用,分成2个调用方向,一个向左一个像右。
测试:
/**
* A
* B C
* D E F
* H I j K
*/
// 先根顺序
// A B D C E H I F J K
// 中根顺序
// D B A H E I C J F K
// 后跟顺序
// D B H I E J K F C A
2)非递归遍历,非递归遍历需要结束栈。
2.1.1)非递归遍历,先根遍历
public void preOrder() {
BinaryTreeNode<T> current = root;
while ( !stack.isEmpty() || current != null){
if (current != null){
visit(current);
//不停的往左,知道最左边一个叶子结点,进栈
stack.push(current);
current = current.leftChild();
}else {
BinaryTreeNode<T> node = stack.pop();
//先根访问可以加
// while (!stack.isEmpty()&&node.rightChild() == null){
// node = stack.pop();
// }
//向右扩展
current = node.rightChild();
}
}
}
2.2)非递归,中根遍历
public void inOrder() {
BinaryTreeNode<T> current = root;
while ( !stack.isEmpty() || current != null){
if (current != null){
stack.push(current);
current = current.leftChild();
}else {
BinaryTreeNode<T> node = stack.pop();
//此处执行操作
visit(node);
current = node.rightChild();
}
}
}
2.3)非递归,后根遍历
public void postOrder() {
BinaryTreeNode<T> current = root;
while (true){
while (current != null){
//tag用于标记结点是从左边还是右边入栈
current.setTag(BinaryTreeNode.LEFT_FLAG);
//不停的像右扩展,并压入栈中
stack.push(current);
current = current.leftChild();
}
//到最左边的叶子几点后出栈
current = stack.pop();
while (current.getTag() == BinaryTreeNode.RIGHT_FLAG){
visit(current);
if (stack.empty()){
return;
}else {
current = stack.pop();
}
}
//再次入栈,将入栈标记改为右边
current.setTag(BinaryTreeNode.RIGHT_FLAG);
stack.push(current);
current = current.rightChild();
}
}
测试:
/**
* A
* B C
* D E F
* H I j K
*/
// 先根顺序
// A B D C E H I F J K
// 中根顺序
// D B A H E I C J F K
// 后跟顺序
// D B H I E J K F C A
3)二叉树的广度周游
广度周游需要借助队列实现(FIFO)
public void levelOrder(){
BinaryTreeNode<T> current = root;
queue.offer(current);
while (!queue.isEmpty()){
current = queue.poll();
visit(current);
if (current.leftChild() != null){
queue.offer(current.leftChild());
}
if (current.rightChild() != null){
queue.offer(current.rightChild());
}
}
}
广度周游是按照二叉树的层次从上到下从左到右的遍历。
测试:
/**
* A
* B C
* D E F
* H I j K
*/
// 广度周游
// A B C D E F H I J K