heap、红黑树和hash散列

        在学习stl容器时,我们接触到了一些底层数据结构,其中,相对比较复杂的结构有heap堆、红黑树和hash散列,接下来针对这三种结构做一些总结:

一、heap堆:

堆的用处目前见过的有三类:a、堆排序;b、STL容器中,优先队列的底层结构;c、解决海量数据处理中的topK问题。

STL里面的堆操作一般用到的只有4个:make_heap();、pop_heap();、push_heap();、sort_heap(),他们的头函数是algorithm。堆排序在数据结构中是一种重要的排序算法,将在数据结构部分讲解,在这里省略这部分内容,着重分析一下STL中,heap的创建、删除、插入的相关算法。实际上,了解了堆的构建、插入、删除原理,就会很容易理解堆排序的思想了。此外,heap并不归属于STL容器组件,它是个幕后英雄,扮演priority queue的助手,heap的底层实现是vector。

1、建堆:以小顶堆为例

思想:将原始数据存储在堆数组中,然后,从下到上、从右往左,找到第一个非叶节点的节点,比较其与左右孩子的大小,若其比孩子大,则交换。不断重复这个过程。

代码:

void Swap(int * array, int  i, int  j)  
 {   
     int  tmp;  
     tmp = array[j];  
     array[j] = array[i];  
     array[i] = tmp;  
 }
/*小根堆调整*/  
 void MinHeapify(int * array, int  heapSize, int  currentNode)  
 {  
     int  leftChild, rightChild,  minimum;  //左、右孩子下标;三者最小元素下标
     leftChild = 2*currentNode + 1;  //当前结点的做左孩子
     rightChild = 2*currentNode + 2; //当前结点的做右孩子 
     if(leftChild < heapSize && array[leftChild] < array[currentNode])  
         minimum = leftChild;  //有左孩子并且左孩子较小
     else  
         minimum = currentNode;  
     if(rightChild < heapSize && array[rightChild] < array[minimum])  
         minimum = rightChild; //有右孩子并且右孩子是三者最小的 
     if(minimum != currentNode)  
     {//    当前结点不是最小的,需要交换
         Swap(array, minimum, currentNode);  
         MinHeapify(array, heapSize, minimum);  //子节点需要重新递归调整,非常重要!!!
     }  
 }  
 /*构建小根堆*/  
 void MinHeapCreate(int * array, int  heapSize)  
 {  
     int i;  
     for(i = heapSize/2; i >= 0; i--)  
     { //需要从最后一个非叶节点开始调整
         MinHeapify(array, heapSize, i);  
     }  

 }

STL中的操作方法:void make_heap(first_pointer,end_pointer,compare_function);

2、插入:以小顶堆为例

STL中的操作方法:void push_heap(first_pointer,end_pointer,compare_function);

思想:上溯。将新节点拿来与其父节点比较,如果其值比父节点小,就父子对换位置。如此一直上溯,直到不需对换或直到根节点为止。

2、删除

STL中的操作方法:void pop_heap(first_pointer,end_pointer,compare_function);

思想:堆中每次都只能删除堆顶元素。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于根结点数据的“下沉”过程。

        注意,pop_heap之后,最大元素只是被置于底部容器的最尾端,尚未被取走

二、红黑树

红黑树是一种平衡的二叉查找树,其查找、插入、删除的时间复杂度为logN。接下来,我们将红黑树、BST和AVL树进行比较,分析C++选择红黑树作为关联式容器底层结构的原因。

1、红黑树数据结构定义:

enum Color
{
RED = 0,
BLACK = 1

};

struct RBTreeNode
{
struct RBTreeNode *left, *right, *parent;
int key;
int data;
Color color;

};

2、RBT与BST、AVL树比较:

BST特性:a、所有非叶子结点至多拥有两个儿子;b、所有结点存储一个关键字;c、非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树;

如果二叉搜索树的非叶子节点的左右子树节点数目相差不多,则二叉搜索树查找的时间复杂度接近logN,逼近二分查找,并且在删除、插入操作时,二叉搜索树不需要像连续空间上的二分查找那样,移动大段的内存。但是,经过多次删除、插入操作之后,二叉搜索树可能退化为线性结构,所以在实际中,往往在BST的基础上,引入平衡算法,构建平衡二叉搜索树,如,RBT、AVL树。

AVL树是最先发明的自平衡二叉查找树,其定义:a、它的左子树和右子树都是AVL树;b、左子树和右子树的高度差不能超过1。

实际中,AVL树的应用非常少,基本上能被RBT替代,这是因为:针对查找操作,红黑树和AVL的时间复杂度相同。实际操作中,由于红黑树相对AVL树稍微不平衡一些,所以红黑树的查找速度会比AVL树慢一点,但它们都具有对数复杂度。针对删除和插入操作,由于红黑树引入了颜色,其所需做的颜色变换和旋转操作比AVL树为了维护平衡的开销小的多。综合来看,RBT的统计性能高于AVL树。

3、红黑树特性:

a、节点是红色或黑色。
b、根是黑色。
c、所有叶子都是黑色(叶子是NIL节点)。
d、每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

e、从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(简称黑高)。

性质d、e 保证了每个节点其到叶节点的最长路径不会超过最短路径的2倍。这是因为,根据性质do、e可知,某条路径最短时,应该由黑色节点构成,某条路径最长时,应该由黑白节点构成,因为路径上不能有两个连续的红色节点,所以最长路径也只能是最短路径的2倍。

4、红黑树的操作:

红黑树的操作有查找、插入、删除。其中,查找类似二叉搜索树的查找,比较简单。插入、删除比较复杂。

红黑树的操作有变色、左旋和右旋,任何不平衡的结构都可以在变色和三次旋转之内解决。具体的原理和代码可以在网上找到。不再赘述。

三、hash散列

1、hash散列和红黑树的区别:

a、红黑树查找和删除的时间复杂度都是O(logn),Hash查找和删除的时间复杂度都是O(1)。但是,并不一定常数就比log(n) 小,因为hash还有hash函数的耗时,hash的构造速度较慢。只有当元素达到一定数量级时,考虑hash。b、红黑树占用的内存更小(仅需要为其存在的节点分配内存),而Hash事先就应该分配足够的内存存储散列表。c、如果数据完全是静态的,做一个哈希表,性能可能会更好一些。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值