二叉查詢樹 C++實現
阿新 • • 發佈:2019-01-29
二叉查詢樹的C++實現
二叉查詢樹(BST)是父節點的值比左兒子的值大,比右兒子的值小的一種二叉樹。其資料元素集合包括每個節點,每個節點又包含節點的值和它的左兒子和右兒子。基本操作有:構造空的BST;判空;查詢;插入;刪除和遍歷。可以採用遞迴的方法來定義這些操作,也可採用非遞迴方法。
插入和刪除相對來說比較有難度,如果有不對的還請看到的童鞋指出來。
在非遞迴中,插入和刪除都用到了查詢,在插入的時候,如果該元素已經在樹中,就不用再次插入了,在刪除的時候,如果該元素不在樹中,那還刪除什麼?
所以,先寫一下查詢函式吧。
- 查詢Search
找到這一個值item,如果找到了found=true,還需要item這個值所在的位置p和它父親所在的位置pre。 - 插入
先查詢一遍,沒有該值則插入,由於Search函式中記錄了p和pre,那麼,只需判斷一下該值與pre->data的大小,就可以了。 - 刪除
我覺得刪除是這幾個中最麻煩的,因為有三種情況:1. 刪除的是葉子節點;2.刪除的是隻有一個兒子的節點;3.刪除的是有兩個兒子的節點。
情況1和2都比較好解決,先Search一遍,這樣可以找到要刪除的點item的p和pre,然後建立一顆子樹subtree,如果p的左子樹為NULL,那麼subtree=p->right,比較item和pre->data的大小,然後,將pre的left或right指向subtree就OK了。
說說情況3,可以這樣想,將情況3轉換成情況1或2,怎麼轉換呢?當然,Search後記錄了p和pre,想想,把當前這個節點刪了,你要找其它的來替換它吧,不然它的孩子怎麼辦?找誰呢?要找的這個值要比它的左值大,又要比右值小,所以一定要在它的右子樹找,找到什麼時候為止呢?找到最小點!也就是要找的那個點沒有左兒子!這時,將這個點賦值給要刪除的那個點,而將這個點刪除,這個點有什麼特點?它的left=null對不對?又變為了情況2或1。
非遞迴的實現:
#include <iostream>
using namespace std;
class BST
{
public:
class node
{
public:
node(){ left = right = NULL; }
public:
int data;
node* left;
node* right;
};
public:
BST(){ root = NULL; };
BST(int item){ root->data = item; root = NULL; }
bool empty()const;
void Search(int item, bool &found, node* &pre, node* &p);
void Insert(int value);
void Delete(int item);
private:
node* root;
};
bool BST::empty()const
{
return (root == NULL);
}
void BST::Search(int item, bool &found, node* &pre, node* &p)
{
for (;;)
{
if (found||p==NULL)
break;
else
{
if (item<p->data)
{
pre = p;
p = p->left;
}
else if (item > p->data)
{
pre = p;
p = p->right;
}
else
found = true;
}
}
}
void BST::Insert(int value)
{
bool found = false;
node* p = root;
node* pre = p;
Search(value, found, pre, p);
if (found)
cout << "The value has been in the BST" << endl;
else
{
node* n = new node;
n->data = value;
if (empty())
root = n;
else
{
if (value < pre->data)
pre->left = n;
if (value>pre->data)
pre->right = n;
}
}
}
void BST::Delete(int item)
{
bool found = false;
node* p = root;
node* pre = p;
Search(item, found, pre, p);
if (!found)
cout << "The value is not in the BST" << endl;
else
{
if (p->left!=NULL&&p->right!=NULL)
{
node* p2 = p->right;//找到右子樹最小點
node* pre2 = p;//找到右子樹最小點的父節點
while (p2->left!=NULL)
{
pre2 = p2;
p2 = p2->left;
}
p->data = p2->data;
pre = pre2;//與情況1和2合併
p = p2;//與情況1和2合併
}
node* subtree = NULL;
if (p->left == NULL)
subtree = p->right;
if (p->right == NULL)
subtree = p->left;
if (pre == NULL)
root = subtree;
else
if (pre->right==p)
pre->right = subtree;
else
pre->left = subtree;
delete p;
}
}
遞迴的用來遍歷是最方便的了
void BST::Inorder(node* p)const
{
if (p!=NULL)
{
Inorder(p->left);
cout << p->data;
Inorder(p->right);
}
}
附上一個我看過了一個題目,我當時做不出來,現在可以了。
題目:在二元樹中找出和為某一值的所有路徑
輸入一個整數和一棵二元樹。從樹的根結點開始往下訪問一直到葉結點所經過的所有結點形成一條路徑。
打印出和與輸入整數相等的所有路徑。
例如輸入整數22 和如下二元樹
10
/ \
5 12
/ \
4 7
則打印出兩條路徑:10, 12 和10, 5, 7。
這個題目基本就是遍歷,列印路徑,那麼應該用遞迴+佇列或陣列
思路,由於要列印路徑,那麼就應該將走過的路徑記錄下來 ,可以放入陣列中,還需構造一個二叉樹,新增10/5/4/7/12這幾個數,然後先訪問10這個數,入陣列,用10與sum=22比較,10<22,sum-10=12,繼續比較,12入陣列,這時,與12剛好相等,sum=0,同時12是葉子節點,依次出陣列;回到父節點,繼續。程式碼如下:
#include <iostream>
#include <vector>
using namespace std;
class BSTNode
{
public:
BSTNode(){ Left = Right = 0; }
BSTNode(int & item){ data = item; }
public:
int data;
BSTNode* Left;
BSTNode* Right;
};
class BSTTree
{
public:
BSTTree(){ root = 0; }
void Insert(BSTNode* &loc, int value);
void PrintPath(BSTNode* loc, int sum, vector<int>& path);
public:
BSTNode* root;
};
void BSTTree::Insert(BSTNode* & loc,int value)
{
if (loc == 0)
loc = new BSTNode(value);
else if (value<loc->data)
{
Insert(loc->Left, value);
}
else if (value > loc->data)
{
Insert(loc->Right, value);
}
else
cout << "該數已經在樹中了!" << endl;
}
void BSTTree::PrintPath(BSTNode* loc, int sum, vector<int>& path)
{
bool isleaf = loc->Left == 0 && loc->Right==0;
path.push_back(loc->data);
if (loc->data > sum || loc == 0)
return;
else if (loc->data==sum&&isleaf)
{
for (vector<int>::iterator i = path.begin(); i != path.end();i++)
{
cout << *i << " ";
}
}
else
{
sum = sum - loc->data;
if (loc->Left)
{
PrintPath(loc->Left, sum, path);
}
if (loc->Right)
{
PrintPath(loc->Right, sum, path);
}
}
sum = sum + loc->data;
path.pop_back();
}
int main()
{
BSTTree T;
vector<int> path;
T.Insert(T.root, 10);
T.Insert(T.root, 5);
T.Insert(T.root, 4);
T.Insert(T.root, 7);
T.Insert(T.root, 12);
T.PrintPath(T.root, 22, path);
return 0;
}
個人覺得,在插入,遍歷和查詢時用遞迴很方便,刪除的話我比較喜歡非遞迴方式。