二叉查找树(BST)
2017-01-15 12:43
274 查看
二叉查找树应该是最最基本的树,其基本原理较简单:
1、每一个节点最多有两个子节点
2、它的左子节点肯定比它小
3、它的右子节点肯定比他大
可以了。
下面直接上代码:
1、首先是头文件b.h:
2、然后是二叉查找树的功能实现b_func.h:
2.1、首先是二叉查找树的创建和销毁
2.2、二叉查找树的插入新节点的方法:
插入的平均时间复杂度在O(logN),但如果不是很平衡,比如这样一个二叉查找树,那就是O(N)了:
1-2-3-4-5-6-7-8-9,这是插入10,很明显10的插入过程,需要从1走到9
即平均时间复杂度O(logN),最坏情况为O(N)
二叉查找树完全没有考虑如何尽可能平衡的问题,所以可以讲除教学以外,毫无实际用途。
2.3、二叉查找树的查找方法:
2.4、二叉查找树的节点删除方法:
删除操作确实是比插入/查找多了找替代者节点的过程,但就总体而言时间复杂度实质上和查找/插入也差不多,平均在O(logN)最坏在O(N)
2.5、前中后序遍历
3、测试程序test_b.cpp:
g++ -g test_b.cpp -o test_b
对于二叉查找树及其所有衍生品,越平衡,增删查改的时间复杂度越往O(logN)的方向去发展,反之则往O(N)的方向去发展,所以其所有衍生品都是致力于如何在代价不太高的情况下做到尽可能平衡。比如比较极端的avl数和比较合理的红黑树。
有了二叉查找树的概念,接下来介绍的首先是二叉堆,这个可以解决不少日常实际问题的东西。
1、每一个节点最多有两个子节点
2、它的左子节点肯定比它小
3、它的右子节点肯定比他大
可以了。
下面直接上代码:
1、首先是头文件b.h:
//1.1、结构体nd是节点的数据结构,包括值(data)、左右子节点
template<class T> struct nd { T data; struct nd *lchild; struct nd *rchild; nd (T _data, struct nd *_lchild, struct nd *_rchild): data(_data), lchild(_lchild), rchild(_rchild) {} };
//1.2、btree是二叉查找树的数据结构,包括插入、删除、查找三方法,以及前中后遍历方法 template <class T> class btree { nd<T> *root; nd<T> *getNearest(nd<T> *nodae); void show(T data); void free(nd<T> *node); void swap(nd<T> *node1, nd<T> *node2); void pre(nd<T> *root); void mid(nd<T> *root); void post(nd<T> *root); public: btree(){} btree(T *data, int size); ~btree(); int find(T data); void insert(T data); void del(T data); void preshow(); void midshow(); void postshow(); };
2、然后是二叉查找树的功能实现b_func.h:
2.1、首先是二叉查找树的创建和销毁
#include "b.h" #include <iostream>
//构造二叉查找树,就是一个不断插入的过程,insert方法后面介绍 template<class T> btree<T>::btree (T *data, int size) { root = new nd<T>(data[0], 0, 0); for (int i = 1; i < size; i++) { T curdata = data[i]; insert(curdata); } }
//销毁二叉查找树,按后序遍历删除,所有二叉树的销毁必然都是这样的顺序 template<class T> void btree<T>::free (nd<T> *node) { if (!node->lchild && !node->rchild) { delete node; node = 0; return; } if (node->lchild) { free(node->lchild); } if (node->rchild) { free(node->rchild); } } template<class T> btree<T>::~btree () { free(root); }
2.2、二叉查找树的插入新节点的方法:
//按新节点的大小找到合适的坑
template<class T> void btree<T>::insert (T data) { nd<T> *node = root; while (node) { if (node->data == data) { break; } else if (node->data < data) { if (node->rchild) { node = node->rchild; } else { node->rchild = new nd<T>(data, 0, 0); break; } } else if (node->data > data) { if (node->lchild) { node = node->lchild; } else { node->lchild = new nd<T>(data, 0, 0); break; } } } }
插入的平均时间复杂度在O(logN),但如果不是很平衡,比如这样一个二叉查找树,那就是O(N)了:
1-2-3-4-5-6-7-8-9,这是插入10,很明显10的插入过程,需要从1走到9
即平均时间复杂度O(logN),最坏情况为O(N)
二叉查找树完全没有考虑如何尽可能平衡的问题,所以可以讲除教学以外,毫无实际用途。
2.3、二叉查找树的查找方法:
template<class T> int btree<T>::find (T data) { nd<T> *node = root; if (root) { while (node) { if (node->data == data) { return 1; } else if (node->data > data) { if (node->lchild) { node = node->lchild; } else { return 0; } } else if (node->data < data) { if (node->rchild) { node = node->rchild; } else { return 0; } } } } return 0; }二叉查找树的查找,和插入完全没有实质区别,时间复杂度完全一模一样。
2.4、二叉查找树的节点删除方法:
//swap方法,交换两个节点的值
template<class T> void btree<T>::swap (nd<T> *node1, nd<T> *node2) { T tmp = node1->data; node1->data = node2->data; node2->data = tmp; }
//寻找和节点node的值最接近的。什么是最接近的,如果节点node有右子节点,那么就优先选右子树中,最小的节点;
//如果node没有右子节点了,但是有左子节点,那么找到左子树中,最大的节点。这就是离它最近的节点。
//如果node既没有左子节点又没有右子节点,那么是不会调用这个方法的 template<class T> nd<T> *btree<T>::getNearest (nd<T> *node) { if (node->rchild) { nd<T> *right = node->rchild, *lchild = right->lchild; while (right && lchild) { if (!lchild->lchild) { break; } right = lchild; lchild = lchild->lchild; } return right; } else { nd<T> *left = node->lchild, *rchild = left->rchild; while (left && rchild) { if (!rchild->rchild) { break; } left = rchild; rchild = rchild->rchild; } return left; } }
//不仅是二叉查找树,所有的树种删除节点的方式,都
//是首先寻找被删除节点的最优替代者节点,然后把替代者节点和要删的节点交换值,然后实际删除替代者节点,而不是实际删除要删的节点,这是一个重要技巧。
//1、首先找到要删除的节点是哪个,如果都没有找到那么可以直接返回了
//2、如果要删除的节点,既没有左子节点也没有右子节点,那么这肯定是个叶子节点,直接删除即可
//3、如果要删除的节点,有右子节点,那么从右子树中找到最小的节点,这个就是它的替代者节点(右子树中比要删除节点大的节点中最小的节点),交换值后实际删除替代者节点
// 注意这里的判断逻辑比较细一点,要判断替代者节点是否就是要删除节点的右子节点,包括下面的判断左子树也有这个逻辑
//4、如果要删除的节点,没有右子节点但是有左子节点,那么从左子树中找到最大的节点,这个就是它的替代者节点(右子树中比要删除节点小的节点中最大的节点),后面过程同第三步 template<class T> void btree<T>::del (T data) { nd<T> *node = root, *parent = root; if (!node) { return; } while (node) { if (node->data == data) { break; } else if (node->data > data) { if (node->lchild) { parent = node; node = node->lchild; } else { return; } } else if (node->data < data) { if (node->rchild) { parent = node; node = node->rchild; } else { return; } } } if (!node->lchild && !node->rchild) { if (parent->lchild == node) { parent->lchild = 0; } else { parent->rchild = 0; } delete node; } else if (node->rchild) { nd<T> *nearest = getNearest(node), *delnode = 0; if (nearest == node->rchild) { delnode = nearest; node->rchild = delnode->rchild; node->data = delnode->data; delete delnode; } else { delnode = nearest->lchild; node->data = delnode->data; nearest->lchild = 0; delete delnode; } } else if (node->lchild) { nd<T> *nearest = getNearest(node), *delnode = 0; if (nearest == node->lchild) { delnode = nearest; node->lchild = delnode->lchild; node->data = delnode->data; delete delnode; } else { delnode = nearest->rchild; node->data = delnode->data; nearest->rchild = 0; delete delnode; } } }
删除操作确实是比插入/查找多了找替代者节点的过程,但就总体而言时间复杂度实质上和查找/插入也差不多,平均在O(logN)最坏在O(N)
2.5、前中后序遍历
template<class T> void btree<T>::show (T data) { std::cout << data << std::endl; } template<class T> void btree<T>::pre (nd<T> *root) { if (root) { show(root->data); pre(root->lchild); pre(root->rchild); } } template<class T> void btree<T>::preshow () { std::cout << "preshow" << std::endl; pre(root); } template<class T> void btree<T>::mid (nd<T> *root) { if (root) { mid(root->lchild); show(root->data); mid(root->rchild); } } template<class T> void btree<T>::midshow () { std::cout << "midshow" << std::endl; mid(root); } template<class T> void btree<T>::post (nd<T> *root) { if (root) { post(root->lchild); post(root->rchild); show(root->data); } } template<class T> void btree<T>::postshow () { std::cout << "postshow" << std::endl; post(root); }二叉查找树及其所有衍生品的中序遍历都是有序的,这个可以作为判断程序对错的依据。
3、测试程序test_b.cpp:
g++ -g test_b.cpp -o test_b
#include "b_func.h" #include <stdlib.h> using namespace std; void initdata (int *testdata, int size) { srand((int)time(0)); for (int i = 0; i < size; i++) { testdata[i] = rand() % 100; std::cout << testdata[i]; if (i != size - 1) { std::cout << ","; } } std::cout << std::endl << "inited" << std::endl; } int main () { int testdata[100] = {0}; initdata(testdata, sizeof(testdata)/sizeof(testdata[0])); btree<int> bt(testdata, sizeof(testdata)/sizeof(testdata[0])); bt.preshow(); bt.midshow(); bt.postshow(); bt.del(testdata[5]); std::cout << "del: " << testdata[5] << std::endl; bt.preshow(); bt.midshow(); bt.postshow(); std::cout << bt.find(-1) << std::endl; return 0; }
对于二叉查找树及其所有衍生品,越平衡,增删查改的时间复杂度越往O(logN)的方向去发展,反之则往O(N)的方向去发展,所以其所有衍生品都是致力于如何在代价不太高的情况下做到尽可能平衡。比如比较极端的avl数和比较合理的红黑树。
有了二叉查找树的概念,接下来介绍的首先是二叉堆,这个可以解决不少日常实际问题的东西。
相关文章推荐
- 二叉查找树(BST)
- BST 二叉查找树的实现
- 动态查找树比较: 二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)
- BST(Binary Search Tree) 二叉查找树
- 【查找结构 2】二叉查找树 [BST]
- 树03-二叉查找树[BST]
- BST二叉查找树的实现与操作
- 二叉查找树的实现(BST)
- 常用树类数据结构总结-二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)的性能分析
- 二叉查找树(二叉排序树)BST解析
- 二叉搜索树;二叉查找树;二叉排序树;binary search tree(BST)
- Javascript数据结构算法之二叉查找树BST(构造,遍历,查找,删除,计数)
- Java实现二叉查找树(BST)
- 数据结构:二叉查找树(BST)&平衡二叉树(AVL)
- 二叉查找树(BST)
- 二叉查找树/二叉排序树/二叉搜索树----> BST
- 二叉查找树(BST)---创建 清空 遍历
- 最优二叉查找树(optimal BST)
- 查找结构 动态查找树比较 树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree)
- 二叉查找树的实现(BST)(转载自http://blog.csdn.net/collonn/article/details/18732079)