查找(3)---二叉查找树

来源:互联网 发布:科讯cms从入门到精通 编辑:程序博客网 时间:2024/05/18 22:54

二叉查找树

一种能够将链表插入的灵活性和有序数组查找的高效性结合起来的符号表。使用每个结点含有两个链接的二叉树来高效的实现符号表,这也是计算机科学中最重要的算法之一。

定义:二叉查找树,

1)是一棵树

2)没个结点都包含一个键值对

3)每个结点的键都大于其左子树的键,且小于右子树的键。


代码:

class Node{friend class BST;string key;//键string data;//值Node* left;//左孩子Node* right;//右孩子int N;//以该结点为根的子结点树Node(string key,string item,int N):key(key), data(item), N(N){}};class BST{public:int size(){ return size(root); }string get(string key){ return get(root, key); }void put(string key, string item)//插入{root = put(root, key, item);}string min(){ return min(root)->key; }//最小键string max(){ return max(root)->key; }//最大键string floor(string key)//向下取整{//小于等于他的键中的最大键Node* x = floor(root, key);return x ? x->key : "No key";}string ceiling(string key)//向上取整{//大于等于他的键中的最小键Node* x = ceiling(root, key);return x ? x->key : "No key";}string select(int k)//排名第k个键(从0开始){ return  select(root, k) ? select(root, k)->key : "No key";}int rank(string key){ return rank(root, key); }void deletemin(){ root = deletemin(root); }//删除void deletemax(){ root = deletemax(root); }void delete_1(string key){ root = delete_1(root, key); }queue<string> keys(string lo, string hi)//查询键的集合{queue<string> q;keys(root, q, lo, hi);return q;}private:Node* root = NULL;//根结点//工具函数int size(Node* x){ return x ? x->N : 0; }string get(Node* x, string key);Node* put(Node* x, string key, string item);Node* min(Node* x){ return x->left ? min(x->left) : x; }Node* max(Node* x){ return x->right ? max(x->right) : x; }Node* floor(Node* x, string key);Node* ceiling(Node* x, string key);Node* select(Node* x, int k);int rank(Node* x, string key);Node* deletemin(Node* x);Node* deletemax(Node* x);Node* delete_1(Node* x, string key);void keys(Node* x, queue<string>& q, string lo, string hi);};string BST::get(Node* x, string key)//查找{if (!x)return "No key";if (key < x->key)return get(x->left, key);else if (key > x->key)return get(x->right, key);else return x->data;}Node* BST::put(Node* x, string key, string item)//插入{if (!x)return new Node(key, item, 1);if (key < x->key)x->left = put(x->left, key, item);else if (key > x->key)x->right = put(x->right, key, item);else x->data = item;x->N = size(x->left) + size(x->right) + 1;return x;}Node* BST::floor(Node* x, string key){if (!x)return NULL;if (key == x->key)return x;if (key < x->key)return floor(x->left, key);Node* t = floor(x->right, key);return t ? t : x;}Node* BST::ceiling(Node* x, string key){if (!x)return NULL;if (key == x->key)return x;if (key > x->key)return ceiling(x->right, key);Node* t = ceiling(x->left, key);return t ? t : x;}Node* BST::select(Node* x, int k){if (!x)return NULL;int t = size(x->left);if (t > k)return select(x->left, k);else if (t < k)return select(x->right, k - t - 1);else return x;}int BST::rank(Node* x, string key)//求排名{if (!x)return 0;if (key < x->key)return rank(x->left,key);else if (key > x->key)return rank(x->right, key) + size(x->left) + 1;else return size(x->left);}Node* BST::deletemin(Node* x)//删除最小值{if (!x->left){Node* t = x->right;delete x;return t;}x->left = deletemin(x->left);x->N = size(x->left) + size(x->right) + 1;return x;}Node* BST::deletemax(Node* x){if (!x->right){Node* t = x->left;delete x;return t;}x->right = deletemax(x->right);x->N = size(x->left) + size(x->right) + 1;return x;}Node* BST::delete_1(Node* x, string key){if (!x)return NULL;if (key < x->key) x->left = delete_1(x->left, key);else if (key > x->key) x->right = delete_1(x->right, key);else{if (!x->right)//不存在右子树{Node* t = x->left; delete x;return t;}if (!x->left)//不存在左子树{Node* t = x->right; delete x;return t;}Node* t = x;x = min(t->right);//将右子树中的最小值做为新的根结点x->right = deletemin(t->right);x->left = t->left;}x->N = size(x->left) + size(x->right) + 1;return x;}void BST::keys(Node* x, queue<string> &q,string lo, string hi){if (!x)return;if (lo < x->key)keys(x->left, q, lo, hi);if (lo <= x->key&&hi >= x->key)q.push(x->key);if (hi > x->key)keys(x->right, q, lo, hi);}


过程中出现的问题:

1.在递归情况下结果的返回:

有两种情况

1)是直接返回到return中,如get函数

if (key < x->key)return get(x->left, key);
2)是返回到左右子树中,如put函数

if (key < x->key)x->left = put(x->left, key, item);

两种情况的区别在于,函数的返回值不同,get函数需要的是结点中的一个数据,floor函数需要的是一个结点,所以他们可以直接将找到的结点直接传递出来;而put函数中,需要传递出来一棵完整的树,所以要先将插入后的子树返回原来的位置。


2.向下取整的具体步骤:

向下取整的意思是返回小于等于他的元素中最大的那一个,所以步骤应为:

1)判断查询键是否等于结点键,是就直接返回

2)判断查询键是否小于结点键,是查询结点的左子树

3)如果通过1.2.则结点键小于查询键且为第一个被查询的键,所以此时结点右子树中最大的键,即为所查键,最后递归查找结点的右子树直到为空

4)结点右子树不为空,返回右子树,否则返回结点


3.删除操作:

1)递归查找键的位置

2)删除键:

左子树为空,则记录右子树,删除结点,返回右子树;

右子树为空,则记录左子树,删除结点,返回左左树;

记录结点,将结点右子树中的最小值赋给结点,然后删除原结点右子树的最小值,接着将左右子树赋值给结点,最后删除原结点。


性能分析

树的高度决定了所有操作在最坏情况下的性能。(范围查找除外,他的额外成本和返回的键的数量成正比)

在一棵二叉查找树中,所有操作在最坏情况下所需的时间都和树的高度成正比。

由此可知,二叉查找树的基本实现的良好性能依赖于其中的键的分布足够随机以消除长路径。对于快排我们可以随机元素,但是先后插入的树我们无能为力。为了解决这个问题,也就有了一种改进的数据结构,平衡二叉树。在下一节中会进行介绍。




0 0
原创粉丝点击