1.引入的概念:
結點:指樹中的一個元素;
結點的度:指結點擁有的子樹的個數,二叉樹的度不大於2;
樹的度:指樹中的最大結點度數;
葉子:度爲0的結點,也稱爲終端結點;
高度:葉子節點的高度爲1,根節點高度最高;
層:根在第一層,以此類推;
2.樹的存儲結構:
(1)雙親表示法:以雙親作爲索引的關鍵詞的一種存儲方式
每個結點只有一個雙親,所以選擇順序存儲佔主要以一組連續空間存儲樹的結點,同時在每個結點中,附設一個指示其雙親結點位置的指針域
(2)孩子表示法:類似於單鏈表的存儲方式,指針域內的元素個數有子樹個數決定。
(3)孩子兄弟表示法:任意一棵樹,他的結點的第一個孩子如果存在就是唯一結點,他的右兄弟如果存在,也是唯一的,因此,可以設置兩個指針,分別指向該結點的第一個孩子和該結點的右兄弟
3.特殊的二叉樹
二叉樹的定義:由一個結點和兩顆互不相交、分別稱爲這個根的左子樹和右子樹的二叉樹構成(遞歸定義)
(1)二叉樹的性質:
1:二叉樹的第i層上至多有2^(i-1)個結點
2:深度爲k的二叉樹,至多有2^k-1個結點
3:在一棵二叉樹中,如果葉子結點數爲n0,度爲2的結點數爲n2,則有: n0=n2+1
4:具有n個結點的完全二叉樹的深度爲 log2n +1
5:對一棵具有n個結點的完全二叉樹中從1開始按層序編號,則對於任意的序號爲i(1≤i≤n)的結點有:
(1)如果i>1, 則結點i的雙親結點的序號爲 i/2;如果i=1, 則結點i是根結點,無雙親
(2)如果2i≤n, 則結點i的左孩子的序號爲2i; 如果2i>n,則結點i無左孩子。
(3)如果2i+1≤n, 則結點i的右孩子的序號爲2i+1;如果2i+1>n,則結點 i無右孩子。
(2)滿二叉樹:葉子節點一定要在最後一層,並且所有非葉子節點都存在左孩子和右孩子;
(3)最特別的二叉樹:完全二叉樹:從左到右、從上到下構建的二叉樹;
二叉樹的遍歷(遞歸):
1:先序遍歷:根->左子樹->右子樹(先序)
2:中序遍歷:左子樹->根->右子樹(中序)
3:後序遍歷:左子樹->右子樹->根(後序)
這三種遍歷方法只是訪問結點的時機不同,訪問結點的路徑都是一樣的,時間和空間複雜度皆爲O(n)
4:層序遍歷:逐層訪問
代碼實現:
#include <iostream>
using namespace std;
template<typename T>
struct BiNode
{
T data;
BiNode<T>*lchild;
BiNode<T>*rchild;
};
template<typename T>
class BiTree
{
private:
BiNode<T>*root;
BiNode<T>*Creat();
void Release(BiNode<T>*bt);
void PreOrder(BiNode<T>*bt);
void InOrder(BiNode<T>*bt);
void PostOrder(BiNode<T>*bt);
public:
BiTree(){
root = Creat();
}
~BiTree(){
Release(root);
}
void PreOrder(){
PreOrder(root);
}
void InOrder(){
InOrder(root);
}
void PostOrder(){
PostOrder(root);
}
};
template<typename T>
BiNode<T>*BiTree<T>::Creat()//創建一個二叉樹
{
BiNode<T>*bt;
char ch;
cin>>ch;
if(ch=='#')
bt = NULL;
else
{
bt = new BiNode<T>;
bt->data = ch;
bt->lchild = Creat();
bt->rchild = Creat();
}
return bt;
}
template<typename T>
void BiTree<T>::PreOrder(BiNode<T>*bt)//前序遍歷
{
if(bt==NULL)
return;
else
{
cout<<bt->data;
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}
template<typename T>
void BiTree<T>::InOrder(BiNode<T>*bt)//中序遍歷
{
if(bt==NULL)
return;
else
{
InOrder(bt->lchild);
cout<<bt->data;
InOrder(bt->rchild);
}
}
template<typename T>
void BiTree<T>::PostOrder(BiNode<T>*bt)//後序遍歷
{
if(bt==NULL)
return;
else
{
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout<<bt->data;
}
}
前序遍歷的非遞歸實現:
1.棧s初始化(空棧);
2.循環直到root爲空且棧s爲空
2.1 當root不空時循環
2.1.1 輸出root->data;
2.1.2 將指針root的值保存到棧中;
2.1.3 繼續遍歷root的左子樹(root=root->lchild)
2.2 如果棧s不空,則
2.2.1 將棧頂元素彈出至root(root=s.pop());
2.2.2 準備遍歷root的右子樹(root=root->rchild);
template <class T>
void BiTree::PreOrder(BiNode<T> *root) {
SeqStack<BiNode<T> *> s;
while (root!=NULL | | !s.empty()) {
while (root!= NULL) {
cout<<root->data;
s.push(root);
root=root->lchild;
}
if (!s.empty()) {
root=s.pop();
root=root->rchild;
}
}
}
中序遍歷的非遞歸實現
1.棧s初始化(空棧);
2.循環直到root爲空且棧s爲空
2.1 當root不空時循環
2.1.1 將指針root的值保存到棧中;
2.1.2 繼續遍歷root的左子樹(root=root->lchild)
2.2 如果棧s不空,則
2.2.1 將棧頂元素彈出至root(root=s.pop());
2.2.2 輸出root->data;
2.2.3 準備遍歷root的右子樹(root=root->rchild);
template <class T>
void BiTree::InOrderwithoutD (BiNode<T> *root)
{
stack< BiNode<T> * > aStack;
}
while(!aStack.empty()||root) {
while(root){
aStack.push(root);
root=root->lchild;
}
if(!aStack.empty()){
root=aStack.top();
aStack.pop();
cout<<root->data;
root=root->rchild;
}
}
}
後序遍歷的非遞歸實現:
1.定義一個棧;從根節點出發開始遍歷,p=root,如果,root==NULL, 不進行遍歷;
2.無條件進行下面的工作
(1)如果指針不空,指針打上left標記,並將指針進棧,執行(2);否則,執行(3)
(2)p->lchild,重複(1)
(3)棧頂元素出棧P
(4)查看P的標誌,如果標誌爲right,進行下面的工作,否則,執行(5)
a.訪問當前節點P
b.如果棧空 ,算法結束;
c.否則,棧頂元素出棧,轉(4)
(5)修改P的標誌,讓P重新入棧,p=P->rchild,執行2
#include <stack>
Using namespace std;
template<class T>
void BiTree<T>::PostOrderWithoutRecusion(BiTreeNode<T>* root){
StackElement<T> element;
stack<StackElement<T > > aStack;//棧申明
BiTreeNode<T>* pointer;
if(root==NULL)
return;//空樹即返回
}
else
pointer=root;
while(true){
while(pointer!=NULL){//進入左子樹
element.pointer=pointer;
element.tag=Left; //沿左子樹方向向下周遊
aStack.push(element);
pointer=pointer->lchild;
}
ement=aStack.pop();
pointer=element.pointer;
while(element.tag==Right){
cout<<pointer->data;
if(aStack.empty()) return;
else{
element=aStack.pop();
pointer=element.pointer;
}//end else
} //endwhile
element.tag=Right;
aStack.push(element);
pointer=pointer->rchild();
}//end while
(5)Huffman編碼
Huffman是一種前綴編碼;Huffman編碼是建立在Huffman樹的基礎上進行的,因此爲了進行Huffman編碼,必須先構建Huffman樹;樹的路徑長度是每個葉節點到根節點的路徑之和;帶權路徑長度是(每個葉節點的路徑長度*wi)之和;Huffman樹是最小帶權路徑長度的二叉樹;
構造Huffman樹的過程:
(1)將各個節點按照權重從小到大排序;
(2)取最小權重的兩個節點,並新建一個父節點作爲這兩個節點的雙親,雙親節點的權重爲子節點權重之和,再將此父節點放入原來的隊列;
(3)重複(2)的步驟,直到隊列中只有一個節點,此節點爲根節點;