一,二叉樹
每個節點均不超過2 度的有序樹,稱作二叉樹(Binary tree)
在算法領域,二叉樹的重要地位是其它結構無法替代的。所謂二叉樹就是各節點不超過2 度的有序樹,因此每個節點的孩子(如果存在的話)可以左、右區分,分別稱作左孩子和右孩子——之所以如此命名,是因為在畫出二叉樹時,通常都將左、右孩子分別畫在其父節點的左下方、右下方。
二,二叉樹ADT
操作方法 | 功能描述 |
---|---|
getElement() | 返回存放當前節點處的對象 輸入:無 輸出:對象 |
setElement(e) | 將對象e 存入當前節點,并返回其中此前所存的內容 輸入:一個對象 輸出:對象 |
getParent() | 返回當前節點的父節點 輸入:無 輸出:樹節點 |
getLChild() | 返回當前節點的左孩子 輸入:無 輸出:二叉樹節點 |
getRChild() | 返回當前節點的右孩子 輸入:無 輸出:二叉樹節點 |
三,基于鏈表實現二叉樹
二叉樹的基本算法分析
** getSize()、getHeight()和getDepth() **
這三個方法的功能是,分別返回二叉子樹的規模、樹根節點的高度和深度。
這里為每個節點設置了三個變量size、height和depth,分別對應于這三個指標,這樣,只需在O(1)時間內返回相應的變量,即可實現相應的功能。**updateSize() **
若當前節點的孩子發生變化,比如原有的某個孩子被刪除或者有新的孩子插入,就需要更新當前節點及其祖先的規模記錄,以便后續的查詢。
算法:updateSize(v)
輸入:二叉樹中任一節點v
輸出:更新v的后代規模記錄
{
令size(v) = 1 + size(lc) + size(rc);
若v的父親p存在,則調用updateSize(p),遞歸地更新父親的規模記錄;//尾遞歸,可改寫為迭代形式
}
-
updateHeight()
同樣地,在孩子發生變化后,也有必要更新當前節點的高度記錄。
算法:updateHeight(v)
輸入:二叉樹中任一節點v
輸出:更新v的高度記錄
{
height(v) = 0;//先假設沒有左、右孩子
若v有左孩子lc,則令:height(v) = Max(height(v), 1 + height(lc));
若v有右孩子lc,則令:height(v) = Max(height(v), 1 + height(rc));
若v的父親p存在,則調用updateHeight(p),遞歸地更新父親的高度記錄;
}
-
updateDepth()
在父親節點發生變化后,有必要更新當前節點的深度記錄。
算法:updateDepth(v)
輸入:二叉樹中任一節點v
輸出:更新v的深度記錄
{
若v的父親節點p存在,則令depth(v) = depth(p)+1;
否則,令depth(v) = 0;
若v的左孩子lc存在,則調用updateDepth(lc);//沿孩子引用逐層向下,
若v的右孩子rc存在,則調用updateDepth(rc);//遞歸地更新所有后代的深度記錄
}
-
secede()
為了簡化二叉樹的動態操作的實現,這里專門設計了一個secede()方法。,該
方法的功能是,將以某一節點為根的子樹從母樹中分離出來。
算法:secede(v)
輸入:二叉樹中任一節點v
輸出:將以v為根的子樹叢母樹中分離出來
{
若v有父親{
切斷父親指向v的引用;
調用updateSize(v)和updateHeight(v),更新v及其祖先的規模記錄和高度記錄;
切斷v指向父親的引用;
調用updateDepth(v),更新v及其后代的深度記錄;
}
}
-
attachL()和attachR()
這一對方法的功能是,將節點c作為當前節點的左或右孩子與節點v聯接起來。
算法:attachL(p, c)
輸入:兩個二叉樹節點p與c
輸出:將c作為左孩子,與p聯接起來
{
若p已經有左孩子lc,則首先調用secede(lc)將其摘除;
調用secede(c),使c及其后代脫離原屬母樹;
設置相應的引用,在p和c之間建立父子關系;
調用updateSize(p)和updateHeight(p),更新節點p及其祖先的規模和高度;
調用updateDepth(c),更新c及其后代節點的深度;
}
二叉樹節點接口:BinTreePosition
package dsa.BinTree;
/*
* 二叉樹節點ADT接口
*/
import dsa.Iterator.Iterator;
/*
* 二叉樹節點ADT接口
*/
import other.Position;
/*
* 二叉樹節點ADT接口
*/
public interface BinTreePosition extends Position {
// 判斷是否有父親(為使代碼描述簡潔)
public boolean hasParent();
// 返回當前節點的父節點
public BinTreePosition getParent();
// 設置當前節點的父節點
public void setParent(BinTreePosition p);
// 判斷是否為葉子
public boolean isLeaf();
// 判斷是否為左孩子(為使代碼描述簡潔)
public boolean isLChild();
// 判斷是否有左孩子(為使代碼描述簡潔)
public boolean hasLChild();
// 返回當前節點的左孩子
public BinTreePosition getLChild();
// 設置當前節點的左孩子(注意:this.lChild和c.parent都不一定為空)
public void setLChild(BinTreePosition c);
// 判斷是否為右孩子(為使代碼描述簡潔)
public boolean isRChild();
// 判斷是否有右孩子(為使代碼描述簡潔)
public boolean hasRChild();
// 返回當前節點的右孩子
public BinTreePosition getRChild();
// 設置當前節點的右孩子(注意:this.rChild和c.parent都不一定為空)
public void setRChild(BinTreePosition c);
// 返回當前節點后代元素的數目
public int getSize();
// 在孩子發生變化后,更新當前節點及其祖先的規模
public void updateSize();
// 返回當前節點的高度
public int getHeight();
// 在孩子發生變化后,更新當前節點及其祖先的高度
public void updateHeight();
// 返回當前節點的深度
public int getDepth();
// 在父親發生變化后,更新當前節點及其后代的深度
public void updateDepth();
// 按照中序遍歷的次序,找到當前節點的直接前驅
public BinTreePosition getPrev();
// 按照中序遍歷的次序,找到當前節點的直接后繼
public BinTreePosition getSucc();
// 斷絕當前節點與其父親的父子關系
// 返回當前節點
public BinTreePosition secede();
// 將節點c作為當前節點的左孩子
public BinTreePosition attachL(BinTreePosition c);
// 將節點c作為當前節點的右孩子
public BinTreePosition attachR(BinTreePosition c);
// 前序遍歷
public Iterator elementsPreorder();
// 中序遍歷
public Iterator elementsInorder();
// 后序遍歷
public Iterator elementsPostorder();
// 層次遍歷
public Iterator elementsLevelorder();
}
基于鏈表節點實現二叉樹節點:BinTreeNode
package dsa.BinTree;
/*
* 基于鏈表節點實現二叉樹節點
*/
import dsa.Iterator.Iterator;
import dsa.List.List;
import dsa.List.List_DLNode;
import dsa.Queue.Queue_List;
public class BinTreeNode implements BinTreePosition {
protected Object element;// 該節點中存放的對象
protected BinTreePosition parent;// 父親
protected BinTreePosition lChild;// 左孩子
protected BinTreePosition rChild;// 右孩子
protected int size;// 后代數目
protected int height;// 高度
protected int depth;// 深度
/**************************** 構造方法 ****************************/
public BinTreeNode() {
this(null, null, true, null, null);
}
public BinTreeNode(Object e, // 節點內容
BinTreePosition p, // 父節點
boolean asLChild, // 是否作為父節點的左孩子
BinTreePosition l, // 左孩子
BinTreePosition r)// 右孩子
{
size = 1;
height = depth = 0;
parent = lChild = rChild = null;// 初始化
element = e;// 存放的對象
// 建立與父親的關系
if (null != p)
if (asLChild)
p.attachL(this);
else
p.attachR(this);
// 建立與孩子的關系
if (null != l)
attachL(l);
if (null != r)
attachR(r);
}
/**************************** Position接口方法 ********************************/
// 返回當前節點中存放的對象
public Object getElem() {
return element;
}
// 將對象obj存入當前節點,并返回此前的內容
public Object setElem(Object obj) {
Object bak = element;
element = obj;
return bak;
}
/**************************** BinTreePosition接口方法 *************************/
// 判斷是否有父親(為使代碼描述簡潔)
public boolean hasParent() {
return null != parent;
}
// 返回當前節點的父節點
public BinTreePosition getParent() {
return parent;
}
// 設置當前節點的父節點
public void setParent(BinTreePosition p) {
parent = p;
}
// 判斷是否為葉子
public boolean isLeaf() {
return !hasLChild() && !hasRChild();
}
// 判斷是否為左孩子(為使代碼描述簡潔)
// 若當前節點有父親,而且是左孩子,則返回true;否則,返回false
public boolean isLChild() {
return (hasParent() && this == getParent().getLChild()) ? true : false;
}
// 判斷是否有左孩子(為使代碼描述簡潔)
public boolean hasLChild() {
return null != lChild;
}
// 返回當前節點的左孩子
public BinTreePosition getLChild() {
return lChild;
}
// 設置當前節點的左孩子(注意:this.lChild和c.parent都不一定為空)
public void setLChild(BinTreePosition c) {
lChild = c;
}
// 判斷是否為右孩子(為使代碼描述簡潔)
// 若當前節點有父親,而且是右孩子,則返回true;否則,返回false
public boolean isRChild() {
return (hasParent() && this == getParent().getRChild()) ? true : false;
}
// 判斷是否有右孩子(為使代碼描述簡潔)
public boolean hasRChild() {
return null != rChild;
}
// 返回當前節點的右孩子
public BinTreePosition getRChild() {
return rChild;
}
// 設置當前節點的右孩子(注意:this.rChild和c.parent都不一定為空)
public void setRChild(BinTreePosition c) {
rChild = c;
}
// 返回當前節點后代元素的數目
public int getSize() {
return size;
}
// 在孩子發生變化后,更新當前節點及其祖先的規模
public void updateSize() {
size = 1;// 當前節點
if (hasLChild())
size += getLChild().getSize();// 左子樹的規模
if (hasRChild())
size += getRChild().getSize();// 右子樹的規模
if (hasParent())
getParent().updateSize();// 遞歸更新各個真祖先的規模記錄
}
// 返回當前節點的高度
public int getHeight() {
return height;
}
// 在孩子發生變化后,更新當前節點及其祖先的高度
public void updateHeight() {
height = 0;// 先假設沒有左、右孩子
if (hasLChild())
height = Math.max(height, 1 + getLChild().getHeight());// 左孩子
if (hasRChild())
height = Math.max(height, 1 + getRChild().getHeight());// 右孩子
if (hasParent())
getParent().updateHeight();// 遞歸更新各個真祖先的高度記錄
}
// 返回當前節點的深度
public int getDepth() {
return depth;
}
// 在父親發生變化后,更新當前節點及其后代的深度
public void updateDepth() {
depth = hasParent() ? 1 + getParent().getDepth() : 0;// 當前節點
if (hasLChild())
getLChild().updateDepth();// 沿孩子引用逐層向下,
if (hasRChild())
getRChild().updateDepth();// 遞歸地更新所有后代的深度記錄
}
// 按照中序遍歷的次序,找到當前節點的直接前驅
public BinTreePosition getPrev() {
// 若左子樹非空,則其中的最大者即為當前節點的直接前驅
if (hasLChild())
return findMaxDescendant(getLChild());
// 至此,當前節點沒有左孩子
if (isRChild())
return getParent();// 若當前節點是右孩子,則父親即為其直接前驅
// 至此,當前節點沒有左孩子,而且是左孩子
BinTreePosition v = this;// 從當前節點出發
while (v.isLChild())
v = v.getParent();// 沿左孩子鏈一直上升
// 至此,v或者沒有父親,或者是父親的右孩子
return v.getParent();
}
// 按照中序遍歷的次序,找到當前節點的直接后繼
public BinTreePosition getSucc() {
// 若右子樹非空,則其中的最小者即為當前節點的直接后繼
if (hasRChild())
return findMinDescendant(getRChild());
// 至此,當前節點沒有右孩子
if (isLChild())
return getParent();// 若當前節點是左孩子,則父親即為其直接后繼
// 至此,當前節點沒有右孩子,而且是右孩子
BinTreePosition v = this;// 從當前節點出發
while (v.isRChild())
v = v.getParent();// 沿右孩子鏈一直上升
// 至此,v或者沒有父親,或者是父親的左孩子
return v.getParent();
}
// 斷絕當前節點與其父親的父子關系
// 返回當前節點
public BinTreePosition secede() {
if (null != parent) {
if (isLChild())
parent.setLChild(null);// 切斷父親指向當前節點的引用
else
parent.setRChild(null);
parent.updateSize();// 更新當前節點及其祖先的規模
parent.updateHeight();// 更新當前節點及其祖先的高度
parent = null;// 切斷當前節點指向原父親的引用
updateDepth();// 更新節點及其后代節點的深度
}
return this;// 返回當前節點
}
// 將節點c作為當前節點的左孩子
public BinTreePosition attachL(BinTreePosition c) {
if (hasLChild())
getLChild().secede();// 摘除當前節點原先的左孩子
if (null != c) {
c.secede();// c脫離原父親
lChild = c;
c.setParent(this);// 確立新的父子關系
updateSize();// 更新當前節點及其祖先的規模
updateHeight();// 更新當前節點及其祖先的高度
c.updateDepth();// 更新c及其后代節點的深度
}
return this;
}
// 將節點c作為當前節點的右孩子
public BinTreePosition attachR(BinTreePosition c) {
if (hasRChild())
getRChild().secede();// 摘除當前節點原先的右孩子
if (null != c) {
c.secede();// c脫離原父親
rChild = c;
c.setParent(this);// 確立新的父子關系
updateSize();// 更新當前節點及其祖先的規模
updateHeight();// 更新當前節點及其祖先的高度
c.updateDepth();// 更新c及其后代節點的深度
}
return this;
}
// 前序遍歷
public Iterator elementsPreorder() {
List list = new List_DLNode();
preorder(list, this);
return list.elements();
}
// 中序遍歷
public Iterator elementsInorder() {
List list = new List_DLNode();
inorder(list, this);
return list.elements();
}
// 后序遍歷
public Iterator elementsPostorder() {
List list = new List_DLNode();
postorder(list, this);
return list.elements();
}
// 層次遍歷
public Iterator elementsLevelorder() {
List list = new List_DLNode();
levelorder(list, this);
return list.elements();
}
/**************************** 輔助方法 ****************************/
// 在v的后代中,找出最小者
protected static BinTreePosition findMinDescendant(BinTreePosition v) {
if (null != v)
while (v.hasLChild())
v = v.getLChild();// 從v出發,沿左孩子鏈一直下降
// 至此,v或者為空,或者沒有左孩子
return v;
}
// 在v的后代中,找出最大者
protected static BinTreePosition findMaxDescendant(BinTreePosition v) {
if (null != v)
while (v.hasRChild())
v = v.getRChild();// 從v出發,沿右孩子鏈一直下降
// 至此,v或者為空,或者沒有右孩子
return v;
}
// 前序遍歷以v為根節的(子)樹
protected static void preorder(List list, BinTreePosition v) {
if (null == v)
return;// 遞歸基:空樹
list.insertLast(v);// 訪問v
preorder(list, v.getLChild());// 遍歷左子樹
preorder(list, v.getRChild());// 遍歷右子樹
}
// 中序遍歷以v為根節的(子)樹
protected static void inorder(List list, BinTreePosition v) {
if (null == v)
return;// 遞歸基:空樹
inorder(list, v.getLChild());// 遍歷左子樹
list.insertLast(v);// 訪問v
inorder(list, v.getRChild());// 遍歷右子樹
}
// 后序遍歷以v為根節的(子)樹
protected static void postorder(List list, BinTreePosition v) {
if (null == v)
return;// 遞歸基:空樹
postorder(list, v.getLChild());// 遍歷左子樹
postorder(list, v.getRChild());// 遍歷右子樹
list.insertLast(v);// 訪問v
}
// 層次遍歷以v為根節的(子)樹
protected static void levelorder(List list, BinTreePosition v) {
Queue_List Q = new Queue_List();// 空隊
Q.enqueue(v);// 根節點入隊
while (!Q.isEmpty()) {
BinTreePosition u = (BinTreePosition) Q.dequeue();// 出隊
list.insertLast(u);// 訪問v
if (u.hasLChild())
Q.enqueue(u.getLChild());
if (u.hasRChild())
Q.enqueue(u.getRChild());
}
}
}
二叉樹接口:BinTree
package dsa.BinTree;
import dsa.Iterator.Iterator;
/*
* 二叉樹接口
*/
public interface BinTree {
// 返回樹根
public BinTreePosition getRoot();
// 判斷是否樹空
public boolean isEmpty();
// 返回樹的規模(即樹根的后代數目)
public int getSize();
// 返回樹(根)的高度
public int getHeight();
// 前序遍歷
public Iterator elementsPreorder();
// 中序遍歷
public Iterator elementsInorder();
// 后序遍歷
public Iterator elementsPostorder();
// 層次遍歷
public Iterator elementsLevelorder();
}
基于鏈表實現二叉樹:BinTree_LinkedList
package dsa.BinTree;
import dsa.Iterator.Iterator;
/*
* 基于鏈表實現二叉樹
*/
public class BinTree_LinkedList implements BinTree {
protected BinTreePosition root;// 根節點
/**************************** 構造函數 ****************************/
public BinTree_LinkedList() {
this(null);
}
public BinTree_LinkedList(BinTreePosition r) {
root = r;
}
/**************************** BinaryTree接口方法 ****************************/
// 返回樹根
public BinTreePosition getRoot() {
return root;
}
// 判斷是否樹空
public boolean isEmpty() {
return null == root;
}
// 返回樹的規模(即樹根的后代數目)
public int getSize() {
return isEmpty() ? 0 : root.getSize();
}
// 返回樹(根)的高度
public int getHeight() {
return isEmpty() ? -1 : root.getHeight();
}
// 前序遍歷
public Iterator elementsPreorder() {
return root.elementsPreorder();
}
// 中序遍歷
public Iterator elementsInorder() {
return root.elementsInorder();
}
// 后序遍歷
public Iterator elementsPostorder() {
return root.elementsPostorder();
}
// 層次遍歷
public Iterator elementsLevelorder() {
return root.elementsLevelorder();
}
}