二叉樹是由n(n>=0)個結點組成的有序集合,集合或者爲空,或者是由一個根節點加上兩棵分別稱爲左子樹和右子樹的、互不相交的二叉樹組成。
二叉樹的五種形態:
node
樹的另外一種表示法:孩子兄弟表示法
A、每一個結點都有一個指向其第一個孩子的指針
B、每一個結點都有一個指向其第一個右兄弟的指針
孩子兄弟表示法的特性:
A、可以表示任意的樹形結構
B、每一個結點包含一個數據成員和兩個指針成員
C、孩子結點指針和兄弟結點指針構成樹杈算法
若是二叉樹中全部分支結點的度數都爲2,而且葉子結點都在統一層次上,則二叉樹爲滿二叉樹。
數組
若是一棵具備n個結點的高度爲k的二叉樹,樹的每一個結點都與高度爲k的滿二叉樹中編號爲1——n的結點一一對應,則二叉樹爲徹底二叉樹。
徹底二叉樹的特性:
A、一樣結點數的二叉樹,徹底二叉樹的高度最小
B、徹底二叉樹的葉子結點僅出如今最下邊兩層,而且最底層的葉子結點必定出如今左邊,倒數第二層的葉子結點必定出如今右邊。
C、徹底二叉樹中度爲1的結點只有左孩子。
數據結構
A、在二叉樹的第i層上最多有2^(i-1)個結點(i>=1)。
B、高度爲k的二叉樹,最多有2^k-1個結點(k>=0)。
C、對任何一棵二叉樹,若是其葉結點有n個,度爲2的非葉子結點有m個,則
n = m + 1。
D、具備n個結點的徹底二叉樹的高度爲logn + 1
E、對於有n個結點的徹底二叉樹,按層次對結點進行編號(從上到下,從左到右),對於任意編號爲i的結點:
ide
二叉樹結點包含四個固定的成員:結點的數據域、指向父結點的指針域、指向左子結點的指針域、指向右子結點的指針域。結點的數據域、指向父結點的指針域從TreeNode模板類繼承而來。
二叉樹結點的實現:函數
template <typename T> class BTreeNode:public TreeNode<T> { public: BTreeNode<T>* m_left;//左子結點 BTreeNode<T>* m_right;//右子結點 BTreeNode() { m_left = NULL; m_right = NULL; } //工廠方法,建立堆空間的結點 static BTreeNode<T>* NewNode() { BTreeNode<T>* ret = new BTreeNode<T>(); if(ret != NULL) { //堆空間的結點標識爲true ret->m_flag = true; } return ret; } };
A、基於數據元素的查找
定義基於數據元素查找的函數
post
virtual BTreeNode<T>* find(BTreeNode<T>* node, const T& value)const { BTreeNode<T>* ret = NULL; //若是根節點node if(node != NULL) { if(node->value == value) { ret = node; } else { //查找左子樹 if(ret == NULL) { ret = find(node->m_left, value); } //若是左子樹沒有找到,ret返回NULL,查找右子樹 if(ret == NULL) { ret = find(node->m_right, value); } } } return ret; } BTreeNode<T>* find(const T& value)const { return find(root(), value); }
B、基於結點的查找
定義基於結點查找的函數
this
virtual BTreeNode<T>* find(BTreeNode<T>* node, BTreeNode<T>* obj)const { BTreeNode<T>* ret = NULL; if(node != NULL) { //根節點node爲目標結點 if(node == obj) { ret = node; } else { //查找左子樹 if(ret == NULL) { ret = find(node->m_left, obj); } //若是左子樹沒有找到,ret返回NULL,繼續查找右子樹 if(ret == NULL) { ret = find(node->m_right, obj); } } } return ret; } BTreeNode<T>* find(TreeNode<T>* node)const { return find(root(), dynamic_cast<BTreeNode<T>*>(node)); }
根據插入的位置定義二叉樹結點的位置枚舉類型:設計
enum BTNodePos { Any, Left, Right };
在node結點的pos位置插入newnode結點的功能函數以下:
3d
virtual bool insert(BTreeNode<T>* newnode, BTreeNode<T>* node, BTNodePos pos) { bool ret = true; //插入的位置爲Any if(pos == Any) { //若是沒有左子結點,插入結點做爲左子結點 if(node->m_left == NULL) { node->m_left = newnode; } //若是有左子結點,沒有右子結點,插入結點做爲右子結點 else if(node->m_right == NULL) { node->m_right = newnode; } //若是node結點的左右子結點不爲空,插入失敗 else { ret = false; } } else if(pos == Left) { //若是指定插入左子結點,若是沒有左子結點,插入結點 if(node->m_left == NULL) { node->m_left = newnode; } else { ret = false; } } else if(pos == Right) { //若是指定插入右子結點,若是沒有右子結點,插入結點 if(node->m_right == NULL) { node->m_right = newnode; } else { ret = false; } } else { ret = false; } return ret; }
A、插入新結點
//插入結點,無位置要求 bool insert(TreeNode<T>* node) { return insert(dynamic_cast<BTreeNode<T>*>(node), Any); } //插入結點,指定插入位置 virtual bool insert(BTreeNode<T>* node, BTNodePos pos) { bool ret = true; if(node != NULL) { if(this->m_root == NULL) { node->parent = NULL; this->m_root = node; } else { BTreeNode<T>* np = find(node->parent); if(np != NULL) { ret = insert(dynamic_cast<BTreeNode<T>*>(node), np, pos); } else { THROW_EXCEPTION(InvalidParameterException, "Parameter invalid..."); } } } else { THROW_EXCEPTION(InvalidParameterException, "Parameter invalid..."); } return ret; }
B、插入數據元素
//插入數據,指定插入位置和父結點 virtual bool insert(const T& value, TreeNode<T>* parent, BTNodePos pos) { bool ret = true; BTreeNode<T>* node = BTreeNode<T>::NewNode(); if(node != NULL) { node->parent = parent; node->value = value; ret = insert(node, pos); if(!ret) { delete node; } } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } return ret; } //插入數據,指定父結點 bool insert(const T& value, TreeNode<T>* parent) { return insert(value, parent, Any); }
刪除功能函數的定義:
virtual void remove(BTreeNode<T>* node, BTree<T>* ret) { ret = new BTree<T>(); if(ret == NULL) { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } else { if(node == root()) { this->m_root = NULL; } else { BTreeNode<T>* parent = dynamic_cast<BTreeNode<T>*>(node->parent); if(parent->m_left == node) { parent->m_left = NULL; } else if(parent->m_right == node) { parent->m_right = NULL; } node->parent = NULL; } ret->m_root = node; } }
A、基於數據元素值刪除
//根據數據元素刪除結點 SharedPointer< Tree<T> > remove(const T& value) { BTree<T>* ret = NULL; BTreeNode<T>* node = find(value); if(node == NULL) { THROW_EXCEPTION(InvalidParameterException, "No value..."); } else { remove(node, ret); } return ret; }
B、基於結點刪除
//根據結點刪除結點 SharedPointer< Tree<T> > remove(TreeNode<T>* node) { BTree<T>* ret = NULL; node = find(node); if(node != NULL) { remove(dynamic_cast<BTreeNode<T>*>(node), ret); } else { THROW_EXCEPTION(InvalidParameterException, "No node..."); } return ret; }
將二叉樹中全部在堆空間分配的結點銷燬。
清除node結點爲根節點的二叉樹的功能函數:
virtual void free(BTreeNode<T>* node) { if(node != NULL) { free(node->m_left); free(node->m_right); } //若是結點在堆空間分配 if(node->flag()) { delete node; } } //清空樹 void clear() { free(root()); this->m_root = NULL; }
A、樹中結點的數量
定義計算某個結點爲根結點的樹的結點的數量
int count(BTreeNode<T>* node) const { int ret = 0; if(node != NULL) { ret = count(node->m_left) + count(node->m_right) + 1; } return ret; } //樹的結點數目訪問函數 int count()const { return count(root()); }
B、樹的高度
獲取node結點爲根結點的二叉樹的高度的功能函數:
int height(BTreeNode<T>* node) const { int ret = 0; if(node != NULL) { int l = height(node->m_left); int r = height(node->m_right); ret = ((l > r)?l:r) + 1; } return ret; } //樹的高度訪問函數 int height()const { return height(root()); }
C、樹的度
獲取node爲根結點的二叉樹的度的功能函數:
int degree(BTreeNode<T>* node) const { int ret = 0; if(node != NULL) { //根結點的度數 ret = (!!node->m_left + !!node->m_right); //左子樹的度 if(ret < 2) { int l = degree(node->m_left); if(ret < l) { ret = l; } } //右子樹的度數 if(ret < 2) { int r = degree(node->m_left); if(ret < r) { ret = r; } } } return ret; } //樹的度訪問函數 int degree()const { return degree(root()); }
二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹中的全部結點,使得每一個結點被訪問依次,且僅被訪問一次。
根據遊標思想,提供一組遍歷的先關函數,按層次訪問二叉樹中的數據元素。
引入一個隊列,輔助遍歷二叉樹。
LinkedQueue<BTreeNode<T>*> m_queue;
層次遍歷的過程以下:
//將根結點壓入隊列 bool begin() { bool ret = (root() != NULL); if(ret) { //清空隊列 m_queue.clear(); //根節點加入隊列 m_queue.add(root()); } return ret; } //判斷隊列是否爲空 bool end() { return (m_queue.length() == 0); } //隊頭元素彈出,將隊頭元素的孩子壓入隊列中 bool next() { bool ret = (m_queue.length() > 0); if(ret) { BTreeNode<T>* node = m_queue.front(); m_queue.remove();//隊頭元素出隊 //將隊頭元素的子結點入隊 if(node->m_left != NULL) { m_queue.add(node->m_left); } if(node->m_right != NULL) { m_queue.add(node->m_right); } } return ret; } //訪問隊頭元素指向的數據元素 T current() { if(!end()) { return m_queue.front()->value; } else { THROW_EXCEPTION(InvalidOperationException, "No value at current Node..."); } }
定義克隆node結點爲根結點的二叉樹的功能函數:
BTreeNode<T>* clone(BTreeNode<T>* node) { BTreeNode<T> * ret = NULL; if(node != NULL) { ret = BTreeNode<T>::NewNode(); if(ret != NULL) { ret->value = node->value; //左子樹 ret->m_left = clone(node->m_left); //右子樹 ret->m_right = clone(node->m_right); //若是左子樹不爲空,設置左子樹的父結點 if(ret->m_left != NULL) { ret->m_left->parent = ret; } //若是右子樹不爲空,設置右子樹父結點 if(ret->m_right != NULL) { ret->m_right->parent = ret; } } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } return ret; } SharedPointer<BTreeNode<T>> clone()const { BTree<T>* ret = new BTree<T>(); if(ret != NULL) { ret->m_root = clone(root()); } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } return ret; }
判斷兩棵二叉樹中的數據元素是否對應相等
定義二叉樹相等比較的功能函數:
bool equal(BTreeNode<T>* l, BTreeNode<T>* r)const { bool ret = true; //二叉樹自比較 if(l == r) { ret = true; } //兩棵二叉樹都不爲空 else if(l != NULL && r != NULL) { ret = (l->value == r->value) && (equal(l->m_left, r->m_left)) && (l->m_right, r->m_right); } //有一棵二叉樹爲空,一棵二叉樹不爲空 else { ret = false; } return ret; } bool operator ==(const BTree<T>& tree)const { return equal(root(), tree.root()); } bool operator !=(const BTree<T>& tree)const { return !(*this == tree);//使用==比較 }
將當前二叉樹與參數btree二叉樹中對應的數據元素相加,返回一棵在堆空間建立的新的二叉樹。
二叉樹相加實例以下:
定義將兩棵二叉樹相加的功能函數:
BTreeNode<T>* add(BTreeNode<T>* l, BTreeNode<T>* r)const { BTreeNode<T>* ret = NULL; //二叉樹l爲空 if(l == NULL && r != NULL) { ret = clone(r); } //二叉樹r爲空 else if(l != NULL && r == NULL) { ret = clone(l); } //二叉樹l和二叉樹r不爲空 else if(l != NULL && r != NULL) { ret = BTreeNode<T>::NewNode(); if(ret != NULL) { //根節點數據元素相加 ret->value = l->value + r->value; //左子樹相加 ret->m_left = add(l->m_left, r->m_left); //右子樹相加 ret->m_right = add(l->m_right, r->m_right); //左子樹不爲空,設置左子樹的父結點爲當前結點 if(ret->m_left != NULL) { ret->m_left->parent = ret; } //右子樹不爲空,設置右子樹的父結點爲當前結點 if(ret->m_right != NULL) { ret->m_right->parent = ret; } } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } return ret; } SharedPointer<BTree<T>> add(const BTree<T>& other)const { BTree<T>* ret = new BTree<T>(); if(ret != NULL) { ret->m_root = add(root(), other.root()); } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memoty..."); } return ret; }
二叉樹有先序、中序、後序三種遍歷方式,三種遍歷方法的不一樣主要是取決於根節點的遍歷順序。
若是二叉樹爲空,則無操做,直接返回。
若是二叉樹非空,則執行如下操做:
A、訪問根結點;
B、先序遍歷左子樹;
C、先序遍歷右子樹。
先序遍歷實現代碼:
void preOrderTraversal(BTreeNode<T>* node, LinkedQueue<BTreeNode<T>*>& queue) { if(node != NULL) { queue.add(node); preOrderTraversal(node->m_left, queue); preOrderTraversal(node->m_right, queue); } }
先序遍歷二叉樹示例:
若是二叉樹爲空,則無操做,直接返回。
若是二叉樹非空,則執行如下操做:
A、中序遍歷左子樹;
B、訪問根結點;
C、中序遍歷右子樹。
中序遍歷實現代碼:
void inOrderTraversal(BTreeNode<T>* node, LinkedQueue<BTreeNode<T>*>& queue) { if(node != NULL) { inOrderTraversal(node->m_left, queue); queue.add(node); inOrderTraversal(node->m_right, queue); } }
中序遍歷二叉樹示例:
若是二叉樹爲空,則無操做,直接返回。
若是二叉樹非空,則執行如下操做:
A、後序遍歷左子樹;
B、後序遍歷右子樹;
C、訪問根結點。
後序遍歷實現代碼:
void postOrderTraversal(BTreeNode<T>* node, LinkedQueue<BTreeNode<T>*>& queue) { if(node != NULL) { postOrderTraversal(node->m_left, queue); postOrderTraversal(node->m_right, queue); queue.add(node); } }
後序遍歷二叉樹示例:
定義遍歷方式的枚舉類型:
enum BTTraversal { PreOder, InOder, PostOder };
根據參數order選擇遍歷的方式,返回數組保存了二叉樹遍歷結點
SharedPointer<Array<T>> traversal(BTTraversal order) { DynamicArray<T>* ret = NULL; LinkedQueue<BTreeNode<T>*> queue;//保存遍歷二叉樹的結點 switch (order) { case PreOder: preOrderTraversal(root(), queue); break; case InOder: inOrderTraversal(root(), queue); break; case PostOder: postOrderTraversal(root(), queue); break; default: THROW_EXCEPTION(InvalidParameterException, "Parameter invalid..."); break; } ret = new DynamicArray<T>(queue.length()); if(ret != NULL) { for(int i = 0; i < ret->length(); i++, queue.remove()) { ret->set(i, queue.front()->value); } } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } return ret; }
線索化二叉樹是將二叉樹轉換爲雙向鏈表的過程(將非線性的二叉樹轉換爲線性的鏈表)。
二叉樹的線索化可以反映某種二叉樹的遍歷次序(結點的前後訪問次序)。
線索化二叉樹的過程:
二叉樹線索化的實現:
經過某種遍歷方式遍歷二叉樹,根據遍歷次序將二叉樹結點依次存儲到輔助隊列中,最後將輔助隊列中保存的結點依次出隊並鏈接(鏈接時,原二叉樹結點的m_left指針做爲雙向鏈表結點的m_prev指針,指向結點的前驅;原二叉樹結點的m_right結點做爲雙向鏈表結點的m_next指針,指向結點的後繼),成爲雙向鏈表。
void traversal(BTTraversal order, LinkedQueue<BTreeNode<T>*>& queue) { switch (order) { case PreOrder: preOrderTraversal(root(), queue); break; case InOrder: inOrderTraversal(root(), queue); break; case PostOrder: postOrderTraversal(root(), queue); break; case LevelOrder: levelOrderTraversal(root(), queue); break; default: THROW_EXCEPTION(InvalidParameterException, "Parameter invalid..."); break; } }
增長層次遍歷方式LevelOrder到遍歷方式枚舉類型中。
enum BTTraversal { PreOrder,//先序遍歷 InOrder,//中序遍歷 PostOrder,//後序遍歷 LevelOrder//層次遍歷 };
層次遍歷算法:
A、將根結點入隊
B、訪問隊頭元素指向的二叉樹結點
C、將隊頭元素出隊,隊頭元素的孩子入隊
D、判斷隊列是否爲空,若是非空,繼續B;若是爲空,結束。
層次遍歷二叉樹的實例以下:
//層次遍歷 void levelOrderTraversal(BTreeNode<T>* node, LinkedQueue<BTreeNode<T>*>& queue) { if(node != NULL) { //輔助隊列 LinkedQueue<BTreeNode<T>*> temp; //根結點壓入隊列 temp.add(node); while(temp.length() > 0) { BTreeNode<T>* n = temp.front(); //若是左孩子不爲空,將左孩子結點入隊 if(n->m_left != NULL) { temp.add(n->m_left); } //若是右孩子不爲空,將右孩子結點入隊 if(n->m_right != NULL) { temp.add(n->m_right); } //將隊列的隊頭元素出隊 temp.remove(); //將隊列的隊頭元素入隊輸出隊列 queue.add(n); } } }
將隊列中的全部結點鏈接成爲一個線性的雙向鏈表
void connect(LinkedQueue<BTreeNode<T>*>& queue) { BTreeNode<T>* ret = NULL; if(queue.length() > 0) { //返回隊列的隊頭元素指向的結點做爲雙向鏈表的首結點 ret = queue.front(); //雙向鏈表的首結點的前驅設置爲空 ret->m_left = NULL; //建立一個遊標結點,指向隊列隊頭 BTreeNode<T>* slider = queue.front(); //將隊頭元素出隊 queue.remove(); while(queue.length() > 0) { //當前遊標結點的後繼指向隊頭元素 slider->m_right = queue.front(); //當前隊頭元素的前驅指向當前遊標結點 queue.front()->m_left = slider; //將當前遊標結點移動到隊頭元素 slider = queue.front(); //將當前隊頭元素出隊,繼續處理新的隊頭元素 queue.remove(); } //雙向鏈表的尾結點的後繼爲空 slider->m_right = NULL; } }
線索化二叉樹函數接口的設計:
BTreeNode<T>* thread(BTTraversal order)
A、根據參數order選擇線索化的方式(先序、中序、後序、層次)
B、返回值是線索化二叉樹後指向鏈表首結點的指針
C、線索化二叉樹後,原有的二叉樹被破壞,二叉樹的全部結點根據遍歷次序組建爲一個線性的雙向鏈表,對應的二叉樹應爲空。
線索化二叉樹的流程:
BTreeNode<T>* thread(BTTraversal order) { BTreeNode<T>* ret = NULL; LinkedQueue<BTreeNode<T>*>* queue; //遍歷二叉樹,並按遍歷次序將結點保存到隊列 traversal(order, queue); //鏈接隊列中的結點成爲雙向鏈表 ret = connect(queue); //將二叉樹的根節點置空 this->m_root = NULL; //將遊標遍歷的輔助隊列清空 m_queue.clear(); //返回雙向鏈表的首結點 return ret; }