连载8:动态有序集合——挑战红黑树

问题

    在离散型优化问题(如路径搜索、整数规划等)中,常常需要一种能够高效管理动态有序集合的数据结构:不断加入无序的数据,实时取出有序数据(如从优到劣),并且可能频繁地需要进行筛选、删除、更新(改变元素的key)、查找等操作。
    本节将介绍.Net 提供的2 个数据结构,和自行设计的2 个数据结构,并设计实验对比它们在不同情况下的性能。

    首先定义一个接口IDySet<T>,便于利用统一的测试类进行测试,如左图所示,函数解释如下:

 

 

1. Push:放入一个元素;
2. Pop:取出集合中“最小”元素;
3. 其它略  
 

 

排序顺序表(Sorted List)

    .Net 类库中,System.Collections.Generic 名称空间下提供了,SortedList,其原理是在需要插入新元素的时候,利用二分查找找到应该插入的位置。然而其顺序的存储结构,使得插入和删除操作非常昂贵。所以称其为“静态有序集合”更加合理——适用于一次性填充,计算过程中少有改变的情况,它的优点是提供了O(1)的下标访问。
为了便于对比测试,对它进行改造,Push 时将元素从大到小排序,Pop 的时候将末尾元素删除并返回。

红黑树(Red-black Tree)

    “二分查找”是诱人的,然而顺序存储不利于集合修改。人们自然想到了用链式存储——二叉排序树(BST),遗憾的是,链式结构容易“退化”,为了解决这个问题,有了依靠“旋转节点”的平衡二叉树(AVL)。然而由于AVL 要求严格平衡(左右子树深度差不超过1),后来又出现了“红黑树”,只要求大致平衡,性能较好,得到了广泛应用。

   红黑树满足以下性质:

  1. 节点是红色或黑色。
  2. 根是黑色。
  3. 所有叶子都是黑色(叶子是NIL 节点)。
  4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不
  5. 能有两个连续的红色节点)
  6. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

 

    同样的, System.Collections.Generic 名称空间下提供了SortedDictionary,其中TreeSet<T>类型的数据成员实现了红黑树。为了便于对比测试,在TreeSet 中增加Pop 操作:找到树中最“小”节点(最左子树的左孩子),删除节点并返回。

链式二叉堆(Linked Binary Heap)

    二叉堆想必大家都不陌生,然而常见的都是顺序存储的。是否可以定义链式二叉堆LBH 呢?首先分别来看几个操作:

  • Push:设有待插入节点k,准备加入某LBH。若根其空,直接做根,否则看k是否优于根,优于则交换;若有根有一个空孩子,则填其位;若根为叶子,则随机做其左右;若根为二度点,则随机插入左右子树。从此操作可以看成,LBH 是自动保持平衡的,无需额外考虑。
  • Pop:取根节点(它是最小点)。这时我们得到了两棵LBH,选择根小的作为新根,这时又递归出现同样问题,如图:

 

  • Contains:可以在LBH 内部维护一个哈希表来实现:Dictionary<T, Node>,T为泛型参数,Node 为内部使用节点。

    LBH 的优点是计算过程只修改节点指针,不需要分配节点内存,在给定元素的搜索问题中比较适合,例如:在地图上搜索路径,所有节点是一直存在的。

二叉堆(Binary Heap)

二叉堆是顺序存储的完全二叉树,顺序存储结构会涉及到空间扩充时候的拷贝浪费,那么相比链式结构,其效率如何呢?实验之前,没想它是冠军…
    首先,我们利用列表item(List<T>)来保存元素,哈希表dic(Dictionary<T, int>)来做索引,其value 保存T 在item 中的位置。并设置一个私有变量hashing(通过构造函数赋值),表示是否建立索引。

        1. Push:主要思想为,“提拔”优秀的节点, 代码暂略…
        2. Pop:主要思想为,“降级”劣质节点,代码暂略…

性能测试

为了对比这些数据结构在不同情况下的性能,设计测试类(针对IDySet<T>接口),重复做20 次如下实验(20 次结果已趋于稳定),计算总时间(每次计时前垃圾回收),最后取20 次平均值:

  1. Push 测试:随机生成N个0~n的整数r,若Contains(r) = false则Push(r)。
  2. Pop 测试:将已加入到集合中的元素全部Pop 出来。
  3. 综合测试:随机生成N 个0~n 的整数r,若Contains(r) = true则Remove(r);Push(r);若r 的序号能整除8,则Pop()。

 

*注: Sorted List 拥有最快的Pop 速度,然而其它性能差距却10 倍之多,故不再列出。

讨论

    在解决很多离散型优化问题(如路径搜索、整数规划等)时,可以采用启发是搜索算法(通过估计避免大量不必要的搜索),其中需要要用动态有序集合:
    每次迭代Pop 出最优节点,进行某些处理后(可能涉及查找、删除操作),再发展n 个节点Push 入集合中(如:路径搜索中的A*算法每次扩展周围8 个格子),找到结果后返回(可参见4.3“一摞饼的
排序”)。
    由此可以看出在求解过程中,Push 需求远大于Pop,因此BinaryHeap 获得冠军!。自行设计的Linked Binary Heap 和Binary Heap 结构都比较简单,而其性能却都超越了号称“最复杂数据结构”之一的红黑树。

作者:Silver  原文链接:http://gpww.blog.163.com/blog/static/118268164200997103532110/

其他文章:
连载1:卡特兰数(Catalan)
连载2:序列 ABAB对应字符串集合
连载3:最长公共子序列
连载4:计算字符串的相似度
连载5:寻找符合条件的整数
连载6:数组循环位移
连载7:天平秤球

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值