数据结构之二叉搜索树和二叉平衡树学习笔记
来源:互联网 发布:相册制作软件下载 编辑:程序博客网 时间:2024/06/07 15:15
二叉搜索树(Binary Search Tree)
具有下列性质的二叉树被称为二叉搜索树:
(1)、若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值;(2)、若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值;(3)、它的左、右子树也分别为二叉查找树。
一些典型特点:
- 而中序遍历二叉搜索树可得到一个关于key的有序序列,一个无序序列可以通过构造一棵二叉搜索树变成一个有序序列。
- 对二叉搜索树的搜索,插入,删除操作的次数最多不·超过等于树高,即操作的时间复杂度为O(log(n)).
搜索、插入、删除操作的算法思路:
- 搜索操作:
在搜索元素x的时候,我们可以将x和根节点比较,有以下三种情况:(1) 如果x等于根节点,那么找到x,停止搜索 (终止条件);(2) 如果x小于根节点,那么搜索左子树;(3) 如果x大于根节点,那么搜索右子树;
- 插入操作:
(1) 若二叉搜索树中存在该关键字,则不插入,所以插入的结点总是作为某一叶子节点的子结点;(2) 先检索二叉搜索树,看查找树中是否含有该关键字,若不存在,记录应当要插入位置的父节点;
- 删除:
在二叉搜索树中删除一个给定的结点p有三种情况:(1)叶节点可以直接删除;(2)结点p有左子树(右子树)之一,则把p的左子树(右子树)接到p的父节点上;(3)左右子树同时存在,则找到结点p的中序直接后继结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的右子树总最左的结点,因而s无左子树,所以直接把s的右子树接到s的父节点即可;
实现代码如下:
#include <iostream>#include <cstdio>using namespace std;enum ResultCode { Underflow, Overflow, Success, Duplicate, NotPresent };template<class T>struct BTNode{ BTNode() { lChild = rChild = NULL; } BTNode(const T& x) { element = x; lChild = rChild = NULL; } BTNode(const T& x, BTNode<T>* l, BTNode<T>* r) { element = x; lChild = l; rChild = r; } T element; BTNode<T>* lChild, *rChild;};template<class T>class BST {public: BST() { root = NULL; } bool IsEmpty()const; void Clear(); bool Root(); void MakeTree(const T& x, BST<T>& left, BST<T>& right); void BreakTree(T &x, BST<T>& left, BST<T>& right); int Size(); void PreOrder(void(*Visit)(T& x)); void InOrder(void(*Visit)(T& x)); void PostOrder(void(*Visit)(T& x)); void FreeTree(); ResultCode Search(T &x)const; ResultCode Insert(T &x); ResultCode Remove(T& x);protected: BTNode<T>* root;private: ResultCode Search(BTNode<T> *p, T& x)const; void Clear(BTNode<T>* &t); void PreOrder(void(*Visit)(T& x), BTNode<T>*t); void InOrder(void(*Visit)(T& x), BTNode<T>*t); void PostOrder(void(*Visit)(T& x), BTNode<T>*t); void FreeTree(BTNode<T>* t); int Size(BTNode<T>* t);};template <class T>bool BST<T>::Root(){ if (root) { cout << root->element << endl; return true; } else return false;}template <class T>void BST<T>::MakeTree(const T& x, BST<T>& left, BST<T>& right){ if (root || &left == &right) return; root = new BTNode<T>(x, left.root, right.root); left.root = right.root = NULL;}template <class T>void BST<T>::BreakTree(T& x, BST<T>& left, BST<T>& right){ if (!root || &left == &right || left.root || right.root) return; x = root->element; left.root = root->lChlid; right.root = root->rChild; delete root; root = NULL;}template <class T>void Visit(T &x){ cout << x << " ";}template <class T>void BST<T>::PreOrder(void(*Visit)(T& x)){ PreOrder(Visit, root);}template <class T>void BST<T>::PreOrder(void(*Visit)(T& x), BTNode<T>* t){ if (t) { Visit(t->element); PreOrder(Visit, t->lChild); PreOrder(Visit, t->rChild); }}template <class T>void BST<T>::InOrder(void(*Visit)(T& x)){ InOrder(Visit, root);}template <class T>void BST<T>::InOrder(void(*Visit)(T& x), BTNode<T>* t){ if (t) { InOrder(Visit, t->lChild); Visit(t->element); InOrder(Visit, t->rChild); }}template <class T>void BST<T>::PostOrder(void(*Visit)(T& x)){ PostOrder(Visit, root);}template <class T>void BST<T>::PostOrder(void(*Visit)(T& x), BTNode<T>* t){ if (t) { PostOrder(Visit, t->lChild); PostOrder(Visit, t->rChild); Visit(t->element); }}template <class T>int BST<T>::Size(){ return Size(root);}template <class T>int BST<T>::Size(BTNode<T>* t){ if (!t) return 0; else return Size(t->lChild) + Size(t->rChild) + 1;}template <class T>void BST<T>::FreeTree(){ return FreeTree(root);}template <class T>void BST<T>::FreeTree(BTNode<T>* t){ if (t == NULL) { return; } if (t->lChild != NULL) { FreeTree(t->lChild); t->lChild = NULL; } if (t->rChild != NULL) { FreeTree(t->rChild); t->rChild = NULL; } if (t != NULL) { free(t); t = NULL; }}template<class T>ResultCode BST<T>::Search(T &x)const{ return Search(root, x);}template<class T>ResultCode BST<T>::Search(BTNode<T>* p, T& x)const{ if (!p)return NotPresent; else if (x<p->element) return Search(p->lChild, x); else if (x>p->element) return Search(p->rChild, x); else { x = p->element; return Success; }}template<class T>ResultCode BST<T>::Insert(T& x){ BTNode<T> *p = root, *q = NULL; while (p) { q = p; if (x<p->element) p = p->lChild; else if (x>p->element)p = p->rChild; else { x = p->element; return Duplicate; } } p = new BTNode<T>(x); if (!root) root = p; else if (x<q->element) q->lChild = p; else q->rChild = p; return Success;}template<class T>ResultCode BST<T>::Remove(T &x){ BTNode<T> *c, *s, *r, *p = root, *q = NULL; while (p && p->element != x) { q = p; if (x<q->element)p = p->lChild; else p =p ->rChild; } if (!p)return NotPresent; x = p->element; if (p->lChild&&p->rChild) { s = p->rChild; r = p; while (s->lChild) { r = s; s = s->lChild; } p->element = s->element; p = s; q = r; } if (p->lChild)c = p->lChild; else c = p->rChild; if (p == root)root = c; else if (p == q->lChild) q->lChild = c; else q->rChild = c; delete p; return Success;}int main(){ BST<int> a, b, o, p, q, w, x, y, z; int size; /*下面代码建立一个形如: 4 / \ 2 6 / \ / \ 1 3 5 7 的二叉树*/ y.MakeTree(1, a, b); z.MakeTree(3, a, b); x.MakeTree(2, y, z); p.MakeTree(5, a, b); q.MakeTree(7, a, b); w.MakeTree(6, p, q); o.MakeTree(4, x, w); o.Root(); o.PreOrder(Visit); cout << endl; o.InOrder(Visit); //中序遍历当前二叉,答案应该为'1 2 3 4 5 6 7 ' cout << endl; o.PostOrder(Visit); cout << endl; size = o.Size(); cout << size << endl; int v1 = 5,v2 = 8,v3 = 4,v4=-1; if (o.Search(v1) == Success) //搜索元素5是否在树中,结果应该为Success cout << "Success" << endl; else cout << "No" << endl; if (o.Search(v2) == Success) //搜索元素8是否在树中,结果应该为No cout << "Success" << endl; else cout << "No" << endl; o.Remove(v1); //删除元素5 o.InOrder(Visit); //删除后进行中序遍历,结果应该为1,2,3,4,6,7 cout << endl; o.Remove(v3); //再删除元素4,结果应该为1,2,3,6,7 o.InOrder(Visit); cout << endl; o.Insert(v1); //插入元素5,结果应该为1,2,3,5,6,7 o.InOrder(Visit); cout << endl; o.Insert(v3); //插入元素4,结果应该为1,2,3,4,5,6,7 o.InOrder(Visit); cout << endl; o.Insert(v4); //插入元素-1,结果应该为-1,1,2,3,4,5,6,7 o.InOrder(Visit); cout << endl; return 0;}
实际运行结果如下图:
运行环境VS2015
平衡二叉树(Balanced Binary Tree)
平衡二叉树又称为AVL树
它具有以下性质:
(1)是一棵二叉搜索树(每个节点是惟一出现);(2)每个节点左右子树的高度之差(平衡因子)相差最多为1,平衡因子定义为:左子树高度 - 右子树高度;(3)左右两个子树都是一棵平衡二叉树;
- 搜索算法同上的二叉搜索树。
但是注意和二叉查找树相比,查找方法没有变法,不过根据存储的特性,AVL树能维持在一个O(logN)的稳定的时间,而二叉查找树则相当不稳定。
此处资料学习并参考自:
http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html
- 插入、删除的关键实现原理—失衡旋转:
平衡二叉树在进行插入和删除元素后,平衡性质可能要被打破,也称为失衡,所以要对树进行调整以保证平衡二叉树的平衡性质被保持住。解决的方法就是旋转。旋转又分为单旋转和双旋转,针对不同的情况选择不同的旋转方案: 对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度不平衡时,此节点的两颗子树的高度差2.容易看出,这种不平衡出现在下面四种情况:
1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。 2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。 3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。 4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。 从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。
- 单旋转
单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。图3是左左情况的解决方案,节点k2不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的左子树X子树,所以属于左左情况。
为使树恢复平衡,我们把k2变成这棵树的根节点,因为k2大于k1,把k2置于k1的右子树上,而原本在k1右子树的Y大于k1,小于k2,就把Y置于k2的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。 这样的操作只需要一部分指针改变,结果我们得到另外一颗二叉查找树,它是一棵AVL树,因为X向上一移动了一层,Y还停留在原来的层面上,Z向下移动了一层。整棵树的新高度和之前没有在左子树上插入的高度相同,插入操作使得X高度长高了。因此,由于这颗子树高度没有变化,所以通往根节点的路径就不需要继续旋转了。
- 双旋转
对于左右和右左这两种情况,单旋转不能使它达到一个平衡状态,要经过两次旋转。双旋转是针对于这两种情况的解决方案,同样的,这样两种情况也是对称的,只要解决了左右这种情况,右左就很好办了。图4是左右情况的解决方案,节点k3不满足平衡特性,因为它的左子树k1比右子树Z深2层,而且k1子树中,更深的一层的是k1的右子树k2子树,所以属于左右情况。
为使树恢复平衡,我们需要进行两步,第一步,把k1作为根,进行一次右右旋转,旋转之后就变成了左左情况,所以第二步再进行一次左左旋转,最后得到了一棵以k2为根的平衡二叉树树。
- 插入
插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。
插入代码如下:
//插入template<class T>void AVLTree<T>::insertpri(TreeNode<T>* &node,T x){ if(node==NULL)//如果节点为空,就在此节点处加入x信息 { node=new TreeNode<T>(); node->data=x; return; } if(node->data>x)//如果x小于节点的值,就继续在节点的左子树中插入x { insertpri(node->lson,x); if(2==height(node->lson)-height(node->rson)) if(x<node->lson->data) SingRotateLeft(node); else DoubleRotateLR(node); } else if(node->data<x)//如果x大于节点的值,就继续在节点的右子树中插入x { insertpri(node->rson,x); if(2==height(node->rson)-height(node->lson))//如果高度之差为2的话就失去了平衡,需要旋转 if(x>node->rson->data) SingRotateRight(node); else DoubleRotateRL(node); } node->hgt=Max(height(node->lson),height(node->rson));}
- 删除
删除的方法也和二叉查找树的一致,区别是,删除完成后,需要从删除节点的父亲开始向上维护树的平衡一直到根节点。
删除代码如下:
//删除template<class T>void AVLTree<T>::Deletepri(TreeNode<T>* &node,T x){ if(node==NULL) return ;//没有找到值是x的节点 if(x < node->data) { Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x if(2==height(node->rson)-height(node->lson)) if(node->rson->lson!=NULL&&(height(node->rson->lson)>height(node->rson->rson)) ) DoubleRotateRL(node); else SingRotateRight(node); } else if(x > node->data) { Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x if(2==height(node->lson)-height(node->rson)) if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) )) DoubleRotateLR(node); else SingRotateLeft(node); } else//如果相等,此节点就是要删除的节点 { if(node->lson&&node->rson)//此节点有两个儿子 { TreeNode<T>* temp=node->rson;//temp指向节点的右儿子 while(temp->lson!=NULL) temp=temp->lson;//找到右子树中值最小的节点 //把右子树中最小节点的值赋值给本节点 node->data=temp->data; node->freq=temp->freq; Deletepri(node->rson,temp->data);//删除右子树中最小值的节点 if(2==height(node->lson)-height(node->rson)) { if(node->lson->rson!=NULL&& (height(node->lson->rson)>height(node->lson->lson) )) DoubleRotateLR(node); else SingRotateLeft(node); } } else//此节点有1个或0个儿子 { TreeNode<T>* temp=node; if(node->lson==NULL)//有右儿子或者没有儿子 node=node->rson; else if(node->rson==NULL)//有左儿子 node=node->lson; delete(temp); temp=NULL; } } if(node==NULL) return; node->hgt=Max(height(node->lson),height(node->rson))+1; return;}
全部实现代码如下,测试见main函数:
#include <iostream>#include <algorithm>using namespace std;template <class T>struct AVLNode{ T key; int height; AVLNode(T e, AVLNode<T> *l = NULL, AVLNode<T> *r = NULL) : key(e), height(0), lChild(l), rChild(r){} AVLNode<T> *lChild, *rChild;};template <class T>class AVLTree{protected: void InOrder(AVLNode<T> *&t) const; bool Insert(AVLNode<T> *&t, T key); bool Remove(AVLNode<T> *&t, T key); void LeftRotate(AVLNode<T> *&p); void RightRotate(AVLNode<T> *&p); void PreOrder(AVLNode<T> *&t) const;public: AVLTree() { root = NULL; } ~AVLTree() { Clear(root); } int GetHeight(AVLNode<T> *t); void InOrder(); void PreOrder(); bool Insert(T key); bool Remove(T key); void Clear(AVLNode<T> *&t); AVLNode<T> *root;};template <class T>void AVLTree<T>::Clear(AVLNode<T> *&t){ if (!t) return; Clear(t->lChild); Clear(t->rChild); delete t;}template <class T>int AVLTree<T>::GetHeight(AVLNode<T> *t){ return t ? t->height : 0;}template <class T>void AVLTree<T>::PreOrder(AVLNode<T> *&t) const{ if (!t) return; cout << t->key << " "; PreOrder(t->lChild); PreOrder(t->rChild);}template <class T>void AVLTree<T>::PreOrder(){ if (!root) return; cout << "前序遍历为:" << endl; PreOrder(root); cout << endl;}template <class T>void AVLTree<T>::InOrder(AVLNode<T> *&t) const{ if (!t) return; InOrder(t->lChild); cout << t->key << " "; InOrder(t->rChild);}template <class T>void AVLTree<T>::InOrder(){ if (!root) return; cout << "中序遍历为:" << endl; InOrder(root); cout << endl;}template <class T>inline void AVLTree<T>::RightRotate(AVLNode<T> *&p){ AVLNode<T> *l = p->lChild; p->lChild = l->rChild; l->rChild = p; p->height = max(GetHeight(p->lChild), GetHeight(p->rChild)) + 1; l->height = max(GetHeight(l->lChild), p->height) + 1; p = l;}template <class T>inline void AVLTree<T>::LeftRotate(AVLNode<T> *&p){ AVLNode<T> *r = p->rChild; p->rChild = r->lChild; r->lChild = p; p->height = max(GetHeight(p->lChild), GetHeight(p->rChild)) + 1; r->height = max(GetHeight(r->rChild), p->height) + 1; p = r;}template <class T>bool AVLTree<T>::Insert(T key){ return Insert(root, key);}template <class T>bool AVLTree<T>::Insert(AVLNode<T> *&t, T key){ if (!t) { t = new AVLNode<T>(key); if (!t) return false; } if (key < t->key) { if (!Insert(t->lChild, key)) return false; if (2 == GetHeight(t->lChild) - GetHeight(t->rChild)) { if (key < t->lChild->key) { RightRotate(t); } else { LeftRotate(t->lChild); RightRotate(t); } } } else if (key > t->key) { if (!Insert(t->rChild, key)) return false; if (2 == GetHeight(t->rChild) - GetHeight(t->lChild)) { if (key > t->rChild->key) { LeftRotate(t); } else { RightRotate(t->rChild); LeftRotate(t); } } } t->height = max(GetHeight(t->lChild), GetHeight(t->rChild)) + 1; return true;}template <class T>bool AVLTree<T>::Remove(T key){ return Remove(root, key);}template <class T>bool AVLTree<T>::Remove(AVLNode<T> *&t, T key){ if (!t) return false; if (key < t->key) { if (!Remove(t->lChild, key)) return false; if (GetHeight(t->rChild) - GetHeight(t->lChild) == 2) { AVLNode<T> *r = t->rChild; if (GetHeight(r->lChild) > GetHeight(r->rChild)) { RightRotate(t->rChild); LeftRotate(t); } else RightRotate(t); } } else if (key > t->key) { if (!Remove(t->rChild, key)) return false; if (GetHeight(t->lChild) - GetHeight(t->rChild) == 2) { AVLNode<T> *l = t->lChild; if (GetHeight(l->rChild) > GetHeight(l->lChild)) { LeftRotate(t->lChild); RightRotate(t); } else LeftRotate(t); } } else { if (t->lChild && t->rChild) { if (GetHeight(t->lChild) > GetHeight(t->rChild)) { AVLNode<T> *s = t->lChild; //while (s->lChild) //s = s->lChild; t->key = s->key; Remove(t->lChild, s->key); } else { AVLNode<T> *s = t->rChild; //while (s->lChild L) // s = s->lChild; t->key = s->key; Remove(t->rChild, s->key); } } else { AVLNode<T> *p = t; t = t->lChild ? t->lChild : t->rChild; delete p; } } return true;}int main(void){ AVLTree<int> T; for (int i = 1; i <= 7; i++) T.Insert(i); /*下面代码建立一个形如: 4 / \ 2 6 / \ / \ 1 3 5 7的二叉树*/ T.InOrder(); // 进行中序遍历,结果应该为:1,2,3,4,5,6,7 cout << endl; T.PreOrder(); //进行先序遍历,结果应为:4,2,1,3,6,5,7 cout << endl; T.Remove(5); //移除元素5,再进行中序遍历,结果应为:1,2,3,4,6,7 T.InOrder(); T.Insert(8); //插入元素8 T.InOrder(); //此时要经过失衡旋转,再进行中序遍历结果应为:1,2,3,4,6,7,8 cout << endl; T.PreOrder(); //进行先序遍历,结果应为:4,2,1,3,7,6,8 cout << endl; T.Insert(9); //在插入元素9,进行中序遍历,结果应为:1,2,3,4,6,7,8,9 T.InOrder(); cout << endl; bool v = T.Remove(9); //删除元素9,再进行中序遍历,结果应为:1,2,3,4,6,7,8 T.InOrder(); cout << endl; T.PreOrder(); //再进行先序遍历,结果应为:4,2,1,3,7,6,8 cout << endl; cout << endl; return 0;}
运行结果如下,运行环境为VS2015:
学习及资料参考:
《数据结构—使用C++语言描述》(第2版) 人民邮电出版社 陈慧南
《数据结构(C++语言版)》(第3版)清华大学出版社 邓俊辉
C小加博客之一步一步写平衡二叉树(AVL树)http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html
0 0
- 数据结构之二叉搜索树和二叉平衡树学习笔记
- 数据结构 学习笔记(五):树(中):二叉搜索树,平衡二叉树
- 数据结构学习笔记-平衡二叉树实现
- 搜索二叉树和平衡二叉树
- 数据结构学习-平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之 平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之平衡二叉树
- 数据结构之动态搜索&平衡二叉树(ADT)
- 数据结构-平衡搜索二叉树(AVL树)
- 数据结构——二叉搜索平衡树
- 平衡二叉搜索树
- 数据结构 - 平衡二叉树
- 命名空间
- 《大话设计模式》-- 第24个 职责链模式
- Java之同步代码块实现取钱实例
- 对于sqldependency类的使用(通过它来监听数据库的变化)
- Bootstrap 元素居中
- 数据结构之二叉搜索树和二叉平衡树学习笔记
- java二叉树非递归之前序遍历
- 看完9个笑话 顿悟9个人生道理
- 2016.10.27 函数特性
- 复选框(checkbox)全选/全不选/反选代码
- 2009 Multi-University Training Contest 1 - Host by TJU
- 10056---Java中Synchronized的用法
- IT研发老兵跳槽记之(六):上将伐谋
- ORB-Slam