基本概念
二叉树:递归定义。二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
五种形态:1.空树;2.仅有一个根结点的二叉树;3.仅有左子树而右子树为空的二叉树;4.仅有右子树而左子树为空的二叉树;5.左、右子树均非空的二叉树
满二叉树:除叶子结点外的所有结点均有两个子结点。特点:节点数达到最大值时所有叶子结点必须在同一层上;总节点数一定是奇数
完全二叉树:若树有K层,则0~K-1层结点都完全填满,在第K层上如果不是满的,则只缺少右边的若干结点。至于和满二叉树区别,从定义可以看出!(设某个结点为序号为i, 如果它有左子树,那么左子树的位置是2i+1,如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是floor((i-1)/2))
哈夫曼树:为满二叉树。应用:哈夫曼编码。定义:一种带权路径长度最短(WPL)的二叉树,也称为最优二叉树。,一棵二叉树要使其WPL值最小,必须使权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。
一些基本性质:在二叉树的第K层上,最多有2^(k-1)个节点;层数为K的二叉树最多有2^K-1个节点,最少有K个节点;对于一棵非空的二叉树,如果叶子节点数为n0,度数为2的节点数为n2,则有n0=n2+1
二叉树遍历
遍历是二叉树最主要和最基本的方法,通常有层次遍历,前、中、后序遍历!需要掌握递归和非递归算法!
- 定义一个二叉树节点类
package xianggen.tree;
/**
* 树节点类,泛型
* TreeNode.java
* @author xianggen
* @date 2016年8月11日 下午2:37:23
* @param <E>
*/
public class TreeNode<E> {
private E value;
private TreeNode<E> leftChild;
private TreeNode<E> rightChild;
/**
* 无参构造函数
*/
public TreeNode(){
}
/**
* 带value构造函数
* @param value
*/
public TreeNode(E value){
this.value=value;
leftChild=null;
rightChild=null;
}
/**
* 三个参数的set和get方法
* @return
*/
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
public TreeNode<E> getLeftChild() {
return leftChild;
}
public void setLeftChild(TreeNode<E> leftChild) {
this.leftChild = leftChild;
}
public TreeNode<E> getRightChild() {
return rightChild;
}
public void setRightChild(TreeNode<E> rightChild) {
this.rightChild = rightChild;
}
}
- 二叉树类,各种遍历算法
package xianggen.tree;
import java.util.LinkedList;
import java.util.Stack;
/**
* 二叉树,前、中后序遍历 BinaryTree.java
*
* @author xianggen
* @date 2016年8月11日 下午2:38:42
*/
public class BinaryTree {
private TreeNode<Integer> root;
public BinaryTree() {
root = new TreeNode<Integer>();
}
/**
* 带参构造函数
*
* @param root
*/
public BinaryTree(TreeNode<Integer> root) {
this.root = root;
}
/**
* 获取二叉树根节点
*
* @return
*/
public TreeNode<Integer> getRoot() {
return root;
}
/**
* 数组构造器,利用给定数组创建一棵二叉树
* @param arr
*/
public BinaryTree(int[] arr) {
boolean isLeft = true;
int len = arr.length;
if (len == 0)
return;
LinkedList<TreeNode<Integer>> queue = new LinkedList<TreeNode<Integer>>();
root = new TreeNode<Integer>(arr[0]); // 数组第一个元素作为根节点
queue.addLast(root);
TreeNode<Integer> parentNode,currentNode;
for (int i = 1; i < len; i++) {
currentNode = new TreeNode<Integer>(arr[i]);
queue.addLast(currentNode);
if (isLeft) {
parentNode = queue.getFirst();
} else {
parentNode = queue.removeFirst();
}
if (isLeft) {
parentNode.setLeftChild(currentNode);
isLeft = false;
} else {
parentNode.setRightChild(currentNode);
isLeft = true;
}
}
}
/**
* 访问节点,获取value
*
* @param treeNode
*/
public void visitTreeNode(TreeNode<Integer> treeNode) {
System.out.print(treeNode.getValue() + " ");
}
/**
* 递归获取二叉树深度
*
* @param treeNode
* @return
*/
public int getTreeDepthByRecursion(TreeNode<Integer> treeNode) {
if (treeNode == null) {
return 0;
}
TreeNode<Integer> leftChild = treeNode.getLeftChild();
TreeNode<Integer> rightChild = treeNode.getRightChild();
return Math.max(getTreeDepthByRecursion(leftChild),
getTreeDepthByRecursion(rightChild)) + 1;
}
/**
* 层次遍历--非递归
* 采用一个队列作为辅助!!先进先出!
*/
public void TraverseByLevel() {
LinkedList<TreeNode<Integer>> queue = new LinkedList<TreeNode<Integer>>();
if (root != null) {
queue.add(root);
}
TreeNode<Integer> currentNode;
while (!queue.isEmpty()) {
currentNode = queue.removeFirst();
if (currentNode.getLeftChild() != null) {
queue.addLast(currentNode.getLeftChild());
}
if (currentNode.getRightChild() != null) {
queue.addLast(currentNode.getRightChild());
}
visitTreeNode(currentNode);
}
System.out.println();
}
/**
* 前序遍历--递归
* 二叉树前序递归遍历!
* 先访问根结点然后遍历左子树,最后遍历右子树
* 在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。
* @param treeNode
*/
public void TraverseByPreOrderRecursion(TreeNode<Integer> treeNode) {
if (treeNode != null) { //在此处做非空判断后就不需要在下面子节点判断非空
visitTreeNode(treeNode);
TraverseByPreOrderRecursion(treeNode.getLeftChild());
TraverseByPreOrderRecursion(treeNode.getRightChild());
}
// System.out.println();
}
/**
* 前序遍历--非递归
* 非递归前序(先根)遍历二叉树
* 利用栈先进后出的特点,先将右子节点压栈
* @param treeNode
*/
public void TraverseByPreOrder(TreeNode<Integer> treeNode){
Stack<TreeNode<Integer>> stack=new Stack<TreeNode<Integer>>();
TreeNode<Integer> currentNode=treeNode;
if(currentNode!=null){
stack.push(currentNode);
while(!stack.isEmpty()){
currentNode=stack.pop();
visitTreeNode(currentNode);
//关键,根据栈先进后出的特点,先讲右子节点压栈
if(currentNode.getRightChild()!=null){
stack.push(currentNode.getRightChild());
}
if(currentNode.getLeftChild()!=null){
stack.push(currentNode.getLeftChild());
}
}
}
}
/**
* 中序遍历--递归
* 首先遍历左子树,然后访问根结点,最后遍历右子树
* 在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树
* @param treeNode
*/
public void TraverseByInOrderRecursion(TreeNode<Integer> treeNode) {
if(treeNode!=null){
TraverseByInOrderRecursion(treeNode.getLeftChild());
visitTreeNode(treeNode);
TraverseByInOrderRecursion(treeNode.getRightChild());
}
}
/**
* 中序遍历--非递归
* 首先遍历左子树,然后访问根结点,最后遍历右子树
* 在遍历左、右子树时,仍然先遍历左子树,再访问根结点,最后遍历右子树
* @param treeNode
*/
public void TraverseByInOrder(TreeNode<Integer> treeNode){
Stack<TreeNode<Integer>> stack=new Stack<TreeNode<Integer>>();
TreeNode<Integer> currentNode=treeNode;
//注意循环条件
while(currentNode!=null||!stack.isEmpty()){
//这一步非常关键,找到当前节点的左孩子,一直循环,直至不能找到左孩子为止!!
while(currentNode!=null){
stack.push(currentNode);
currentNode=currentNode.getLeftChild();
}
if(!stack.isEmpty()){
currentNode=stack.pop();
visitTreeNode(currentNode);
currentNode=currentNode.getRightChild();
}
}
}
/**
* 后序遍历--递归
* 先遍历左子树,然后遍历右子树,最后访问根结点
* 在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点
* @param treeNode
*/
public void TraverseByPostOrderRecursion(TreeNode<Integer> treeNode){
if(treeNode!=null){
TraverseByPostOrderRecursion(treeNode.getLeftChild());
TraverseByPostOrderRecursion(treeNode.getRightChild());
visitTreeNode(treeNode);
}
}
/**
* 后序遍历--非递归_1(单栈)
* 先遍历左子树,然后遍历右子树,最后访问根结点
* 在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点
* @param treeNode
*/
public void TraverseByPostOrder_1(TreeNode<Integer> treeNode){
Stack<TreeNode<Integer>> stack=new Stack<TreeNode<Integer>>();
TreeNode<Integer> currentNode=treeNode,previousNode=treeNode;
while(currentNode!=null||!stack.isEmpty()){
while(currentNode!=null){
stack.push(currentNode);
currentNode=currentNode.getLeftChild();
}
if(!stack.isEmpty()){
TreeNode<Integer> tempNode=stack.peek().getRightChild();
//这一步非常重要,previousNode==tempNode表示右子树已访问
if(tempNode==null||previousNode==tempNode){
currentNode=stack.pop();
visitTreeNode(currentNode);
previousNode=currentNode;
currentNode=null;
}else{
currentNode=tempNode;
}
}
}
}
/**
* 后序遍历--非递归_2(双栈)
* 先遍历左子树,然后遍历右子树,最后访问根结点
* 在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点
* @param treeNode
*/
public void TraverseByPostOrder_2(TreeNode<Integer> treeNode){
Stack<TreeNode<Integer>> leftStack=new Stack<TreeNode<Integer>>();
Stack<TreeNode<Integer>> rightStack=new Stack<TreeNode<Integer>>();
TreeNode<Integer> currentNode=treeNode,rightNode;
do{
while(currentNode!=null){
rightNode=currentNode.getRightChild();
leftStack.push(currentNode);
rightStack.push(rightNode);
currentNode=currentNode.getLeftChild();
}
currentNode=leftStack.pop();
rightNode=rightStack.pop();
if(rightNode==null){
visitTreeNode(currentNode);
}else{
leftStack.push(currentNode);
rightStack.push(null);
}
currentNode=rightNode;
}while(!leftStack.isEmpty()||!rightStack.isEmpty());
}
public static void main(String[] args) {
int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
BinaryTree bt = new BinaryTree(arr);
System.out.println("根节点值:"+bt.getRoot().getValue());
System.out.println("树深度值:"+bt.getTreeDepthByRecursion(bt.getRoot()));
System.out.println("------层次遍历------");
bt.TraverseByLevel();
System.out.println("------前序遍历------");
bt.TraverseByPreOrderRecursion(bt.getRoot());
System.out.println();
bt.TraverseByPreOrder(bt.getRoot());
System.out.println();
System.out.println("------中序遍历------");
bt.TraverseByInOrderRecursion(bt.getRoot());
System.out.println();
bt.TraverseByInOrder(bt.getRoot());
System.out.println();
System.out.println("------后序遍历------");
bt.TraverseByPostOrderRecursion(bt.getRoot());
System.out.println();
bt.TraverseByPostOrder_1(bt.getRoot());
System.out.println();
bt.TraverseByPostOrder_2(bt.getRoot());
System.out.println();
}
}