二分搜索树的实现以及各种操作(支持重复节点)
来源:互联网 发布:芒果tv会员账号淘宝 编辑:程序博客网 时间:2024/06/07 16:53
二分搜索树
一、 二分搜索树的概念
二分搜索树就是左子树的键值小于根节点,右子树键值大于根节点的树。而且左右子树是递归定义。简单来说就是任意节点左子树的树。这里主要讨论的是二分搜索树也就是二叉树。其应用在于查找表,以及字典结构的时候。其查找速度非常之快,而且可以用其求各种算法例如max ,min ,rank 等等。
例如这就是一个简单的二分搜搜树
二、为什么要有搜索二叉树
最简单的查找算法就是暴力查找,从待查找序列从头遍历到尾,与查找的key进行对比。
int Search(int *Arr,int n,int key){ for(int i=0;i<n;i++){ if(Arr[i]==key) return i; } return -1;}
这种算法简单暴力,无论查找,插入,删除操作时间复杂度都在O(n),但是这不一定是最好的。在数据量很大的情况此算法显得有点“蠢”,于是人们想到了二分查找(不是二分搜索树)。
也就是每次查找数组的mid
int biSearch(int *Arr,int n,int key){ int l,r; l=0;r=n-1; while (l<=r) { int mid=l+(r-l)/2;// 防止 r,和m足够大越界问题, if(Arr[mid]==key){// 等于就返回其下标。 return mid; } else if(Arr[mid]>key){ r=mid-1; //在左侧进行查找 } else{ l=mid+1; //在右侧进行查找 } } return -1;}
以上代码是典型的二分查找,在查找方面时间复杂度可以降低到O(logn)级别,但是要求数组一定有序,不是有序的没有办法查找。并且其数据结构为顺序表,也就是说其插入和删除操作时间复杂度还是O(n)级别。那么有没有更优秀的算法使其搜索的时间复杂度为O(logn),插入和删除操作也为O(logn)呢,答案是肯定的,也就是今天的主角。二分搜索树。
三、二分搜索树
3.1 二分搜索树的定义
为了充分体现二分搜索树的优势和使用技巧,这里所做的二分搜索树是一个字典样式的树,也就是说其有key值,和value值。并且其用的是树的链表法去存储,所以也有节点这个概念。节点定义特别很简单,跟普通的树是一样的。
struct Node{ int value; int key; int frequency; // 本次做的二分搜索树是支持重复节点的,这个是记录该节点的重复次数 Node *left; Node *right; Node(int key ,int value){ this->value=value; this->key=key; this->left=this->right=NULL; this->frequency=1; }//构造函数 };
为了方便操作在这里构建的一个BST的类。
class BST{private: struct Node{ int value; int key; int frequency; Node *left; Node *right; Node(int key ,int value){ this->value=value; this->key=key; this->left=this->right=NULL; this->frequency=1; }//节点构造函数 }; Node *root; //root为根 int count; // count记录的shi当前树中拥有节点的个数public: BST(){ root=NULL; count=0; } //BST树的构造函数};
各位看客朋友请好好看看上面类的结构,接下来的所有操作都是基于上述类的。
3.2 二分搜索树的建立
其实树的建立就是一个插入过程,每次插入都不改变其树的性质和结构。
这里利用的树的天然递归性质进行插入的操作。
Node *insert(Node *node,int key,int value){ if(node==NULL){ count++; return new Node(key,value); //如果没有这个节点就用结构体的构造方法,new一个节点。 } if(node->key< key){ node->right=insert(node->right,key,value); //右边去看看 } else if(node->key>key){ node->left=insert(node->left,key,value); //左边去看看 } else if(node->key==key) // 因为允许重复key值的插入,所以有重复key值的时候要更新节点信息 { node->value=value; //可以不要 node->frequency++; // 该节点的频度要++ } return node; //返回的就是这个数的根 }
在类的public中定义一个insert 函数,让这个二分搜索树的根传入进去即可。
void insert(int key,int value){ root=insert(root,key,value); }
大功告成,这样就能做出来一个二分搜索树。
3.3 二分搜索树的搜索操作
跟顺序表的二分搜索一样。废话不多说。直接贴代码
int search(Node *node,int key){ if(node!=NULL ) { if(node->key==key){ return node->value; } else if(node->key>key){ return search(node->left,key); } else { return search(node->right,key); } } return -1; }
对应着在public 中定义一个search方法
int search(int key){ return search(root,key); }
是不是优雅高效?复制即可用。
3.3 二分搜索树的删除
这一步就麻烦了。
分几种情况。
a. 没有左孩子
遇到这种情况怎么办呢
其实很简单只需要用 这个节点的右孩子代替其位置即可。
于是有了
if(node->left==NULL){ Node *rightNode=node->right; //拿到右孩子 delete node; //大胆删除节点 count--; // 数量-- return rightNode; //将其返回 }
b.没有右孩子
跟上面的思路一样
if(node->right==NULL){ Node *leftNode=node->left; //拿到左孩子 delete node; count--; return leftNode; }
c. 有左孩子也有右孩子
这个时候怎么办呢,其实只要找到右子树上最小的那个节点代替即可。
那么怎么去找这个最小的节点呢?很容易发现其实这个节点就是右子树最左边的那个节点。那么这个节点有什么特性呢?很明显这个节点是没有左孩子。
Node* minimun(Node *node){ if(node->left==NULL){ return node; // 成功找到这个节点 } return minimun(node->left); }
找到这个节点后就要想办法把它拿上来,并且被删除那个节点的左孩子和右孩子都要给他。
Node *s=new Node(minimun(node->right)); //用s复制该节点的右子树的最小值的那个节点 count++; s->right=removeMin(node->right);//把右子树那个最小值删掉并且做s的右子树 s->left=node->left; delete node; count--; return s;
细心的同学会发现 这里使用了Node(Node *node)这样的构造函数,这是在类中没有的,所以我们要在类中定义一个重载的节点构造函数。(仅仅是复制而已!)
Node(Node *node){ this->value=node->value; this->key=node->key; this->right=node->right; this->left=node->left; this->frequency=node->frequency; }
好了不要晕,这里贴出完整代码
Node *remove(Node *node,int key){ if(node==NULL){ return NULL; } if(node->key==key){ //找到这个节点 if(node->left==NULL){// 情况a Node *rightNode=node->right; delete node; count--; return rightNode; } if(node->right==NULL){ //情况b Node *leftNode=node->left; delete node; count--; return leftNode; } // node->left!=NULL && node->right!=NULL 情况c Node *s=new Node(minimun(node->right)); //用s复制该节点的右子树的最小值的那个节点 count++; s->right=removeMin(node->right);//把右子树那个最小值删掉并且做s的右子树 s->left=node->left; delete node; count--; return s; } else if(node->key>key){ //左边找找看 node->left=remove(node->left,key); return node; } else{ //右边找找看 node->right=remove(node->right,key); return node; } }
相应在类中加入这个remove函数
void remove(int key){ root=remove(root,key); } // 简单粗暴
这里基本操作讲完了,也就是插入,查找,删除。其时间复杂度都为O(logn)。
四、其他操作
这里贴出整个类的代码以及测试例子,供大家观赏。
#include <iostream>#include <queue>#include <ctime>using namespace std;//template <typename Value,typename Key>class BST{private: struct Node{ int value; int key; int frequency; Node *left; Node *right; Node(int key ,int value){ this->value=value; this->key=key; this->left=this->right=NULL; this->frequency=1; }//构造函数 Node(Node *node){ this->value=node->value; this->key=node->key; this->right=node->right; this->left=node->left; this->frequency=node->frequency; } }; Node *root; int count;public: BST(){ root=NULL; count=0; } ~BST(){ cout<<"已经销毁"<<endl; destroy(root); } int size(){ return count; } bool isempty(){ return count==0; } void insert(int key,int value){ root=insert(root,key,value); } void preOrder(){ preOrder(root); } void inOrder(){ inOrder(root); } void postOrder(){ postOrder(root); } int search(int key){ return search(root,key); } void leverOrder(){ levelOrder(root); } bool contain(int key){ return contain(root,key); } int getFrequency(int key){ return getFrequency(root,key); } int getMin(){ return getMin(root); } int getMax(){ return getMax(root); } void removeMin(){ if(root!=NULL){ root=removeMin(root); } } void removeMax(){ if(root!=NULL){ root=removeMax(root); } } void remove(int key){ root=remove(root,key); }private: Node *insert(Node *node,int key,int value){ if(node==NULL){ count++; return new Node(key,value); } if(node->key< key){ node->right=insert(node->right,key,value); } else if(node->key>key){ node->left=insert(node->left,key,value); } else if(node->key==key) { node->value=value; node->frequency++; } return node; } void preOrder(Node *node){ if(node!=NULL){ cout<<node->key<<endl; preOrder(node->left); preOrder(node->right); } } void inOrder(Node *node){ if(node!=NULL){ inOrder(node->left); cout<<node->key<<endl; inOrder(node->right); } } void postOrder(Node *node){ if(node!=NULL){ postOrder(node->left); postOrder(node->right); cout<<node->key<<endl; } } void levelOrder(Node *node){ queue<Node *>que; que.push(node); while (!que.empty()){ Node *p=que.front(); que.pop(); cout<<p->key<<endl; if(p->left!=NULL){ que.push(p->left); } if(p->right!=NULL){ que.push(p->right); } } } int search(Node *node,int key){ if(node!=NULL ) { if(node->key==key){ return node->value; } else if(node->key>key){ return search(node->left,key); } else { return search(node->right,key); } } return -1; } int getFrequency(Node *node,int key){ if(node!=NULL ) { if(node->key==key){ return node->frequency; } else if(node->key>key){ return getFrequency(node->left,key); } else { return getFrequency(node->right,key); } } return -1; } bool contain(Node *node,int key){ if(node!=NULL) { if(node->key==key){ return true; } else if(node->key>key){ return contain(node->left,key); } else{ return contain(node->right,key); } } return false; } void destroy(Node *node){ if(node!=NULL){ destroy(node->left); destroy(node->right); delete node; count--; } } Node* minimun(Node *node){ if(node->left==NULL){ return node; } return minimun(node->left); } Node* maxmun(Node *node){ if(node->right==NULL){ return node; } return maxmun(node->right); } int getMin(Node *node){ if(node->left==NULL){ return node->key; } return getMin(node->left); } int getMax(Node *node){ if(node->right==NULL){ return node->key; } return getMax(node->right); } Node *removeMin(Node *node){ if(node->left==NULL){ Node *rightNode=node->right; delete node; count--; return rightNode; } else { node->left=removeMin(node->left); return node; } } Node *removeMax(Node *node){ if(node->right==NULL){ Node *leftNode=node->left; delete node; count--; return leftNode; } else { node->right=removeMax(node->right); return node; } } Node *remove(Node *node,int key){ if(node==NULL){ return NULL; } if(node->key==key){ if(node->left==NULL){ Node *rightNode=node->right; delete node; count--; return rightNode; } if(node->right==NULL){ Node *leftNode=node->left; delete node; count--; return leftNode; } // node->left!=NULL && node->right!=NULL Node *s=new Node(minimun(node->right)); //用s复制该节点的右子树的最小值的那个节点 count++; s->right=removeMin(node->right);//把右子树那个最小值删掉并且做s的右子树 s->left=node->left; delete node; count--; return s; } else if(node->key>key){ node->left=remove(node->left,key); return node; } else{ node->right=remove(node->right,key); return node; } }};int main() { BST bst; srand(time(NULL)); int a[7]={5,3,3,3,7,6,8}; int n=10; for(int i=0;i<7;i++){// int key=rand()%n; int key=a[i]; int value=2*key; bst.insert(key,value); cout<<key<<" "; } cout<<"\n"<<"树的大小为:"<<bst.size()<<endl; cout<<"\n\n"<<"先序遍历为:\n";// cout<<bst.size()<<endl; bst.preOrder(); cout<<"中序遍历:\n"; bst.inOrder(); cout<<"后序遍历:\n"; bst.postOrder(); cout<<"层次遍历:\n"; bst.leverOrder(); cout<<"\n"<<bst.getFrequency(3)<<endl; cout<<bst.getMin()<<endl; cout<<bst.getMax()<<endl; bst.remove(3); cout<<bst.size()<<" "<<bst.getMax()<<endl; bst.preOrder(); cout<<"中序遍历:\n"; bst.inOrder(); cout<<"后序遍历:\n"; bst.postOrder(); cout<<"层次遍历:\n"; bst.leverOrder(); return 0;}
上面所有操作都封装很好,可供大家二次开发,如果各位觉得有用复制之后点个赞哦!!!
- 二分搜索树的实现以及各种操作(支持重复节点)
- 二分搜索树的删除节点操作
- 二叉树的定义,节点的增删改查以及父节点、子节点的各种操作。
- 5-3 二分搜索树的节点插入(插入新的节点)
- 二分搜索树的实现
- 二分的各种操作
- 二分搜索(折半搜索)的实现
- 二分查找的各种情况实现以及一些注意点
- python实现二分搜索树以及改进后的AVL Tree
- 二叉搜索树各种算法的实现
- 二分搜索的java实现
- 递归实现的二分搜索
- 二分搜索算法的实现
- 自己实现的二分搜索
- ES节点类型以及各种节点的分工:
- 删除链表中重复的节点(Java实现)
- java语言实现的二叉树的各种操作(包括递归与非递归遍历二叉树,求二叉树的高度,节点总数,叶子节点等)
- 二分搜索算法(折半查找)原理以及递归(recuition),迭代(iteration)的两种实现源代码
- 合并排序的递归算法
- 浏览器渲染引擎
- 推荐系统之协同过滤(CF)算法
- 2017算法课.09(Is Subsequence)
- vb.net 教程 3-3 窗体编程 输入对话框 2
- 二分搜索树的实现以及各种操作(支持重复节点)
- 【Java】大文本字符串滤重的简单方案~
- AngularJs学习笔记(2)--控制器和ng-bind
- 用户画像设计总结
- 三十七将项目四 大奖赛计分
- Linux内核学习总结
- 事务的传播行为(讲得比较好)
- 在ROS上发布传感器流
- Hadoop 在分机上启动常见问题