二叉查找樹(Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具備下列性質的二叉樹:node
二叉搜索樹做爲一種經典的數據結構,它既有鏈表的快速插入與刪除操做的特色,又有數組快速查找的優點;因此應用十分普遍,例如在文件系統和數據庫系統通常會採用這種數據結構進行高效率的排序與檢索操做。
算法
template<typename T> class BSTNode{ public: T e; //節點元素 BSTNode<T>*left; //左節點 BSTNode<T>*right; //右節點 public: BSTNode(T E):e(E),left(nullptr),right(nullptr){} //初始化 };
class BST { private: BSTNode<T> *root; //根節點 int size; //大小 public: };
二叉查找樹也是二叉樹,因此存儲結構和二叉樹同樣。數據庫
/*------------------------------------------------------------------*/ private: //返回插入新節點後二分搜索樹的根 BSTNode<T> *add(BSTNode<T> *node, T e) { if (node == nullptr) { //若是結點爲空 ++size; //大小+1 return new BSTNode<T>(e); //插入元素e返回新節點 } if (node->e > e) { //若是要插入的節點值小於當前節點元素 node->left = add(node->left, e); //第歸左子樹直到爲空返回新節點爲當前節點的左孩子 } else if (node->e < e) { //若是要插入的節點值大於當前節點元素 node->right = add(node->right, e); //第歸右子樹直到爲空返回新節點爲當前節點的右孩子 } return node; //返回原節點 } public: void add(T e) { //添加節點 root = add(root, e); //添加新節點傳入根結點和元素並返回新的根結點 } /*------------------------------------------------------------------*/
個人BST不包含重複元素,若是想要包含重複元素的話,只須要定義:數組
/*------------------------------------------------------------------*/ private: bool contains(BSTNode<T> *node, T e) { //判斷是否有元素e if (node == nullptr)return false; //若是節點爲空,則返回false if (node->e == e)return true; //若是要找的的元素等於節點元素e,則返回true else if (node->e > e) { //若是要找的元素小於節點元素 return contains(node->left, e); //則第歸當前節點的左子樹 } else { // if(node->e<e) //若是要找的元素大於節點元素 return contains(node->right, e); //則第歸當前節點的右子樹 } } public: bool contains(T e) { //判斷是否有元素e return contains(root, e); } /*------------------------------------------------------------------*/
/*------------------------------------------------------------------*/ private: void preOrder(BSTNode<T> *node){//第歸前序遍歷 if (node == nullptr)return; //若是節點爲則空返回 std::cout << node->e << std::endl;//打印節點元素 preOrder(node->left); //第歸當前節點左子樹 preOrder(node->right); //第歸當前節點右子樹 } public: void preOrder(){//第歸前序遍歷 preOrder(root); } /*------------------------------------------------------------------*/
//非遞歸前序遍歷 深度優先遍歷 void preOrderNR() { std::stack<BSTNode<T> *> s;//建立一個棧 s.push(root);//根節點入棧 while (!s.empty()) { //若是棧爲空,則中止循環 BSTNode<T> *cur = s.top(); //建立一個節點暫存棧頂節點 std::cout << cur->e << " "; //打印棧頂節點 s.pop(); //出棧 if (nullptr != cur->right) { //若是棧頂節點右節點不爲空 s.push(cur->right); //棧頂節點右節點如棧 } if (nullptr != cur->left) { //若是棧頂節點左節點不爲空 s.push(cur->left); //棧頂節點左節點如棧 } } std::cout << std::endl; }
/*------------------------------------------------------------------*/ private: void inOrder(BSTNode<T> *node) { //第歸中序遍歷 if (node == nullptr)return; inOrder(node->left); std::cout << node->e << std::endl; inOrder(node->right); } public: void inOrder() { //第歸中序遍歷 inOrder(root); } /*------------------------------------------------------------------*/
//非遞歸中序遍歷 void inOrderNR() { std::stack<BSTNode<T> *> s; //建立一個棧 BSTNode<T> *cur = root; //當前節點爲根節點 while (!s.empty() || nullptr != cur) { //若是棧爲空或當前節點爲空 //樹的左邊一列入棧 while (nullptr != cur) { s.push(cur); cur = cur->left; } if (!s.empty()) { //若是棧不爲空 cur = s.top(); //當前節點爲棧頂 std::cout << cur->e << " ";//打印節點元素 s.pop(); //出棧 cur = cur->rigfht; //當節點賦值爲當前節點的右節點 } } std::cout << std::endl; }
也叫排序遍歷,它會有序打印二分搜索樹。數據結構
/*------------------------------------------------------------------*/ private: void postOrder(BSTNode<T> *node) {//第歸後續遍歷 if (node == nullptr)return; postOrder(node->left); postOrder(node->right); std::cout << node->e << std::endl; } public: void postOrder() { //後續遍歷 postOrder(root); } /*------------------------------------------------------------------*/
void postOrderNR(){ //非第歸後續遍歷 std::stack<BSTNode<T>*>s; //建立一個棧 BSTNode<T>*cur = root; BSTNode<T>*temp; while(nullptr != cur || !s.empty()){ //樹的左邊一列入棧 while(nullptr != cur){ s.push(cur); cur = cur ->left; } if(!s.empty()){ //若是棧不爲空 cur = s.top(); //當前節點爲棧頂 if(cur->right == temp || nullptr == cur->right){ //若是當前節點爲暫存節點的父節點或當前節的右節點爲空 std::cout<<cur->e<<" "; //打印當前節點元素 s.pop(); //出棧 temp = cur; //暫存當前節點 cur = nullptr; //cur賦值爲空 } else{ cur = cur->right; //不然遍歷右節點 } } } std::cout<<std::endl; }
後續遍歷,它會先深刻到樹的底部從下向上遍歷,因此後續遍歷適用於釋放內存。函數
/*------------------------------------------------------------------*/ public: void levelOrder(){ std::queue<BSTNode<T>*>q; //建立一個隊列 q.push(root); //根結點如對 while(!q.empty()){ //若是隊列不爲空 BSTNode<T>*cur = q.front(); //取出對頭節點 q.pop(); //出隊 std::cout<<cur->e<<" "; //打印對頭節點元素 if(nullptr != cur->left){ //若是當前節點的左孩子不爲空 q.push(cur->left); //則當前節點的左孩子入隊 } if(nullptr !=cur->right){ //若是當前節點的右孩子不爲空 q.push(cur->right); //則當前節點的右孩子入隊 } } std::cout<<std::endl; } /*------------------------------------------------------------------*/
層次遍歷是從上向下遍歷,適用於搜索策略上。post
/*------------------------------------------------------------------*/ private: //返回最小值的節點 BSTNode<T> *min(BSTNode<T> *node) { if (node->left == nullptr) //二分搜索樹最小值確定是最左邊的節點 return node; return min(node->left); } //返回最大值的節點 BSTNode<T> *max(BSTNode<T> *node) { if (node->right == nullptr) //二分搜索樹最大值確定是最右邊的節點 return node; return max(node->right); } public: T minimun() {//返回最小值 if (size > 0) return min(root)->e; } T maximun() {//返回最大值 if (size > 0) return max(root)->e; } /*------------------------------------------------------------------*/
根據BST的特性,最小值,必定在樹的最左邊節點,最大值,必定在樹的最右邊節點。code
/*------------------------------------------------------------------*/ private: //刪除以node爲根的二分搜索樹的最小節點 //返回刪除節點後新的二分搜搜索樹的根結點 BSTNode<T> *removeMin(BSTNode<T> *node) { if (node->left == nullptr) { //若是當前節點左孩子爲空 BSTNode<T> *rightNode = node->right; //則暫存當前節點的右孩子 delete node; //刪除當前節點 size--; //個數-1 return rightNode; //返回被刪除節點的右孩子 } node->left = removeMin(node->left); //第歸節點的左孩子,把返回的右孩子放入上一節點的左孩子 return node; //返回根結點 } //刪除以node爲根的二分搜索樹的最大節點 //返回刪除節點後新的二分搜搜索樹的根結點 BSTNode<T> *removeMax(BSTNode<T> *node) { if (node->right == nullptr) { BSTNode<T> *leftNode = node->left; delete node; size--; return leftNode; } node->right = removeMax(node->right); return node; } public: T removeMin() { //刪除最小值並返回最小值 T ret = minimun(); root = removeMin(root); return ret; } T removeMax() { //刪除最大值並返回最大值 T ret = maximun(); root = removeMax(root); return ret; } /*------------------------------------------------------------------*/
由於最大最小值都是在樹的葉子節點,這個最小值葉子只有右孩子可能會有子樹,因此刪除葉子節點時不管它有沒有右孩子,都把它的右孩子放到它父節點的左孩子上。
注意:空也是一個二叉樹;blog
前驅法同理排序
/*------------------------------------------------------------------*/ private: //刪除元素e 後繼法 BSTNode<T>* RRemove(BSTNode<T>*node,T e) { if(nullptr == node)return nullptr; //若是節點爲空,則返回空 if(e<node->e) //若是要刪除元素小於當前節點元素 { node->left = RRemove(node->left,e); //當前節點的左孩子賦值爲,第歸當前節點的左孩子返回當節點前左孩子的右孩子 return node; //返當根節點 }else if(e>node->e){//若是要刪除元素大於當前節點元素 node->right = RRemove(node->right,e);//當前節點的右孩子賦值爲,第歸當前節點的右孩子返回當前節點右孩子的左孩子 return node; //返回根節點 } else { //e == node->e 若是要刪除元素等於當前節點元素 if (nullptr == node->left) { //若是當前節點沒有左孩子 BSTNode<T> *rightNode = node->right; //就暫存當前節點的右孩子 delete node; //刪除當前節點 --size; //大小-1 return rightNode; //返回當前節點的右孩子 } if (nullptr == node->right) { //若是當前節點沒有左孩子 BSTNode<T> *leftNode = node->left; //就暫存當前節點的左孩子 delete node; //刪除當前節點 --size; //大小-1 return leftNode; //返回當前節點的左孩子 } //若是不爲空就把離要刪除節點最近的比節點要大的節點拿過來,換成它。 BSTNode<T> *successor = new BSTNode<T>(min(node->right)->e); //建立一個節點把要刪除節點的右子樹中最小值給新建節點 successor->right = removeMin(node->right); //新建節點的右孩子指向,刪除當前節點的右子樹中的最小節點並返回這個根結點。 // removeMin過程當中size已經-1,因此不須要再-1 successor->left = node->left; //新建節點的左孩子指向要刪除節點的左孩子 node->left = node->right = nullptr; //把要刪除節點左右孩子指向空 delete node; //刪除要刪除節點 return successor; //返回新建節點給上一級 } } //刪除元素e 前驅法 BSTNode<T>* LRemove(BSTNode<T>*node,T e) { if(nullptr == node)return nullptr; if(node->e>e) { node->left = LRemove(node->left,e); return node; }else if(node->e<e){ node->right = LRemove(node->right,e); return node; } else{ //node->e == e if(nullptr == node->left){ BSTNode<T>*rightNode = node->right; delete node; --size; return rightNode; } if(nullptr == node->right){ BSTNode<T>*leftNode = node->left; delete node; --size; return leftNode; } BSTNode<T>*precursor = new BSTNode<T>(min(node->left)->e); precursor->left = removeMin(node->left); precursor->right = node->right; node->left = node->right = nullptr; delete node; return precursor; } } public: void RRemove(T e)//刪除任意元素e前驅法 { root = RRemove(root,e); } void LRemove(T e)//刪除任意元素e後繼法 { root = LRemove(root,e); } /*------------------------------------------------------------------*/
#ifndef C___BST_H #define C___BST_H #include <stack> #include <queue> template<typename T> class BSTNode{ public: T e; //節點元素 BSTNode<T>*left; //左節點 BSTNode<T>*right; //右節點 public: BSTNode(T E):e(E),left(nullptr),right(nullptr){} }; template<typename T> class BST { private: BSTNode<T> *root; //根節點 int size; //大小 /*------------------------------------------------------------------*/ private: //返回插入新節點後二分搜索樹的根 BSTNode<T> *add(BSTNode<T> *node, T e) { if (node == nullptr) { //若是結點爲空 ++size; //大小+1 return new BSTNode<T>(e); //插入元素e返回新節點 } if (node->e > e) { //若是要插入的節點值小於當前節點元素 node->left = add(node->left, e); //第歸左子樹直到爲空返回新節點爲當前節點的左孩子 } else if (node->e < e) { //若是要插入的節點值大於當前節點元素 node->right = add(node->right, e); //第歸右子樹直到爲空返回新節點爲當前節點的右孩子 } return node; //返回原節點 } public: void add(T e) { //添加節點 root = add(root, e); //添加新節點傳入根結點和元素並返回新的根結點 } /*------------------------------------------------------------------*/ private: bool contains(BSTNode<T> *node, T e) { //判斷是否有元素e if (node == nullptr)return false; //若是節點爲空,則返回false if (node->e == e)return true; //若是要找的的元素等於節點元素e,則返回true else if (node->e > e) { //若是要找的元素小於節點元素 return contains(node->left, e); //則第歸當前節點的左子樹 } else { // if(node->e<e) //若是要找的元素大於節點元素 return contains(node->right, e); //則第歸當前節點的右子樹 } } public: bool contains(T e) { //判斷是否有元素e return contains(root, e); } /*------------------------------------------------------------------*/ private: void preOrder(BSTNode<T> *node){//第歸前序遍歷 if (node == nullptr)return; //若是節點爲則空返回 std::cout << node->e << std::endl;//打印節點元素 preOrder(node->left); //第歸當前節點左子樹 preOrder(node->right); //第歸當前節點右子樹 } public: void preOrder(){//第歸前序遍歷 preOrder(root); } //非遞歸前序遍歷 深度優先遍歷 void preOrderNR() { std::stack<BSTNode<T> *> s;//建立一個棧 s.push(root);//根節點入棧 while (!s.empty()) { //若是棧爲空,則中止循環 BSTNode<T> *cur = s.top(); //建立一個節點暫存棧頂節點 std::cout << cur->e << " "; //打印棧頂節點 s.pop(); //出棧 if (nullptr != cur->right) { //若是棧頂節點右節點不爲空 s.push(cur->right); //棧頂節點右節點如棧 } if (nullptr != cur->left) { //若是棧頂節點左節點不爲空 s.push(cur->left); //棧頂節點左節點如棧 } } std::cout << std::endl; } /*------------------------------------------------------------------*/ private: void inOrder(BSTNode<T> *node) { //第歸中序遍歷 if (node == nullptr)return; inOrder(node->left); std::cout << node->e << std::endl; inOrder(node->right); } //也叫排序遍歷,它會有序打印二分搜索樹 public: void inOrder() { //第歸中序遍歷 inOrder(root); } //非遞歸中序遍歷 void inOrderNR() { std::stack<BSTNode<T> *> s; //建立一個棧 BSTNode<T> *cur = root; //當前節點爲根節點 while (!s.empty() || nullptr != cur) { //若是棧爲空或當前節點爲空 //樹的左邊一列入棧 while (nullptr != cur) { s.push(cur); cur = cur->left; } if (!s.empty()) { //若是棧不爲空 cur = s.top(); //當前節點爲棧頂 std::cout << cur->e << " ";//打印節點元素 s.pop(); //出棧 cur = cur->rigfht; //當節點賦值爲當前節點的右節點 } } std::cout << std::endl; } /*------------------------------------------------------------------*/ private: void postOrder(BSTNode<T> *node) {//第歸後續遍歷 if (node == nullptr)return; postOrder(node->left); postOrder(node->right); std::cout << node->e << std::endl; } public: void postOrder() { //後續遍歷 postOrder(root); } //後續遍歷適用於析溝函數 void postOrderNR(){ //非第歸後續遍歷 std::stack<BSTNode<T>*>s; //建立一個棧 BSTNode<T>*cur = root; BSTNode<T>*temp; while(nullptr != cur || !s.empty()){ //樹的左邊一列入棧 while(nullptr != cur){ s.push(cur); cur = cur ->left; } if(!s.empty()){ //若是棧不爲空 cur = s.top(); //當前節點爲棧頂 if(cur->right == temp || nullptr == cur->right){ //若是當前節點爲暫存節點的父節點或當前節的右節點爲空 std::cout<<cur->e<<" "; //打印當前節點元素 s.pop(); //出棧 temp = cur; //暫存當前節點 cur = nullptr; //cur賦值爲空 } else{ cur = cur->right; //不然遍歷右節點 } } } std::cout<<std::endl; } /*------------------------------------------------------------------*/ public: //層次遍歷 廣度優先遍歷 適用於搜索 void levelOrder(){ std::queue<BSTNode<T>*>q; //建立一個隊列 q.push(root); //根結點如對 while(!q.empty()){ //若是隊列不爲空 BSTNode<T>*cur = q.front(); //取出對頭節點 q.pop(); //出隊 std::cout<<cur->e<<" "; //打印對頭節點元素 if(nullptr != cur->left){ //若是當前節點的左孩子不爲空 q.push(cur->left); //則當前節點的左孩子入隊 } if(nullptr !=cur->right){ //若是當前節點的右孩子不爲空 q.push(cur->right); //則當前節點的右孩子入隊 } } std::cout<<std::endl; } /*------------------------------------------------------------------*/ private: //返回最小值的節點 BSTNode<T> *min(BSTNode<T> *node) { if (node->left == nullptr) //二分搜索樹最小值確定是最左邊的節點 return node; return min(node->left); } //返回最大值的節點 BSTNode<T> *max(BSTNode<T> *node) { if (node->right == nullptr) //二分搜索樹最大值確定是最右邊的節點 return node; return max(node->right); } public: T minimun() {//返回最小值 if (size > 0) return min(root)->e; } T maximun() {//返回最大值 if (size > 0) return max(root)->e; } /*------------------------------------------------------------------*/ private: //刪除以node爲根的二分搜索樹的最小節點 //返回刪除節點後新的二分搜搜索樹的根結點 BSTNode<T> *removeMin(BSTNode<T> *node) { if (node->left == nullptr) { //若是當前節點左孩子爲空 BSTNode<T> *rightNode = node->right; //則暫存當前節點的右孩子 delete node; //刪除當前節點 size--; //個數-1 return rightNode; //返回被刪除節點的右孩子 } node->left = removeMin(node->left); //第歸節點的左孩子,把返回的右孩子放入上一節點的左孩子 return node; //返回根結點 } //刪除以node爲根的二分搜索樹的最大節點 //返回刪除節點後新的二分搜搜索樹的根結點 BSTNode<T> *removeMax(BSTNode<T> *node) { if (node->right == nullptr) { BSTNode<T> *leftNode = node->left; delete node; size--; return leftNode; } node->right = removeMax(node->right); return node; } public: T removeMin() { //刪除最小值並返回最小值 T ret = minimun(); root = removeMin(root); return ret; } T removeMax() { //刪除最大值並返回最大值 T ret = maximun(); root = removeMax(root); return ret; } /*------------------------------------------------------------------*/ private: //刪除元素e 後繼法 BSTNode<T>* RRemove(BSTNode<T>*node,T e) { if(nullptr == node)return nullptr; //若是節點爲空,則返回空 if(e<node->e) //若是要刪除元素小於當前節點元素 { node->left = RRemove(node->left,e); //當前節點的左孩子賦值爲,第歸當前節點的左孩子返回當節點前左孩子的右孩子 return node; //返當根節點 }else if(e>node->e){//若是要刪除元素大於當前節點元素 node->right = RRemove(node->right,e);//當前節點的右孩子賦值爲,第歸當前節點的右孩子返回當前節點右孩子的左孩子 return node; //返回根節點 } else { //e == node->e 若是要刪除元素等於當前節點元素 if (nullptr == node->left) { //若是當前節點沒有左孩子 BSTNode<T> *rightNode = node->right; //就暫存當前節點的右孩子 delete node; //刪除當前節點 --size; //大小-1 return rightNode; //返回當前節點的右孩子 } if (nullptr == node->right) { //若是當前節點沒有左孩子 BSTNode<T> *leftNode = node->left; //就暫存當前節點的左孩子 delete node; //刪除當前節點 --size; //大小-1 return leftNode; //返回當前節點的左孩子 } //若是不爲空就把離要刪除節點最近的比節點要大的節點拿過來,換成它。 BSTNode<T> *successor = new BSTNode<T>(min(node->right)->e); //建立一個節點把要刪除節點的右子樹中最小值給新建節點 successor->right = removeMin(node->right); //新建節點的右孩子指向,刪除當前節點的右子樹中的最小節點並返回這個根結點。 // removeMin過程當中size已經-1,因此不須要再-1 successor->left = node->left; //新建節點的左孩子指向要刪除節點的左孩子 node->left = node->right = nullptr; //把要刪除節點左右孩子指向空 delete node; //刪除要刪除節點 return successor; //返回新建節點給上一級 } } //刪除元素e 前驅法 BSTNode<T>* LRemove(BSTNode<T>*node,T e) { if(nullptr == node)return nullptr; if(node->e>e) { node->left = LRemove(node->left,e); return node; }else if(node->e<e){ node->right = LRemove(node->right,e); return node; } else{ //node->e == e if(nullptr == node->left){ BSTNode<T>*rightNode = node->right; delete node; --size; return rightNode; } if(nullptr == node->right){ BSTNode<T>*leftNode = node->left; delete node; --size; return leftNode; } BSTNode<T>*precursor = new BSTNode<T>(min(node->left)->e); precursor->left = removeMin(node->left); precursor->right = node->right; node->left = node->right = nullptr; delete node; return precursor; } } public: void RRemove(T e)//刪除任意元素e前驅法 { root = RRemove(root,e); } void LRemove(T e)//刪除任意元素e後繼法 { root = LRemove(root,e); } /*------------------------------------------------------------------*/ private: void generateDepthString(int depth) { for (int i = 0; i < depth; ++i) { std::cout << "--"; } } public: void generateBSTString(BSTNode<T> *node, int depth){ if (node == nullptr){ generateDepthString(depth); std::cout << "NULL" << std::endl; return; } generateDepthString(depth); std::cout << node->e << std::endl; generateBSTString(node->left, depth + 1); generateBSTString(node->right, depth + 1); } void print(){ generateBSTString(root, 0); } /*------------------------------------------------------------------*/ public: BST() : root(nullptr), size(0) {} int getSize() const { //返回當前樹的大小 return size; } bool isEmpty() const { //判斷當前樹是否爲空 return size == 0; } }; #endif //C___BST_H