本文根據《大話數據結構》一書,對Java版的二叉樹、線索二叉樹進行了一定程度的實現。
另:
二叉樹的性質
性質1:二叉樹第i層上的結點數目最多為 2{i-1} (i≥1)。
性質2:深度為k的二叉樹至多有2{k}-1個結點(k≥1)。
性質3:在任意一棵二叉樹中,若終端結點的個數為n0,度為2的結點數為n2,則n0=n2+1。
證明提示:分支線總數=n0+n1+n2-1=n1+2×n2
性質4:具有n個節點的完全二叉樹的深度為[log2n]+1。([ ]代表向下取整)
證明提示:假設深度為k,則有2{k-1} -1<n≤2{k} -1。
性質5:如果有一顆有n個節點的完全二叉樹的節點按層次序編號,對任一層的節點i(1<=i<=n)有
1.如果i=1,則節點是二叉樹的根,無雙親,如果i>1,則其雙親節點為[i/2]
2.如果2i>n那么節點i沒有左孩子(葉子結點),否則其左孩子為2i
3.如果2i+1>n那么節點沒有右孩子,否則右孩子為2i+1
二叉鏈表的定義代碼
class BiTNode<E>{
E data;
BiTNode<E> lchild,rchild;
public BiTNode(E data) {
this.data=data;
this.lchild=null;
this.rchild=null;
}
}
public class BiTree<E> {
private BiTNode<E> root;
public BiTree() {
root=null;
}
...
}
二叉樹的遍歷
/*
* 前序遍歷
*/
public void preOrder() {
preOrderTraverse(root);
System.out.println();
}
private void preOrderTraverse(BiTNode<E> node) {
if(node==null)
return;
System.out.print(node.data);
preOrderTraverse(node.lchild);
preOrderTraverse(node.rchild);
}
/*
* 中序遍歷
*/
public void inOrder() {
inOrderTraverse(root);
System.out.println();
}
private void inOrderTraverse(BiTNode<E> node) {
if(node==null)
return;
inOrderTraverse(node.lchild);
System.out.print(node.data);
inOrderTraverse(node.rchild);
}
/*
* 后序遍歷
*/
public void postOrder() {
postOrderTraverse(root);
System.out.println();
}
private void postOrderTraverse(BiTNode<E> node) {
if(node==null)
return;
postOrderTraverse(node.lchild);
postOrderTraverse(node.rchild);
System.out.print(node.data);
}
二叉樹的建立
《大話》一書中,6.9節關於二叉樹的建立如下:

通過輸入AB#D##C##,可以生成上述二叉樹,其C語言的實現算法如下:


暫時能力有限,還不懂如何改為Java代碼。
幾點疑問:1.exit(OVERFLOW)不是很清楚什么意思;
2.代碼中為char類型,如何用於泛型?
3.scanf()在Java中怎么實現?用scanner嗎?程序如何知道輸入完“AB#D##C##”就結束二叉樹的構造呢?總的實現代碼(包括main部分)是怎么樣的?
以下為測試代碼遍歷的總體測試代碼:
package BiTree;
class BiTNode<E>{
E data;
BiTNode<E> lchild,rchild;
public BiTNode(E data) {
this.data=data;
this.lchild=null;
this.rchild=null;
}
}
public class BiTree<E> {
private BiTNode<E> root;
public BiTree() {
//root=new BiTNode(null, null, null);
root=null;
}
/*
* 前序遍歷
*/
public void preOrder() {
preOrderTraverse(root);
System.out.println();
}
private void preOrderTraverse(BiTNode<E> node) {
if(node==null)
return;
System.out.print(node.data);
preOrderTraverse(node.lchild);
preOrderTraverse(node.rchild);
}
/*
* 中序遍歷
*/
public void inOrder() {
inOrderTraverse(root);
System.out.println();
}
private void inOrderTraverse(BiTNode<E> node) {
if(node==null)
return;
inOrderTraverse(node.lchild);
System.out.print(node.data);
inOrderTraverse(node.rchild);
}
/*
* 后序遍歷
*/
public void postOrder() {
postOrderTraverse(root);
System.out.println();
}
private void postOrderTraverse(BiTNode<E> node) {
if(node==null)
return;
postOrderTraverse(node.lchild);
postOrderTraverse(node.rchild);
System.out.print(node.data);
}
/*
* 6.9 二叉樹的建立暫時不會,略
*/
public static void main(String[] args) {
BiTree<String> aBiTree = new BiTree<String>();
aBiTree.root=new BiTNode("A");
aBiTree.root.lchild=new BiTNode("B");
aBiTree.root.rchild=new BiTNode("C");
aBiTree.root.lchild.rchild=new BiTNode("D");
System.out.println("————前序————");
aBiTree.preOrder();
System.out.println("————中序————");
aBiTree.inOrder();
System.out.println("————后序————");
aBiTree.postOrder();
}
}
————前序————
ABDC
————中序————
BDAC
————后序————
DBCA
線索二叉樹

