平衡二叉树总结一:二叉搜索树

  这段时间一直再刷leetcode,现在刷了快一半了吧,感觉没什么状态了,再继续刷可能就不太好了,想想不如回头再补补算法吧。总结了自己在基础算法上的一些短板,准备后面这些时间把它们都补上。好了,上正题。

  平衡二叉树算是应用最广泛的树型结构了吧,至少在内存里面是吧。刚开始学数据结构的时候,树的那一章基本没怎么看,大部分精力都放在了排序和图论里,而且感觉树也挺简单的,现在刷题才慢慢发现这种数据结构的威力,虽然c++的算法库已经有了现成的工具,但是学习好这种数据结构的思想还是很有必要的。

  首先,平衡二叉树是一颗二叉搜索树。其结构满足 root->left->val < root-> val < root->right->val 即根节点左儿子值 < 根节点值 < 右儿子值,并且根节点左右儿子也是二叉搜索树。

第一次作图,凑或着看吧

struct searchTree{
  searchTree* left;
  searchTree* right;
  int val;
}
typedef serachTree* tree;
二叉搜索树的结构,c++版。主要是一个左指针、一个右指针、一个值,不难看出left与right其实是“多余”的,然而没有这俩多余的域二叉树的搜索效率就无从谈起,这就是空间
换时间的典型吧。


  1.二叉搜索树的搜索

  二叉搜索树操作都很简单,搜索时,我们只需比较待搜索值与根节点的大小,比根节点大肯定在左子树,这样我们排除了右子树上节点,这就是二叉搜索树能节省时间的原理,当然比根节点小自然就在右边,相等的话该节点就是我们要找的节点了,代码如下:

/*递归的写法,当然也可以改成循环的,不过递归的逻辑更简明。*/
tree search(tree t,int& val){
  if(t==NULL)return NULL;//为空说明没找到该值。
  if(val>t->val) return search(t->right,val);//大于根节点,往右走。
  else if(val<t->val) return search(t->left,val);//小于往左走。
  return t;//等于,返回该节点。
}
2.插入

/*二叉树的插入,与搜索算法类似。比根节点大就往右插,小往左(等于的情形需要特别
处理,这里先不说,之后的也不考虑等于的情形),直到遇到空节点。*/
tree insert(tree t,int& val){
  if(t==NULL){
    t = new searchTree;
    t->val = val;
    t->left = t->right = NULL;
  }
  if(val>t->val)t->right = insert(t->right,val);
  if(val<t->val)t->left = insert(t->left,val);
  return t;
  //其实返回t不是最优的写法,会多一个赋值过程,不过这样程序看起来更简明。
}
3.删除

  相对而言,删除操作是比较复杂的,需要寻找删除节点的左子树的最大值,或右子树最小值来代替该节点,中间会有一些细节需要注意。

代码稍长,主要是最后寻找替代节点比较麻烦。

/*二叉树的删除或多或少会有些复杂,如果要删除的叶节点,直接删除即可。但是倘若
要删除非叶节点则需要找一个节来替代它。一般是选取左子树的最大值或右子树的最小值。*/
tree del(tree t,int& val){
  if(t==NULL)return NULL;
  if(val>t->val)t->right=del(t->right,val);//递归删除左边
  if(val<t->val)t->left = del(t->left,val);//递归删除右边
  if(val==t->val){//找到节点
    tree v,p=t;
    if(t->left){//如果该节点有左子树,找到左子树最大值。
      for(v=t->left;v->right;p=v,v=v->right){}
      t->val = v->val;//最大值替换删除值。
      if(p==t)p->left = v->left;//如果p节点等于t说明左子树最大值正好为t的左儿子。
      //v自然没有了右儿子,所以将v左儿子赋值给p的右儿子。
      else{p->right = v->left;}//此时p改变过一次,需要p的右儿子是v,所以需要改变一下。
      delete v;//删除v。
    }
    else if(t->right){
      for(v=t->right;v->left;p=v,v=v->left){}
      t->val = v->val;
      if(p==t)p->right = v->right;
      else{p->left = v->right;}
      delete v;
    }
    else{
      delete t;
      return NULL;
    }
    return t;
  }
}

4.总结

  二叉搜索树的搜索与插入效率理想的情形下(左右子树都填满)自然是log2(n)的,但是实际操作中往往达不到这个要求,比如插入的序列是递增的,此时该树退化成

链表,效率变为o(n)。所以为了防止该情况的出现,人们开发出了二叉平衡树,也就是对于根节点而言其左右子树尽量相等的二叉搜索树,平衡条件不同,树的搜索与插入

效率不同。不过一般平衡条件越严格,搜索效率越高,但是插入就变得困难了。

  二叉平衡树有很多,如最早的avl树以及其变种红黑树,无需额外空间的splay树,编程简单的treap树,SBT树,替罪羊树等,待我一一总结起来。

 

阅读更多
个人分类: 平衡二叉树
上一篇Complete Binary Search Tree(mooc)
下一篇平衡二叉树总结二:avl树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