搜索樹數據結構支持許多動態集合操做,包括SEARCH(查詢)、MINIMUM(最小值)、MAXIMUM(最大值)、PREDECESSOR(前驅)、SUCCESSOR(後繼)、INSERT(插入)、DELETE(刪除)。所以咱們使用一棵搜索樹既能夠做爲一個字典又能夠做爲一個優先隊列。html
對任何結點x,其左子樹中的關鍵字最大不超過x.key,其右子樹中的關鍵字最小不低於x.key。(如圖)node
對於二叉樹能夠用其簡單的方式來遍歷。如先序遍歷,中序遍歷,後序遍歷。(這裏不進行講解)數據結構
存儲學習
在學習二叉搜索樹以前咱們先了解一下怎麼存儲樹,用的是C++代碼,網上的代碼都是基於類的。而且對於C++的指針開空間說明的並不清楚。在上一篇文章樹中,我介紹了一種存儲二叉樹結點的方式(鏈表式),這裏咱們改進一下。spa
struct node //結點 { int value; //關鍵字 node* parent; //父結點 node* left; //左子 node* right; //右子 }* root; //定義一個全局根
初始化3d
賦空天然是初始化要作的事情,不過在C++11中,爲了不老版本的NULL的問題(自行百度),加入了一個新的賦空方式nullptr,可是由於我用的編譯器版本太低不支持C++11新特性,因此在這裏我仍是用的NULL,最好是用0賦空。指針
node* Tree_createNode(int value) { node* p = (node*)malloc(sizeof(node)); //結構體指針開空間 p->value = value; //賦值 p->parent = 0; //賦空 p->left = 0; //賦空 p->right = 0; //賦空 return p; }
在學習樹用法以前呢,咱們先要了解一下怎麼構樹,說白了就是插入。code
而插入也不是盲目的瞎放,咱們要保證其二叉搜索樹的性質來進行插入操做。htm
void Tree_Insert(int value) { if (!root) { root = Tree_createNode(value); //若是根不存在,建立 return; } node* curNode = root; //當前結點位置 node* insertNode = NULL; //要插入結點的父結點 node* p = Tree_createNode(value); //要插入結點 while (curNode) //找到插入結點的父結點(既子結點爲空) { insertNode = curNode; if (value < curNode->value) curNode = curNode->left; else curNode = curNode->right; } p->parent = insertNode; //設置父結點 if (value < insertNode->value) //判斷插入左子,仍是右子 insertNode->left = p; else insertNode->right = p; }
SEARCHblog
咱們常常須要查找一個存儲在二叉搜索樹上的關鍵字。設樹的高度爲h的話,查詢的最壞時間爲O(h)。
輸入一個指向根的指針和一個關鍵字,若是這個結點存在則返回次結點,不然返回NULL。
代碼其實跟插入是相同的思想。
node* Tree_Search(node* p, int value) //查找 { if (!p || value == p->value) //若是指針爲空或找到,返回p return p; if (value < p->value) return Tree_Search(p->left,value); else return Tree_Search(p->right,value); }
最大關鍵字(MAXIMUM)和最小關鍵字(MINIMUM)元素
瞭解二叉搜索樹的思想就能想到,很簡單,最小值就是一直找其左子,知道找到不到爲止(最大值同理)。
node* Tree_Minimum(node* p) //查找最小值 { if (!p->left) return p; Tree_Minimum(p->left); } node* Tree_Maximum(node* p) //查找最大值 { if (!p->right) return p; Tree_Minimum(p->right); }
後繼和前驅(SUCCESSOR&PREDECESSOR)
所謂前驅和後繼是相對於遍歷方式來講的,不過在二叉搜索樹中比較特殊,由於二叉搜索樹的中序遍歷就是從小到大輸出。因此相對於中序遍歷的前驅和後繼天然就是相鄰的兩個值。
後繼有兩種狀況:
node* Tree_Successor(node* p) //查找後繼 { if (p->right) return Tree_Minimum(p->right); node* y = p->parent; while(y && p == y->right) { p = y; y = y->parent; } return y; }
有了插入天然也少不了刪除的存在,相對於插入來講,刪除較爲複雜。
對於二叉搜索樹中刪除一個結點的整個策略分爲三種基本狀況(咱們把刪除結點設爲z):
對於第二種狀況的示意圖(左子同理)
對於第三種狀況的示意圖分兩種狀況
1.右子直接爲其刪除結點z的後繼
2.右子並不是爲其刪除結點z的後繼,則要找其z的後繼。
從上圖能夠看出,無論哪一種刪除操做都有一個共同特徵,移動子樹,因此咱們定義一個過程TRANSPLANT。基本思想就是用另外一顆子樹替換一棵子樹併成爲其父結點的孩子結點。
node* Tree_Transplant(node *u, node *v) //將一棵以v爲根的子樹,替換一棵以u爲根的子樹 { if (!u->parent) //處理樹根的狀況 { root = u; } else if (u == u->parent->left) //u爲其左孩子 { u->parent->left = v; } else //u爲右孩子 { u->parent->right = v; } if (v) //v不爲空,則將v的父親指向u的父親 { v->parent = u->parent; } }
咱們利用TRANSPLANT操做,從二叉搜索樹中刪除結點z的刪除過程:
node* Tree_Delete(node *z) //刪除操做 { if (!z->left) //左子樹爲空,對應第二種狀況 { Tree_Transplant(z,z->right); } else if (!z->right) //同上 { Tree_Transplant(z,z->left); } else //第三種狀況 { node *y = Tree_Minimum(z->right); if (y->parent != z) //第三種狀況中的第二種狀況,分紅兩個子樹 { Tree_Transplant(y,y->right); y->right = z->right; y->right->parent = y; } Tree_Transplant(z,y); y->left = z->left; y->left->parent = y; } }
(完) 若有問題請留言,只要看到必定回覆。