二叉樹的相關操作,主要包括創建、判空、刪除子樹、刪除節點、各種遍歷算法等(包括先根遍歷、中根遍歷、後根遍歷、層次遍歷等)。本章重點說明四種遍歷算法與刪除節點操作的實現。以如下二叉樹爲例:
1、先根遍歷:先訪問根節點,然後是左節點,然後是右節點,遞歸直至訪問結束;上述二叉樹先根遍歷順序爲ABDGCEFH
2、中根遍歷:先訪問左節點,然後根節點,然後右節點,遞歸訪問至結束;上述二叉樹中根遍歷順序爲G D B A E C F H
3、後根遍歷:先訪問左節點,然後右節點,然後根節點,遞歸訪問至結束;上述二叉樹後根遍歷順序爲G D B E H F C A
4、層次遍歷:從根節點開始,從左往右遍歷,一層遍歷完,再前往下一層繼續遍歷,直至結束。上述二叉樹後根遍歷順序爲 A B C D E F G H
5、二叉樹節點刪除:二叉樹節點的刪除在普通二叉樹中的意義不大,但是在二叉排序樹(也叫做二叉查找樹)中的應用卻十分重要。具體分析在二叉樹節點刪除的方法中會詳細說明。
下面貼代碼:
package erchashu;
public class BinaryTree {
public TreeNode root;
public TreeNode node_b;
public TreeNode node_c;
public TreeNode node_d;
public TreeNode node_e;
public TreeNode node_f;
public TreeNode node_g;
public TreeNode node_h;
public BinaryTree()
{
root=new TreeNode("A");
}
//創建一顆樹
public void CreateTree()
{
node_b=new TreeNode("B");
node_c=new TreeNode("C");
node_d=new TreeNode("D");
node_e=new TreeNode("E");
node_f=new TreeNode("F");
node_g=new TreeNode("G");
node_h=new TreeNode("H");
root.lchild=node_b;
root.rchild=node_c;
node_b.lchild=node_d;
node_d.lchild=node_g;
node_c.lchild=node_e;
node_c.rchild=node_f;
node_f.rchild=node_h;
}
//判斷是否是空樹
public boolean isEmpty()
{
return root==null;
}
//計算樹的高度
public int TreeHeight()
{
int length=height(root);
return length;
}
public int height(TreeNode root)
{
if(root==null)
return 0;
else
{
int i=height(root.lchild);
int j=height(root.rchild);
return (i>j)?(i+1):(j+1);
}
}
//計算節點個數
public int TreeNodeIndex()
{
return size(root);
}
public int size(TreeNode root)
{
if(root==null)
return 0;
else
{
return 1+size(root.lchild)+size(root.rchild);
}
}
//返回父母節點
public TreeNode parentNode(TreeNode root,TreeNode trnode)
{
TreeNode node=null;
if(root!=null&&root!=trnode)
{
if(root.lchild==trnode||root.rchild==trnode)
return root;
else
{
TreeNode s1=parentNode(root.lchild,trnode);
TreeNode s2=parentNode(root.rchild,trnode);
if(s1!=null)
node=s1;
else
if(s2!=null)
node=s2;
}
}
return node;
}
//刪除子樹,需確定刪除的子樹是其父節點的左子樹還是右子樹
public void deleteNodeTree(TreeNode trnode)
{
if(root==null||root==trnode)
{
root=null;
return;
}
TreeNode temp=parentNode(root,trnode);
if(temp.lchild==trnode)
temp.lchild=null;
if(temp.rchild==trnode)
temp.rchild=null;
if(trnode!=null)
{
deleteNode(trnode.lchild);
deleteNode(trnode.rchild);
}
}
/*
* 刪除節點
*
*
* 關於刪除節點,這裏有一點要特別說明,刪除操作一般是應用於二叉查找樹,涉及到排序的問題,所以在刪除的時候要考慮到排序不能亂,
* 所以關於二叉樹 的刪除較爲複雜一些。即我們刪除後的二叉樹依舊是二叉排序樹。
* 這裏我們不多做考慮,主要是爲了闡述刪除節點的算法,如果覺得這裏寫的不清楚,可以查看我後面寫的關於二叉查找樹的博客,二叉查找樹也叫二叉排序樹
*
*
* 刪除節點依據要刪除節點位置分爲三種情況來討論
* 1.刪除節點爲葉子節點,則直接刪除
* 2.刪除節點不是葉子節點,且該節點僅有一個孩子節點,則將該節點刪除,該節點的孩子節點直接放置於刪除節點位置
* 3.刪除節點不是葉子節點,且該節點有兩個孩子節點,此時按照中序排列取該節點的前驅或者後驅置於該節點位置並刪除前驅或者後驅節點
* 我們選擇的前驅或者後驅節點必然是沒有左孩子或者右孩子的節點,或者是葉子節點
*/
public void deleteNode(TreeNode trnode)
{
if(root==null)
{
root=null;
return;
}
TreeNode tnee=parentNode(root,trnode);
//要刪除的節點爲葉子結點,先確定該節點是其父母節點的左節點還是右節點
if(trnode.lchild==null&&trnode.rchild==null)
{
if(trnode==root)
{
root=null;
return;
}
if(tnee.lchild==trnode)
tnee.lchild=null;
if(tnee.rchild==trnode)
tnee.rchild=null;
return;
}
if(trnode.lchild!=null&&trnode.rchild==null)
{
if(root==trnode)
{
root=trnode.lchild;
return;
}
if(tnee.lchild==trnode)
tnee.lchild=trnode.lchild;
if(tnee.rchild==trnode)
tnee.rchild=trnode.lchild;
return;
}
if(trnode.lchild==null&&trnode.rchild!=null)
{
if(root==trnode)
{
root=trnode.rchild;
return;
}
if(tnee.lchild==trnode)
tnee.lchild=trnode.rchild;
if(tnee.rchild==trnode)
tnee.rchild=trnode.rchild;
return;
}
if(trnode.lchild!=null&&trnode.rchild!=null)
{
if(root==trnode) //根節點的刪除要單獨處理
{
TreeNode tmp=trnode.rchild;
while(tmp.lchild!=null)
tmp=tmp.lchild;
deleteNode(tmp);
root=tmp;
tmp.rchild=trnode.rchild;
tmp.lchild=trnode.lchild;
return;
}
//我們這裏採用該節點處中根遍歷的後驅節點
TreeNode temp=trnode.rchild;
while(temp.lchild!=null)
temp=temp.lchild;
if(tnee.lchild==trnode)
{
deleteNode(temp);
tnee.lchild=temp;
temp.rchild=trnode.rchild;
temp.lchild=trnode.lchild;
}
if(tnee.rchild==trnode)
{
deleteNode(temp);
tnee.rchild=temp;
temp.rchild=trnode.rchild;
temp.lchild=trnode.lchild;
}
return;
}
}
//先根遍歷
public void PreOrder(TreeNode trnode)
{
if(trnode!=null)
{
visit(trnode);
PreOrder(trnode.lchild);
PreOrder(trnode.rchild);
}
}
//中根遍歷
public void InOrder(TreeNode trnode)
{
if(trnode!=null)
{
InOrder(trnode.lchild);
visit(trnode);
InOrder(trnode.rchild);
}
}
//後根遍歷
public void PostOrder(TreeNode trnode)
{
if(trnode!=null)
{
PostOrder(trnode.lchild);
PostOrder(trnode.rchild);
visit(trnode);
}
}
//層次遍歷,層次遍歷這裏使用了隊列.隊列使用的類型是帶頭結點的單循環鏈表
public void LevelOrder(TreeNode trnode)
{
Queue mqq=new Queue();
if(trnode==null)
return;
mqq.EnQueue(trnode);
while(mqq.first.next!=mqq.first)
{
TreeNode node=mqq.DeQueue();
System.out.print(node.data+" ");
if(node.lchild!=null)
mqq.EnQueue(node.lchild);
if(node.rchild!=null)
mqq.EnQueue(node.rchild);
}
}
public void visit(TreeNode trnode)
{
System.out.print(trnode.data+" ");
}
//測試
public static void main(String[] args)
{
BinaryTree btr=new BinaryTree();
btr.CreateTree();
btr.isEmpty();
int height=btr.TreeHeight();
System.out.println("該樹高度爲: "+height);
int index=btr.TreeNodeIndex();
System.out.println("該樹節點數爲: "+index);
TreeNode tn=btr.parentNode(btr.root,btr.node_d);
System.out.println("該節點的父節點是: "+tn.data);
// btr.deleteNodeTree(btr.node_g); //刪除子樹
// btr.deleteNode(btr.root); //刪除節點
System.out.print("先根遍歷的結果:");
btr.PreOrder(btr.root);
System.out.println();
System.out.print("中根遍歷的結果:");
btr.InOrder(btr.root);
System.out.println();
System.out.print("後根遍歷的結果:");
btr.PostOrder(btr.root);
System.out.println();
System.out.print("層次遍歷的結果: ");
btr.LevelOrder(btr.root);
System.out.println();
}
}
package erchashu;
public class TreeNode {
public TreeNode lchild;
public TreeNode rchild;
public TreeNode next;
public String data;
public TreeNode(String data)
{
this.data=data;
this.lchild=null;
this.rchild=null;
this.next=null;
}
}
package erchashu;
public class Queue {
public TreeNode first;
public String data;
public Queue()
{
first=new TreeNode(null);
first.next=first;
}
//入隊
public void EnQueue(TreeNode node)
{
TreeNode temp=first;
while(temp.next!=first)
temp=temp.next;
temp.next=node;
node.next=first;
}
//出隊
public TreeNode DeQueue()
{
TreeNode tr=first.next;
first.next=tr.next;
return tr;
}
}