樹狀結構就像以下圖片的樣子,是一種有階層架構的非線性資料集合
一般的樹狀結構擁有以下的特徵:
節點:代表某項資料
根結點:沒有父節點的節點,一顆樹也只能有一個根結點
父節點、子節點:若某個節點連接到下面一個節點,則該節點為父節點,而下面的節點稱為子節點
祖先節點、子孫節點:若某個節點有一條路徑可以通往另一個節點,那就代表該節點為祖先節點
兄弟節點:當它們都是同一個父節點的子節點時則該子節點們互為兄弟節點
樹葉節點:分支度為0的節點
分支度:一個節點有幾個子節點稱為分支度
高度:某節點到距離最遠的樹葉節點的最長路徑
深度:根節點到某節點的路徑長度
階度:樹中結點世代的關係
樹的表示法
如果一般樹使用鏈結串列來表示的話,會因為最大節點的數量而決定要多少個鏈結,可是這樣就會導致空間上的浪費,因為不是每個節點都有最大數量的子節點
像上面的圖片,最大數量就是3,可是其他節點的子節點就只有2而已,所以每個節點都還會再浪費一個鏈結空間,樹葉節點還會再浪費更多的空間
所以通常會用另外一種方法Left Child-Right Sibling,把多元樹轉換成二元樹,這樣可以節省空間,但缺點是操作會比一般的樹還要花更多的時間,所以通常會使用的場合是,記憶體非常少又或是不需要隨機訪問的時候
二元樹
二元樹的意思就是每個節點最多只有兩個子節點而已,而他和一般樹的差別在於二元樹有排列順序關係,而且二元樹可以是空的,那會用到二元樹是因為二元樹浪費的空間比起其他的一般樹還要少,所以基本上都會使用二元樹。下面有著3個比較特殊的二元樹種類:
$$歪斜樹:如果歪斜樹完全沒有左節點或右節點的話,就稱為歪斜樹$$
$$完滿二元樹:如果有一棵二元樹的高度為k,那麼樹的節點數為2^k - 1$$
$$完整二元樹:如果有一棵二元樹的高度為k,那麼樹的節點數少於2^k - 1$$
二元樹的表示法
而二元樹的儲存方式也分為陣列和鏈結串列,但是如果用陣列儲存的話可能會發生空間上的浪費,不過如果是完整二元樹或完滿二元樹的話,因為是按照陣列的順序排列的,所以不會有跳號的問題,相對地說陣列不適合用於歪斜樹
鏈結串列方式就比較適合二元樹,加入和刪除就會比較容易,尤其是當二元樹是不完整的,但是會比較難找到父節點
我們也可以用動態的方式建立二元樹,透過提供的字串來建立,因為二元樹只有兩個子節點,所以字串可以寫成R(A...)(B...),利用兩個括號來代表左子樹及右子樹
二元樹走訪
二元樹走訪也就是說把樹中的每個節點都走訪過一次,並且都是從左到右的話,可以分為三種遍歷方式:
前序走訪:先印出root節點然後才印出左子節點及右子節點,如果比起其他的節點需要先印出root節點的話,可以使用前序走訪
使用堆疊來完成前序走訪的話,會先把root的左子節點都先push進堆疊裡並且印出,之後再判斷該節點是否指向空的位址,如果是的話就把堆疊裡的top節點給pop,然後在檢查是否有右子節點
中序走訪:會先走訪左子樹之後拜訪根節點之後走訪右子樹,如果樹有固定的排序的話,可以使用中序走訪,來印出原本的內容
使用堆疊來完成中序走訪的話,會先把root的左子節點都先push進堆疊裡,之後再判斷該節點是否指向空的位址,如果是的話就把堆疊裡的top節點給pop,然後在檢查是否有右子節點
後序走訪:先從左至右才印出root節點,如果需要先印出樹葉節點的話,可以使用後序走訪
還有一個叫做level order的遍歷方式,依照階層來印出值
二元運算樹
運算式可以用二元樹來儲存,二元運算樹的前中後序,就代表運算式的前中後序。可以使用括號來逐步變成二元運算樹,會先由內部的括號開始,然後左邊運算元做為左子節點,又變運算元做為右子節點。
引線二元樹
引線二元樹就是把原本沒利用到的空節點加以利用,把這些空節點轉換成引線指向其他節點,其好處是中序遍歷時不需用遞迴跟堆疊
引線二元樹跟二元樹最大的差別在於,引線二元樹多了兩個欄位,去判斷是否為引線
insert()
透過比對值的大小來判斷要插入左邊還是右邊,並且插入在樹葉節點的位置
deleteNode()
先判斷父節點是否為root,接著再判斷要刪除的節點是在父節點的左邊還是右邊
當要刪除兩個子節點時,先尋找中序前行者之後,改變該節點的右節點,最後在判斷該節點是在父節點的左邊還是右邊來連接
刪除只有一個子節點時,要先判斷該節點是在父節點的左邊還是右邊,接著再判斷要刪除的節點有多少個子節點還是樹葉節點。如果父節點就是root的話,就一樣判斷是在左邊還是右邊,如果是左邊的話要尋找中序前行者,右邊的話要尋找中序後繼者
完整程式碼
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
struct tbintree {
int number;
int rbit;
int lbit;
struct tbintree* lchild;
struct tbintree* rchild;
} *root, * ptr, * newnode;
struct tbintree* newtbintree();
void insert();
void deleteNode();
void inorderShow(struct tbintree*);
void insertRight(struct tbintree*, struct tbintree*);
void insertLeft(struct tbintree*, struct tbintree*);
struct tbintree* insucc(struct tbintree*);
struct tbintree* inpred(struct tbintree*);
void preorderShow(struct tbintree*);
struct tbintree* preordersucc(struct tbintree*);
int main()
{
const char* menuPrompt =
"=== Thread Binary Search Program ==\n"
" 1. Insert\n"
" 2. Delete\n"
" 3. Show\n"
" 4. PreShow\n"
" 5. Quit\n"
"Enter your choice: ";
int choice;
root = newtbintree();
root->lchild = root;
root->rchild = root;
root->lbit = 0;
root->rbit = 1;
do {
printf("\n%s", menuPrompt);
scanf_s("%d", &choice);
switch (choice) {
case 1:
insert();
break;
case 2:
deleteNode();
break;
case 3:
inorderShow(root);
break;
case 4:
preorderShow(root);
break;
case 5:
puts("Bye Bye ^_^");
exit(1);
default:
puts("Invalid choice !!");
}
} while (1);
printf("\n");
return 0;
}
void insert()
{
newnode = newtbintree();
printf("\nEnter a number to insert : ");
scanf_s("%d", &newnode->number);
if (root->lchild == root) {
insertLeft(root, newnode);
printf("Node %d has been inserted!\n", newnode->number);
}
else {
ptr = root->lchild;
while (ptr->number != newnode->number) {
if (newnode->number < ptr->number) {
if (ptr->lbit == 0) {
insertLeft(ptr, newnode);
break;
}
else
ptr = ptr->lchild;
}
else if (newnode->number > ptr->number) {
if (ptr->rbit == 0) {
insertRight(ptr, newnode);
break;
}
else
ptr = ptr->rchild;
}
}
if (ptr->number == newnode->number) {
puts("Number existed ...!");
return;
}
else
printf("Node %d has been inserted!\n", newnode->number);
}
}
void deleteNode()
{
struct tbintree* ptrParent;
struct tbintree* ptrPred;
struct tbintree* ptrSucc;
int num;
if (root->lchild == root) {
puts("No Data!");
return;
}
printf("Enter a number u want to delete: ");
scanf_s("%d", &num);
ptrParent = root;
ptr = root->lchild;
while (ptr->number != num) {
ptrParent = ptr;
if (num < ptr->number) {
if (ptr->lbit == 1)
ptr = ptr->lchild;
else
break;
}
else if (num > ptr->number) {
if (ptr->rbit == 1)
ptr = ptr->rchild;
else
break;
}
}
if (ptr->number != num) {
printf("Not found number %d!\n", num);
return;
}
printf("Deleting number %d... OK!\n", ptr->number);
if (ptr->lbit == 0 && ptr->rbit == 0) {
if (ptrParent == root) {
ptrParent->lchild = root;
ptrParent->lbit = 0;
}
else if (ptr->number < ptrParent->number) {
ptrParent->lchild = ptr->lchild;
ptrParent->lbit = 0;
}
else {
ptrParent->rchild = ptr->rchild;
ptrParent->rbit = 0;
}
free(ptr);
}
else if (ptr->lbit == 1 && ptr->rbit == 1) {
ptrPred = inpred(ptr);
ptrPred->rchild = ptr->rchild;
ptrPred->rbit = ptr->rbit;
if (ptrParent->lbit == 1)
{
ptrParent->lchild = ptr->lchild;
}
else
{
ptrParent->rchild = ptr->lchild;
}
free(ptr);
}
else
{
if (ptrParent == root)
{
if (ptr->lbit == 1)
{
ptrPred = inpred(ptr);
root->lchild = ptr->lchild;
ptrPred->rchild = root;
}
else
{
ptrSucc = insucc(ptr);
root->lchild = ptr->rchild;
ptrSucc->lchild = root;
}
}
else
{
if (ptr->number < ptrParent->number)
{
if (ptr->rbit == 1 && ptr->lbit == 0)
{
ptr->rchild->lchild = ptr->lchild;
ptr->rchild->lbit = ptr->lbit;
ptrParent->lchild = ptr->rchild;
ptrParent->lbit = ptr->rbit;
}
else if (ptr->rbit == 0 && ptr->lbit == 1)
{
ptr->lchild->rchild = ptr->rchild;
ptr->lchild->rbit = ptr->rbit;
ptrParent->lchild = ptr->lchild;
ptrParent->lbit = ptr->lbit;
}
}
else
{
if (ptr->lbit == 1 && ptr->rbit == 0)
{
ptrParent->rchild = ptr->lchild;
ptrParent->rbit = ptr->lbit;
ptr->lchild->rchild = ptr->rchild;
ptr->lchild->rbit = ptr->rbit;
}
else if (ptr->lbit == 0 && ptr->rbit == 1)
{
ptrParent->rchild = ptr->rchild;
ptrParent->rbit = ptr->rbit;
ptr->rchild->lchild = ptrParent;
ptr->rchild->lbit = ptr->lbit;
}
}
free(ptr);
}
}
}
void insertRight(struct tbintree* nodeParent, struct tbintree* node)
{
struct tbintree* w;
node->rchild = nodeParent->rchild;
node->rbit = nodeParent->rbit;
node->lchild = nodeParent;
node->lbit = 0;
nodeParent->rchild = node;
nodeParent->rbit = 1;
if (node->rbit == 1) {
w = insucc(node);
w->lchild = node;
}
}
void insertLeft(struct tbintree* nodeParent, struct tbintree* node)
{
struct tbintree* w;
node->lchild = nodeParent->lchild;
node->lbit = nodeParent->lbit;
node->rchild = nodeParent;
node->rbit = 0;
nodeParent->lchild = node;
nodeParent->lbit = 1;
if (node->lbit == 1) {
w = inpred(node);
w->rchild = node;
}
}
struct tbintree* preordersucc(struct tbintree* node) {
struct tbintree* position;
if (node->lbit == 1)
{
return node->lchild;
}
else
{
position = node;
while (position->rbit == 0)
{
position = position->rchild;
}
return position->rchild;
}
}
void preorderShow(struct tbintree* node) {
struct tbintree* p = root;
if (node->lchild == root) {
puts("No Data!");
return;
}
puts("\nPreorder display thread binary search tree");
do
{
p = preordersucc(p);
if (p != root)
printf("%-5d", p->number);
} while (p != root);
puts("\n--------------------------------------------");
}
void inorderShow(struct tbintree* node)
{
if (node->lchild == root) {
puts("No Data!");
return;
}
puts("\nInorder display thread binary search tree");
ptr = root;
do {
ptr = insucc(ptr);
if (ptr != root)
printf("%-5d", ptr->number);
} while (ptr != root);
puts("\n--------------------------------------------");
}
struct tbintree* insucc(struct tbintree* node)
{
struct tbintree* succ;
succ = node->rchild;
if (node->rbit == 1)
while (succ->lbit == 1)
succ = succ->lchild;
return succ;
}
struct tbintree* inpred(struct tbintree* node)
{
struct tbintree* pred;
pred = node->lchild;
if (node->lbit == 1)
while (pred->rbit == 1)
pred = pred->rchild;
return pred;
}
struct tbintree* newtbintree()
{
struct tbintree* temp;
temp = (struct tbintree*)malloc(sizeof(struct tbintree));
temp->lchild = NULL;
temp->rchild = NULL;
return temp;
}