tree
1,樹的類型定義
2,二叉樹的類型定義
3,二叉樹的存儲結構
4,二叉樹的遍歷
5,線索二叉樹
6,樹和森林的表示方法
7,樹和森林的遍歷
8,哈弗曼樹和哈弗曼編碼
一,樹的定義 ?1,數據對象D:D是具有相同特性的數據元素的集合 ?
2,數據關系R:若D為空集,則稱為空樹。
?否則: ?
(1)在D中存在唯一的稱為根的數據元素root。 ?
(2)當n>1時,其余節點可分為m(m>0)個互不相交的有限集T1,T2,....Tm,其中每 一棵子集本身又是一棵符合本定義的樹,稱為根root的子樹。
?3,基本操作: ?
查找類,插入類,刪除類。
查找類: ?Root(T) //求樹的根節點 ?
Value(T,cur_e) //求當前節點的元素值 ?
Parent(T,cur_e) //求當前節點的雙親節點 ?
LeftChild(T,cur_e) //求當前節點的最左孩子
?RightSibling(T,cur_e) //求當前節點的右兄弟
?TreeEmpty(T) //判定樹是否為空樹
?TreeDepth(T) //求樹的深度 ?
TraverseTree(T,Visit()) //遍歷
插入類: ?
InitTree(&T) //初始化置空樹 ?
CreateTree(&T,definition) //按定義構造樹 ?
Assign(T,cur_e,value) //給當前節點賦值 ?
InsertChild(&T,&p,i,c) //將以c為跟的樹插入為節點p的第i棵子樹
刪除類:
?ClearTree(&T) //將樹清空
?DestroyTree(&T) //銷毀樹的結構 ?
DeleteChild(&T,&p,i) //刪除節點p的第i棵子樹
?4,樹的分類 ?(1)有向樹: ???
1,有確定的根; ??
? 2,樹根和子樹根之間為有向關系。 ?
(2)有序樹: ???
1,樹中節點的各子樹之間的先后次序是有意義的,不能互換,否則就稱為另一棵樹了。子 樹之間存在確定的次序關系 ?
(3)無序樹: ???
1,樹中節點的各子樹之間的先后次序無意義,可以互換。子樹之間不存在確定的次序關系。 ?
5,線性結構和樹形結構的對比 ?
1,線性結構: ??????
第一個數據元素(無前驅) 最后一個元素(無后繼) 其他元素(一個前驅、一個后繼) ?
2,樹形結構: ?????
? 根節點(無前驅) 多個葉子節點(無后繼) 其他數據元素(一個前驅,多個后繼) ?
6,基本術語 ?
一,節點:數據元素+若干指向子樹的分支 ?
二,結點的度:分支的個數 ?
三,樹的度:樹中所有結點的度的最大值 ?
四,葉子結點(終端結點):度為零的結點 ?
五,分支結點(非終端結點):度大于零的結點 ?
六(從根到結點的)路徑:由從根到該結點所經分支和結點構成 ?
七,孩子結點、雙親結點、兄弟結點、堂兄弟結點、祖先結點、子孫結點。 ?
八,結點的層次:假設根結點的層次為1,依次加1 ?
九,樹的深度:樹中葉子結點所在的最大層次 ?
7,森林: ? 是m(m>=0)棵互不相交的樹的集合。 ? 任何一棵非空樹是一個二元組 Tree=(root,F),其中root被稱為根結點 F被稱為子樹森林 二,二叉樹的類型定義:二叉樹是樹的基礎,一般的樹可以轉化為二叉樹來處理。
?1,二叉樹的定義:二叉樹或為空樹,或是由一個根結點加上兩棵分別稱為左子樹和右子樹的,互不相交的二叉樹組成(即左,右子樹次序不能顛倒)。
?2,二叉樹的五種基本形態: ? 一,空樹?? 二,只含根結點? 三,左右子樹均不為空樹? 四,右子樹為空樹? 五,左子樹為空樹
?3,二叉樹的主要基本操作: ??? 查找類,插入類,刪除類
? 一,查找類 ? Root(T);
Value(T,e);
Parent(T,e);
LeftChild(T,e);
RightChild(T,e);
LeftSibling(T,e);
RightSibling(T,e);
BiTreeEmpty(T);
BiTreeDepth(T);
PreOrderTraverse(T,Visit());
InOrderTraverse(T,Visit());
PostOrderTraverse(T,Visit());?
LevelOrderTraverse(T,Visit()); ?
二,插入類 ?
InitBiTree(&T);
Assign(T,&e,value);
CreateBiTree(&T,definition);
InsertChiild(T,p,LR,c);
? 三,刪除類 ? clearBiTree(&T); DestroyBiTree(&T); DeleteChild(T,p,LR); ?
四,二叉樹的性質 ??
1,在二叉樹的第i層上至多有2的(i-1)次方 ??
2,在深度為k的二叉樹上至多有2的k次方減一個結點(k>=1) ??
3,對任何一棵二叉樹,若它含有n0個葉子結點、n2個度為2的結點,則比存在關系式:n0=n2+1 ?? 4,具有n個結點的完全二叉樹的深度為[log2 n]+1. ??
5,若對含n個結點的完全二叉樹從上到下且從左至右進行1至n的編號,則對完全二叉樹中任意為i的結點: ?
(1)若i=1,則該結點是二叉樹的根,無雙親,否則,編號為[i/2]的結點為其雙親結點; ? (2)若2i>n,則該結點無左孩子,否則,編號為2i的結點為其左孩子結點; ?
(3)若2i+1>n,則該結點無 右孩子結點,否則,編號為2i+1的結點為其右孩子的結點。 ? 五,兩類特殊形態的二叉樹: ??
1,滿二叉樹:指的是深度為k且含有2的k次方減一個結點的二叉樹 ?
? 2,完全二叉樹:樹中所含的n個結點和滿二叉樹中編號為1至n的結點一一對應。 三,二叉樹的存儲結構 ?
1,二叉樹的順序存儲表示(適合存儲完全二叉樹) ??
(1)即用一組地址連續的存儲單元集中從上至下,從左至右存儲完全二叉樹上的結點元素。也就是說,將完全二叉樹上編號為i的結點存放在數組下標為(i-1)的分量中。 ??
(2)若要存儲一棵一般的二叉樹,結點的存放應與完全二叉樹上的結點對照,存儲在數組的相應分量中。用“0”表示不存在該結點。可能會浪費很多存儲空間,單支樹就是一個極端情況。 ? 2,二叉樹的鏈式存儲表示 ?
3,C語言描述 ??
(1)順序 ?????
#define MAX_TREE_SIZE 100; ?????
typedef TElemType SqBiTree[MAX_TREE_SIZE]; ?????
SqBiTree bt; ??
(2)鏈式 ?????
二叉鏈表 ?????
typedef struct BiTNode{ ?????
??? TElemType data; ???????? struct BiTNode *lchild,*rchild; ????
? }BiTNode,*BiTree; ????
? 三叉鏈表 ????? typedef struct TriTNode{ ?
struct TriTNode *parent;//雙親指針 ?struct TriTNode *lchild;//左孩子指針 ?TElemType data; ?struct TriTNode *rchild;//右孩子指針 ?????
}TriTNode,*TriTree;
四,二叉樹的遍歷 ?
1,遍歷的概念??
2,先左后右的遍歷算法??
3,算法的遞歸描述??
4,遍歷算法應用舉例 ?
一,遍歷的概念:按照某種順序不重復地訪問二叉樹中的所有結點。此處的訪問可以是輸出、修改等操作,根據實際需要而定。(使得每個結點均被訪問一次,而且僅被訪問一次。) ?
二,二叉樹可以有兩種搜索路徑: ??
1,先上后下的按層次遍歷; ??
2,先左后右的遍歷 ???? 先序的遍歷算法,中序的遍歷算法,后序的遍歷算法。 ?
三,先序遍歷算法: ? 若二叉樹為空樹,則空操作;否則, ??
(1)訪問根結點; ?? (2)先序遍歷左子樹; ?? (3)先序遍歷 右子樹 ??? void Preorder(BiTree T) ??? { ???????
if(T)//如果樹不為空 ??????? { ???
visit(T->data);//輸出根結點 ???
Preorder(T->lchild);//先序遍歷左子樹 ???? ???
Preorder(T->rchild);//先序遍歷右子樹 ?
? } ??
?? } ??
四,中序遍歷算法: ?? 若二叉樹為空樹,則空操作;否則, ????
(1)中序遍歷左子樹; ????
(2)訪問根結點; ????
(3)中序遍歷右子樹。 ?????
void Inorder(BiTree T) ????
? { ???????? if(T)//如果樹不為空 ??
{ ???? Inorder(T->lchild);//中序遍歷左子樹 ?
??? visit(T->data);//輸出根結點 ?????
Inorder(T->rchild);//中序遍歷右子樹 ?
? } ????
?? }
?? 五,后序遍歷算法: ?? 若二叉樹為空樹,則空操作;否則, ??????
(1)后序遍歷左子樹; ??????
(2)后序遍歷右子樹; ??????
(3)訪問根結點。 ???????
void Pastorder(BiTree T) ?{ ???
if(T)//如果樹不為空 ??? {
??Pastorder(T->lchild);//后序遍歷左子樹 ??
Pastorder(T->rchild);//后序遍歷右子樹 ??
?visit(T->data);//輸出根結點; ??
?? }
?} ??
六,遍歷算法的應用舉例 ???
1,統計二叉樹中葉子結點的個數(先序遍歷) ???
2,求二叉樹的深度(后序遍歷) ???
3,簡歷二叉樹的存儲結構 ???
4,求二叉樹節點的個數。
1,int CountLeaf(BiTree T) ?{ ??
? if(!T) return 0; ???
else if((!T->lchild)&&(!T->rchild))
return 1; ??
? else{ ????
?? nl=CountLeaf(T->lchild); ??
???? nr=CountLeaf(T->rchild); ??
???? return nl+nr; ????
}
?}
2,int Depth(BiTree T) ? { ???
if(!T)
return 0; ???
else{ ?????
dl=Depth(T->lchild); ???
?? dr=Depth(T->rchild); ?????
dep=1+(dl>dr?dl:dr); ???
} ???
return dep; ??
}
3,status CreateBiTree(BiTree &T) ? { ??
?? scanf(&ch); ???
? if(ch=="")
T=null; ????
else{ ???????
if(!(T=(BiTree)malloc(sizeof(BiTNode))))?
exit(OVERFLOW); ?
T->data=ch; ?
CreateBiTree(T->lchild);//構造左子樹; ?
CreateBiTree(T->rchild);//構造右子樹; ?
??? } ????
return OK; ?
}
4,int NodeCount(BiTree T) ? {
???? if(!T) return 0; ?
??? else{
?nl=NodeCount(T->lchild);
?nr=NodeCount(T->rchild);
?return nl+nr+1; ???
? } ?
? } ??
七,由二叉樹的先序和中序序列建樹 ??
主要思想: ??? 1,先序序列中第一個為“根”,標出之; ??
? 2,在中序序列中標出“根”,并分出左右子樹; ???
3,在先序序列中標出左右子樹; ???
4,分別對左,右子樹重復以上步驟。 ??
八,由二叉樹的后序和中序序列建樹 ?? 主要思想: ???
1,后序序列中最后一個為“根”,標出之; ?
?? 2,在中序序列中標出“根”,并分出左,右子樹; ???
3,在后序序列中標出左,右子樹; ???
4,分別對左、右子樹重復以上步驟。 ?
? 九,由二叉樹的后序和先序序列不能唯一確定一棵二叉樹。 ??
十,在含有n個結點的二叉樹中,含有n+1個空指針。
五,線索二叉樹 ??
1什么是線索二叉樹??? 2,線索鏈表的遍歷算法?? 3,如何建立線索鏈表 ???
一,線索二叉樹是指需要借助含有n個結點的二叉樹中(n+1)個空指針域,作為線索,直接指向遍歷序列中“前驅”或“后繼”結點。 包含“線索”的存儲結構,稱為“線索鏈表” 與其相應的二叉樹,稱作“線索二叉樹” ? 對線索鏈表中結點的約定: ?在二叉鏈表的結點中增加兩個標志域,并做如下規定: ??
1,若該結點的左子樹不空,則Lchild域的指針指向其左子樹,且左標志域的值為“指針Link=0“否則,Lchild域的指針指向其”前驅“,且左標志的值為”線索Thread=1". ?
? 2,若該結點的右子樹不空,則rchild域的指針指向其右子樹,且右標志域的值為”指針Link=0“;否則,rchilid域的指針指向其”后繼“且右標志的值為”線索Thread=1“。 如此定義的二叉樹的存儲結構稱作”線索鏈表“。 ??
3,線索鏈表的類型描述: typedef enum{Link,Thread}PointerThr//Link==0;指針,Thread==1;線索 typedef structBiThrNod { ?? TElemType data; ?? struct BiThrNode *lchild,*rchild;//左右指針 ?? PointerThr LTag,RTag;//左右標志 }BiThrNode,*BiThrTree; ??
二,線索鏈表的遍歷算法: ?? for(p=firstNode(T);p;p=Succ(p)) visit(p); 對中序線索化鏈表的遍歷算法
1,中序遍歷的第一個結點 ?? 左子樹上處于”最左下“(沒有左子樹)的結點。
2,在中序線索化鏈表中結點的后繼 ?? 若無右子樹,則為后繼線索所指向的結點;否則為對其右子樹進行中序遍歷時訪問的第一個結點。
void InOrder(BiThrTree T,void (*Visist)(TElemType e)) { ????
p=T->lchild; //p指向根結點 ????
while(p!=T) //空樹或遍歷結束時,p==T ???? {
?while(p->LTag==Link) p=p->lchild;//第一個結點
??????? while(p->RTag==Thread&&p->rchild!=T) ?{
???? p=p->rchild; Visit(p->data);//訪問后繼結點
?} ?p=p->rchild; ?????
}
}
六,樹和森林 ?? 一,樹的三種存儲結構 ?? 1,雙親表示法??? 2,孩子鏈表表示法?? 3,樹的二叉鏈表(孩子-兄弟)存儲表示法 ?雙親表示法:
?1,定義:用一組地址連續的空間存儲樹的結點,同時在每個結點中附設一個指示器指示雙親結點在鏈表中的位置。(即:保持結點信息和雙親的下標)
?2,特點:求雙親簡便,求孩子麻煩。
?3,C語言的類型描述 ?? #define MAX_TREE_SIZE 100 ??
結點結構 ?? typedef struct PTNode{ ???? ElemType data; ???? int parent; ?? }PTNode; ??
樹結構 ?? typedef struct{ ?PTNode nodes[MAX_TREE_SIZE]; ?int r,n;//根結點的位置和結點個數 ??? }PTree; ?孩子鏈表表示法:
? 1,定義:把每個結點的孩子結點鏈成單鏈表。 ?
2,特點:求孩子方便,求雙親麻煩。
? 3,可能把孩子鏈表表示法和雙親表示法結合一起。data parent firstchild... ?
4,C語言的類型描述: ???
孩子結點結構: ??? typedef struct CTNode{ ?????? ?int child; ?struct CTNode *next; ??? }*ChildPtr; ???
雙親結點結構 ??? typedef struct{ ?ElemType data; ??????? ChildPtr firstchild;//孩子鏈的頭指針 ??? }CTBox; ???
樹結構: ??? typedef struct{ ?CTBox nodes[MAX_TREE_SIZE]; ?int n,r;//結點數和根結點的位置 ???? } ?
樹的二叉鏈表存儲表示法 ??
1,定義:鏈表中的結點的兩個鏈域分別指向該結點的第一個孩子結點和下一個兄弟結點。 ??
2,結點結構:firstchild data nextsibling ?
? 3,C語言的類型描述: ????? typedef struct CSNode{ ?TElemType data; ?struct CSNode *firstchild,*nextsibling; ????? }CSNode,*CSTree; ?
二,樹和二叉樹的轉換 ??
1,轉換規則 ????? (1)樹的根 <---> 二叉樹的根 ????? (2)樹的第一個孩子 <---> 左孩子 ????? (3)右兄弟 <---> 右孩子 ? 注:由樹轉換得到的二叉樹的右子樹永遠為空! ?
三,森林和二叉樹的轉換 ?? 1,規則: ????? (1)森林中第一棵樹的根變成二叉樹的根 ????? (2)森林中其他樹的根當成第一棵樹的根的兄弟 ????? (3)把森林中每棵樹都轉換為相應的二叉樹 ?? 注:由森林轉換得到的二叉樹的右子樹可能不為空!
七,樹和森林的遍歷 ?樹的遍歷 ?? 把樹看成兩個部分: ????
1,樹的根?? 2,根的子樹森林 樹的遍歷可有三條搜索路徑: 先根遍歷:若樹不空,則先訪問根結點,然后依次先根遍歷各棵子樹。 后根遍歷:若樹不空,則先依次后根遍歷各棵子樹,然后訪問根結點。 按層次遍歷:若樹不空,則自上而下自左至右訪問數中每個結點。
?森林的遍歷 ?? 森林由三部分構成: ???
? 1,森林中第一棵樹的根結點?
2,森林中第一棵樹的子樹森林;?
3,森林中其他樹構成的森林 先序遍歷:若森林不空,則訪問森林中第一棵樹的根結點; 先序遍歷森林中第一棵樹的子樹森林;先序遍歷森林中(除第一棵樹之外)其余樹構成的森林。 即:依次從左至右對森林中的每一顆樹進行先序遍歷。
后序遍歷:若森林不空,則后序遍歷森林中第一棵樹的子樹森林;訪問森林中第一棵樹的根結點;后序遍歷森林中(除第一棵樹之外)其余樹構成的森林。 即:依次從左至右對森林中的每一顆樹進行后序遍歷
八,哈弗曼樹和哈弗曼編碼 ?
1,幾個預備概念?
2 最優樹的定義?
3 如何構造最優樹??
4 前綴編碼 ??
1,路徑:樹中一個結點到另一個結點所經過的分支 ??
2,路徑長度:路徑上分支數目。 ??
3,樹的路徑長度:從根到每一個結點的路徑長度之和。(完全二叉樹是路徑長度最短的二叉樹) ?? 4,結點的路徑長度定義為:從根結點到該結點的路徑上的分支的數目。 ??
5,結點的帶權路徑長度定義為:結點的權值乘以該結點的路徑長度。 ??
6,樹的帶權路徑長度定義為:樹中所有葉子結點的帶權路徑長度之和WPL(T)=&Wk*Lk(對所有葉子結點) ??
7,最優二叉樹:在所有含n個葉子結點、并帶相同權值的m叉樹中,必存在一棵其帶權路徑長度取最小值的樹,稱為最優二叉樹。 ? 二,如何構造最優樹(哈弗曼樹) ? 問題:根據給定的n個權值{w1,w2,...,wn},構造一棵以這些權值為葉子的哈弗曼樹? (哈弗曼算法)以二叉樹為例: ?(1)用給定的n個權值構造n棵以各權值為根的二叉樹。
?(2)選取其根結點的權值為最小的兩顆二叉樹,分別作為左、右子樹構造一棵新的二叉樹,并且這課新的二叉樹根結點的權值為其左、右子樹根結點的權值之和;并刪去這兩棵二叉樹,同時加入新生成的二叉樹; ?
(3)重復第(2)步,直至生成一棵樹為止。 ?
三,前綴編碼 ? 指的是,任何一個字符的編碼都不是同一字符集中另一個字符的編碼的前綴。 ? 利用哈弗曼樹可以構造一種不等長的二進制編碼,并且構造所得的哈弗曼編碼是一種最優前綴編碼,即:使所傳電文的總長度最短。
? 四,按層次遍歷二叉樹 ? #define queue ? { ???
? bitree v[MAXSIZE]; ???
? int* front,rear;//對頭,隊尾指針 ?
}q ? void levelorder(bitree t) ? { ?
??? q.front=q.rear=0; ????
if(t!=NULL)
printf(" %d",t->data); //遍歷根結點 ????
q.v[q.rear]=t;? q.rear=q.rear+1; ???
? while(q.front<q.rear) ???? {
?s=q.v[q.front];
q.front=q.front; ? ?
if(s->lchild!=null) ?{ ????
printf(" %d",s->lchild->data); ??
?? q.v[q.rear]=s->lchild;
q.rear=q.rear+1; ?}
?if(s->rchild!=null) ?{ ??
? printf(" %d",s->rchild->data); ???
q.v[q.rear]=s->rchild; ?? ???
q.rear=q.rear+1; ?} ???
? } ??
}
轉載于:https://www.cnblogs.com/WFLing/p/3359801.html
總結
- 上一篇: TP-Link TL-WDR5300 无
- 下一篇: [20131014] 出现错误,显示事务