對一個有n個節點的二叉鏈表(如上圖),整表存在2n個指針域,但分支線只有n-1條,說明空指針域的個數為2n-(n-1) = n+1個,浪費了很多的內存資源。
我們可以通過利用這些空指針域,存放節點在某種遍歷方式下的前驅和后繼節點的指針。我們把這種指向前驅和后繼的指針成為線索,加上線索的二叉鏈表成為線索鏈表,對應的二叉樹就成為“線索二叉樹(Threaded Binary Tree)”,如下圖所示。

線索二叉樹的Java代碼如下:
package BiThrTree;
/**
* 線索二叉樹
* 包含二叉樹的中序線索化及其遍歷
* @author Yongh
*
*/
class BiThrNode<E>{
E data;
BiThrNode<E> lChild,rChild;
boolean lTag,rTag;
public BiThrNode(E data) {
this.data=data;
//tag都先定義成左右孩子指針。
lTag=false; //其實把Tag定義為IsThread更好
rTag=false;
lChild=null;
rChild=null;
}
}
public class BiThrTree<T> {
BiThrNode<T> root;
boolean link=false,thread=true;
public BiThrTree() {
root=null;
}
/*
* 中序線索化二叉樹
* 即:在遍歷的時候找到空指針進行修改。
*/
BiThrNode<T> pre; //線索化時記錄的前一個結點
public void inThreading() {
inThreading(root);
}
private void inThreading(BiThrNode<T> p) {
if(p != null) {
inThreading(p.lChild);
if(p.lChild==null) {
p.lTag=thread;
p.lChild=pre;
}
if(pre!=null && pre.rChild==null) { //pre!=null一定要加上
pre.rTag=thread;
pre.rChild=p;
}
pre=p; //別忘了在這個位置加上pre=p
inThreading(p.rChild);
}
}
/*
* 中序遍歷二叉線索鏈表表示的二叉樹(按后繼方式)
* 書中添加了一個頭結點,本程序中不含頭結點
* 思路:先找到最左子結點
*/
public void inOrderTraverse() {
BiThrNode<T> p = root;
while(p!=null) {
while(p.lTag==link)
p=p.lChild; //找到最左子結點
System.out.print(p.data);
while(p.rTag==thread) { //不是if
p=p.rChild;
System.out.print(p.data);
}
p=p.rChild;
}
System.out.println();
}
/*
* 中序遍歷方法二(按后繼方式)
* 參考別人的博客
*/
public void inOrderTraverse2() {
BiThrNode<T> node = root;
while(node != null && node.lTag==link) {
node = node.lChild;
}
while(node != null) {
System.out.print(node.data + ", ");
if(node.rTag==thread) {//如果右指針是線索
node = node.rChild;
} else { //如果右指針不是線索,找到右子樹開始的節點
node = node.rChild;
while(node != null && node.lTag==link) {
node = node.lChild;
}
}
}
System.out.println();
}
public static void main(String[] args) {
BiThrTree<String> aBiThrTree = new BiThrTree<String>();
aBiThrTree.root=new BiThrNode<String>("A"); // A
aBiThrTree.root.lChild=new BiThrNode<String>("B"); // / \
aBiThrTree.root.lChild.lChild=new BiThrNode<String>("C"); // B D
aBiThrTree.root.rChild=new BiThrNode<String>("D"); // / / \
aBiThrTree.root.rChild.lChild=new BiThrNode<String>("E"); // C E F
aBiThrTree.root.rChild.rChild=new BiThrNode<String>("F");
aBiThrTree.inThreading();
aBiThrTree.inOrderTraverse();
aBiThrTree.inOrderTraverse2();
}
}
CBAEDF
C, B, A, E, D, F,
推薦閱讀:
線索二叉樹原理及前序、中序線索化(Java版)(文中對線索二叉樹的介紹和代碼都比較清晰,且更加全面)。
赫夫曼樹及其應用
帶權路徑長度WPL最小的二叉樹稱為最優二叉樹,也稱為赫夫曼樹(Huffman Tree)。

赫夫曼編碼:


推薦閱讀:哈夫曼樹(三)之 Java詳解
