二叉搜索树的算法实现
文章目錄
- 1 二叉搜索樹簡介
- 2 二叉搜索樹的算法實(shí)現(xiàn)
- 2.1 節(jié)點(diǎn)結(jié)構(gòu)體的定義
- 2.2 二叉搜索樹插入節(jié)點(diǎn)
- 2.3 二叉搜索樹刪除結(jié)點(diǎn)
- 2.4 二叉搜索樹搜索
- 2.5 二叉搜索樹的遍歷
1 二叉搜索樹簡介
當(dāng)我們要在一組數(shù)中要找到 26?你該怎么找?
答案: 從左至右 或 從右至左遍歷一次,找到這個數(shù)字。
當(dāng)我們把數(shù)據(jù)進(jìn)行排序(按照從小到大的順序排列)后,再查找相應(yīng)的這條記錄?還是用上面的方法嗎?
答案:最快的方式,是采用折半法(俗稱二分查找)。
思考: 當(dāng)我們有新的數(shù)據(jù)加進(jìn)來,或者刪除其中的一條記錄,為了保障查找的效率,我們?nèi)匀灰U蠑?shù)組有序,但是,會碰到我們講順序表時的問題,涉及到大量數(shù)據(jù)的移動!在插入和刪除操作上,就需要耗費(fèi)大量的時間(需進(jìn)行元素的移位),能否有一種既可以使得插入和刪除效率不錯,又可高效查找的數(shù)據(jù)結(jié)構(gòu)和算法呢?
拋磚: 首先解決一個問題,插入時不移動元素,我們可以想到鏈表,但是要保證其有序的話,首先得遍歷鏈表尋找合適的位置,那么又如何高效的查找合適的位置呢,能否可以像二分一樣,通過一次比較排除一部分元素?
引玉: 那么我們可以用二叉樹的形式,以數(shù)據(jù)集第一個元素為根節(jié)點(diǎn),之后將比根節(jié)點(diǎn)小的元素放在左子樹中,將比根節(jié)點(diǎn)大的元素放在右子樹中,在左右子樹中同樣采取此規(guī)則。那么在查找 x 時,若 x 比根節(jié)點(diǎn)小可以排除右子樹所有元素,去左子樹中查找(類似二分查找),這樣查找的效率非常好,而且插入的時間復(fù)雜度為 O(h),h 為樹的高度,較 O(n)來說效率提高不少。故二叉搜索樹用作一些查找和插入使用頻率比較高的場景。
二叉樹一般采用鏈?zhǔn)酱鎯Ψ绞?#xff1a;每個結(jié)點(diǎn)包含兩個指針域,指向兩個孩子結(jié)點(diǎn),還包含一個數(shù)據(jù)域,存儲結(jié)點(diǎn)信息。
2 二叉搜索樹的算法實(shí)現(xiàn)
2.1 節(jié)點(diǎn)結(jié)構(gòu)體的定義
#define MAX_NODE 1024#define isLess(a, b) (a<b) #define isEqual(a, b) (a==b)typedef int ElemType;typedef struct _Bnode{ElemType data; //元素類型struct _Bnode *lchild, *rchild;//指向左右孩子節(jié)點(diǎn) }Bnode, *Btree;2.2 二叉搜索樹插入節(jié)點(diǎn)
將要插入的結(jié)點(diǎn) e,與節(jié)點(diǎn) root 節(jié)點(diǎn)進(jìn)行比較,若小于則去到左子樹進(jìn)行比較,若大于則去到右子樹進(jìn)行比較,重復(fù)以上操作直到找到一個空位置用于放置該新節(jié)點(diǎn)。
bool InsertBtree(Btree **root, Bnode *node){Bnode *tmp = NULL;Bnode *parent = NULL;if(!node){return false;}else {//清空新節(jié)點(diǎn)的左右子樹node->lchild = NULL;node->rchild = NULL;}if(*root){//存在根節(jié)點(diǎn)tmp= *root;}else{ //不存在根節(jié)點(diǎn)*root = node;return true;}while(tmp != NULL){parent = tmp;//保存父節(jié)點(diǎn)printf("父節(jié)點(diǎn): %d\n", parent->data);if(isLess(node->data,tmp->data)){tmp = tmp->lchild;}else{tmp = tmp->rchild;}}//若該樹為空樹,則直接將 node 放置在根節(jié)點(diǎn)上if(isLess(node->data, parent->data)){//找到空位置后,進(jìn)行插入parent->lchild = node;}else{parent->rchild = node;}return true; }2.3 二叉搜索樹刪除結(jié)點(diǎn)
將要刪除的節(jié)點(diǎn)的值,與節(jié)點(diǎn) root 節(jié)點(diǎn)進(jìn)行比較,若小于則去到左子樹進(jìn)行比較,若大于則去到右子樹進(jìn)行比較,重復(fù)以上操作直到找到一個節(jié)點(diǎn)的值等于刪除的值,則將此節(jié)點(diǎn)刪除。刪除時有 4 中情況須分別處理:
代碼如下:
/************************ * 采用二叉搜索樹上最大的結(jié)點(diǎn) *************************/ int findMax(Btree* root) {assert(root!=NULL);//方式一 采用遞歸/*if(root->rchild==NULL){return root->data;}return findMax(root->rchild);*///方式二 采用循環(huán)while(root->rchild){root = root->rchild;}return root->data; }/************************ * 采用遞歸方式刪除結(jié)點(diǎn) *************************/```c Btree* DeleteNode(Btree* root, int key) {if(root==NULL)return NULL;//沒有找到刪除節(jié)點(diǎn)if(root->data > key){root->lchild = DeleteNode(root->lchild, key);return root;}if(root->data < key){root->rchild = DeleteNode(root->rchild, key);return root;}//刪除節(jié)點(diǎn)不存在左右子節(jié)點(diǎn),即為葉子節(jié)點(diǎn),直接刪除if(root->lchild==NULL && root->rchild==NULL)return NULL;//刪除節(jié)點(diǎn)只存在右子節(jié)點(diǎn),直接用右子節(jié)點(diǎn)取代刪除節(jié)點(diǎn)if(root->lchild==NULL && root->rchild!=NULL)return root->rchild;//刪除節(jié)點(diǎn)只存在左子節(jié)點(diǎn),直接用左子節(jié)點(diǎn)取代刪除節(jié)點(diǎn)if(root->lchild!=NULL && root->rchild==NULL)return root->lchild;//刪除節(jié)點(diǎn)存在左右子節(jié)點(diǎn),直接用左子節(jié)點(diǎn)最大值取代刪除節(jié)點(diǎn)int val = findMax(root->lchild);root->data=val;root->lchild = DeleteNode(root->lchild,val);return root; }2.4 二叉搜索樹搜索
/************************ * 采用遞歸方式查找結(jié)點(diǎn) *************************/ Bnode* queryByRec(Btree *root, ElemType e){if (root == NULL || isEqual(root->data, e)){return root;} else if(isLess(e, root->data)) {return queryByRec(root->lchild, e);} else {return queryByRec(root->rchild, e);} }/** * 使用非遞歸方式查找結(jié)點(diǎn) */ Bnode* queryByLoop(Bnode *root, int e){while(root != NULL && !isEqual(root->data, e)){if(isLess(e, root->data)){root = root->lchild;}else{root = root->rchild;}}return root; }2.5 二叉搜索樹的遍歷
二叉樹的遍歷是指從根結(jié)點(diǎn)出發(fā),按照某種次序依次訪問所有結(jié)點(diǎn),使得每個結(jié)點(diǎn)僅被訪問一次。共分為四種方式:
- 前序遍歷。
- 中序遍歷。
- 后序遍歷。
- 層次遍歷。
前序遍歷 : 先訪問根節(jié)點(diǎn),然后前序遍歷左子樹,再前序遍歷右子樹。
代碼如下:
另一種版本的實(shí)現(xiàn):
void BinarySortTreePrintFrontRecursive(BinarySortNode* root) {if (!root) return;cout << root->e << endl;BinarySortTreePrintFrontRecursive(root->lChild);BinarySortTreePrintFrontRecursive(root->rChild); }void BinarySortTreePrintFront(BinarySortTree* tree) {if (!tree) return;BinarySortNode* node = tree;stack<BinarySortNode*> nodeStack;while ((node != NULL) || (nodeStack.size() > 0)){if (node != NULL){cout << node->e << endl;nodeStack.push(node);node = node->lChild;}else{node = nodeStack.top();nodeStack.pop();node = node->rChild;}} }中序遍歷: 先訪問根節(jié)點(diǎn)的左子樹,然后訪問根節(jié)點(diǎn),最后遍歷右子樹。
后序遍歷: 從左到右,先葉子后節(jié)點(diǎn)的方式遍歷訪問左右子樹,最后訪問根節(jié)點(diǎn)。
void BinarySortTreePrintTailRecursive(BinarySortNode* root) {if (!root) return;BinarySortTreePrintTailRecursive(root->lChild);BinarySortTreePrintTailRecursive(root->rChild);cout << root->e << endl; }void BinarySortTreePrintTail(BinarySortNode* tree) {if (!tree) return;BinarySortNode* node = tree;stack<BinarySortNode*> nodeStack;stack<BinarySortNode*> output;while ((node != NULL) || (nodeStack.size() > 0)){if (node != NULL){nodeStack.push(node);output.push(node);node = node->rChild;}else{node = nodeStack.top();nodeStack.pop();node = node->lChild;}}while (!output.empty()){cout << output.top()->e << endl;output.pop();} }層序遍歷: 從根節(jié)點(diǎn)從上往下逐層遍歷,在同一層,按從左到右的順序?qū)?jié)點(diǎn)逐個訪問。
void BinarySortTreePrintLevel(BinarySortNode* tree) {if (!tree) return;BinarySortNode* node = tree;queue<BinarySortNode*> nodeQueue;nodeQueue.push(node);while (!nodeQueue.empty()){node = nodeQueue.front();nodeQueue.pop();cout << node->e << endl;if (node->lChild != NULL){nodeQueue.push(node->lChild);}if (node->rChild != NULL){nodeQueue.push(node->rChild);}} }參考資料:
總結(jié)
以上是生活随笔為你收集整理的二叉搜索树的算法实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用邮箱实现多事件的单向同步
- 下一篇: 解析主分区数量