樹
樹是數據結構內很重要的一種結構。不過我們此處不深究,僅討論二叉樹,線索二叉樹,哈夫曼樹(最優樹)。
二叉樹
定義:(1)空樹;(2)只有一個根節點;(3)有左右兩個子樹,並且子樹也是一顆二叉樹(如圖)。
性質:
1.第 i 層上最多有
個節點.
2.深度為k的樹最多有 -1個節點,我們稱之為滿二叉樹,滿二叉樹在底層從右向左減少n個節點,此時稱為完全二叉樹。
3.度為0的結點的個數為度為2的節點個數加一, .
4.n個節點的完全二叉樹的深度為[ ]+1。注釋:[x]表示不超過x的最大整數,也就是向下取整。
5.節點序號為 i ,在子節點存在的情況下,節點的序號為2i和2i+1
遍歷方式
1.先序遍歷:先訪問根節點,再訪問左子樹然后是右子樹。
2.中序遍歷:先訪問左子樹,再訪問根節點然后再右子樹。
3.后序遍歷:先訪問左子樹,再訪問右子樹然后是根節點。
先序中序,后序中序能唯一的確定唯一一個二叉樹
還有其他三種遍歷方式 : 只需將以上三種方式的左和右換位置即可
存儲結構
1.順序存儲結構(由於太浪費內存,所以說一般不用)
2.鏈式存儲結構:利用二叉鏈表來組成的二叉樹(有n個節點的二叉樹,含有n+1個空鏈域)。
樹
#include<stdio.h>
typedef struct Lnode{
char data;
struct Lnode *left,*right;
}Lnode,*linkLnode;
//創建樹
void setleaf(linkLnode &L){
char data;
scanf("%c",&data);
if(data=='#'){
L=NULL;
return;
}
Lnode *w=new Lnode;
L=w;
L->data=data;
setleaf(L->left);
setleaf(L->right);
}
//先序遍歷
void xian(linkLnode L){
if(L==NULL)
return;
else
printf("%c\t",L->data);
xian(L->left);
xian(L->right);
}
//中序遍歷
void zhong(linkLnode L){
if(L==NULL)
return;
zhong(L->left);
printf("%c\t",L->data);
zhong(L->right);
}
//后序遍歷
void hou(linkLnode L){
if(L==NULL)
return;
hou(L->left);
hou(L->right);
printf("%c\t",L->data);
}
//度為0的節點個數
int ling(linkLnode L){
int i;
if(L!=NULL)
i=ling(L->left)+ling(L->right);
else
return 0;
if(L->left==NULL && L->right==NULL)
i=i+1;
return i;
}
//度為1的節點個數
int yi(linkLnode L){
int i;
if(L!=NULL)
i=yi(L->left)+yi(L->right);
else
return 0;
if((L->left==NULL && L->right!=NULL) ||(L->left!=NULL && L->right==NULL))
i=i+1;
return i;
}
//從數的最左邊,最底層,開始往高處,往右側檢測
//度為2的節點個數
int er(linkLnode L){
int i;
if(L!=NULL)
i=er(L->left)+er(L->right);
else
return 0;
if(L->left!=NULL && L->right!=NULL)
i=i+1;
return i;
}
//計算樹的深度
int deep(linkLnode L){
if(L==NULL)
return 0;
if( deep(L->left) > deep(L->right) )
return deep(L->left)+1;
else
return deep(L->right)+1;
}
void main(){
linkLnode L;
L=new Lnode;
L->left=L->right=NULL;
printf("請以先序遍歷的方式輸入(#代表此位置無值)\n");
setleaf(L);
printf("創建成功\n");
printf("先序遍歷:\n");
xian(L);
printf("\n中序遍歷:\n");
zhong(L);
printf("\n后序遍歷:\n");
hou(L);
printf("\n度為0的節點個數為:%d\n",ling(L));
printf("\n度為1的節點個數為:%d\n",yi(L));
printf("\n度為2的節點個數為:%d\n",er(L));
printf("\n樹的深度為:%d\n",deep(L));
}
線索二叉樹
線索二叉樹與二叉樹大致一樣,只不過是將空鏈域充分利用起來,便於查找每個節點的前驅和后繼。
代碼僅是中序線索二叉樹
#include<stdio.h>
typedef struct Lnode{
char data;
struct Lnode *left,*right;
int zuo,you;
}Lnode,*linkLnode;
//創建樹
void setleaf(linkLnode &L){
char data;
scanf("%c",&data);
if(data=='#'){
L=NULL;
return;
}
Lnode *w=new Lnode;
L=w;
L->data=data;
setleaf(L->left);
setleaf(L->right);
}
//中序遍歷
void zhong(linkLnode &L){
if(L==NULL)
return;
zhong(L->left);
zhong(L->right);
if(L->left==NULL)
L->zuo=0;
else
L->zuo=1;
if(L->right==NULL)
L->you=0;
else
L->you=1;
}
//設置線索
void guo(linkLnode &L){
Lnode *m,*n;
m=L->left;n=L->right;
printf("此時進入到了%c\n",L->data); //輸出一些過程,便於觀察
if(L->zuo!=0){
while(m->you==1)
m=m->right;
m->right=L;
printf("m的指向值為%c,m的右側為%c\n",m->data,m->right->data);
guo(L->left);
}
printf("此時返回到了%c\n",L->data);
if(L->you!=0){
while(n->zuo==1)
n=n->left;
n->left=L;
printf("n的指向值為%c,n的左側為%c\n",n->data,n->left->data);
guo(L->right);
}
printf("准備返回上一層\n");
return;
}
void zhongappear(linkLnode L){
Lnode *m;
m=L;
while(1){
while(m->zuo==1)
m=m->left;
if(m==NULL)
break;
printf("%c\t",m->data);
if(m->you==0){
printf("%c\t",m->right->data);
m=m->right->right;}
else
m=m->right;
if(m==NULL){
printf("圓滿結束\n");
break;}
}
}
void main(){
linkLnode L,S;
L=new Lnode;
S=new Lnode;
S->you=0;S->zuo=1;
S->left=L;
L->left=L->right=NULL;
printf("請以先序遍歷的方式輸入(#代表此位置無值)\n");
setleaf(L);
printf("創建成功\n");
zhong(L);
guo(L);
printf("\n中序遍歷結果:\n");
zhongappear(L);
}
哈夫曼樹
又名最優樹,就是指帶權路徑最小的數,也是二叉樹的一種,僅包含度為0或者2的節點。
構造方法
取權值最小的兩個節點,作為額外一個空節點的左右兩個孩子,然后取第三小的權值節點,與這顆樹組成新樹,以此類推,構成一顆哈夫曼樹。權值越大的節點離根節點越近。
森林
一棵樹為樹,兩棵樹及以上則稱為森林
樹
前面介紹的僅是二叉樹,一個節點只能有兩個孩子,對於樹的話則是一個節點可以有無數個孩子
存儲結構
1.雙親表示法:節點存儲在線性表中,每個節點除了存數data外,還存儲父親的下標,這種方法查找節點的父親很容易,查找孩子的話則不簡便。
2.孩子表示法:用的是一個順序表,然后鏈接許多鏈表構成分,連表內包含的是其孩子。
3.孩子兄弟表示法:此方法等於說是將樹化為一顆二叉樹,孩子在左,兄弟在右,處理方法將於二叉樹類似了,頗為常用(根節點的右孩子必為空)。
遍歷
1.先根遍歷:等於二叉樹的前序遍歷,應為根節點的右孩子為空。
2.后根遍歷:等於二叉樹的中序遍歷
森林
兩顆及以上的樹構成森林,森林也可以轉化為一棵樹,規則也是孩子在左,兄弟在右
遍歷
1.先序遍歷:等於二叉樹的先序
2.中序遍歷:等於二叉樹的中序