Project1

标题: 二叉堆[更新] [打印本页]

作者: 猫哥哥    时间: 2008-3-17 19:02
标题: 二叉堆[更新]
    在论坛里搜索“二叉堆”,没有结果。于是把自己写的发出来。或许还有不正确、不成熟的地方,请指正。
    至于,二叉堆可以做什么……将一个数插入到10000个数的数组中,在顺序排列结构的数组里,挨个比较的方法来确定新插入数的位置,最坏的情况得比较10000次;如果用二叉堆的结构,节点比较,最坏的情况则需要比较13次。
    有时候,我们需要的仅仅是最大值(最小值),比如在A*寻路里面,取最小F值,就可以应用到二叉堆。

[LINE]1,#dddddd[/LINE]
二叉堆(Binary Head)是什么?
    二叉堆是一种特殊的堆,二叉堆是完全二元树或者是近似完全二元树。二叉堆满足堆特性:父结点的键值总是大於或等于任何一个子节点的键值。如果想了解更多的内容,在搜索引擎里搜一下吧,也不是一两句话能说清楚,篇幅有限就不赘述了。脚本的具体用法,在脚本里有标注。

  1. #==============================================================================
  2. # ■ Binary_Head
  3. #                     by 猫哥哥
  4. #------------------------------------------------------------------------------
  5. #  将数组(Array)转化为二叉堆,并实现堆数据的添加、删除和修改的模块。此模块的使
  6. # 用方法见脚本内容。此为最小堆。如果要用最大堆,把脚本里的"<="和">="替换一下就
  7. # 行了。
  8. #==============================================================================
  9. # 使用方法:
  10. #
  11. #  Binary_Head.new(ar)            将数组ar转化为二叉堆
  12. #
  13. #  Binary_Head.add(bh,item)       往二叉堆bh里,添加数据item
  14. #
  15. #  Binary_Head.del(bh,pos)        删除二叉堆bh里,第pos个数据
  16. #
  17. #  Binary_Head.alt(bh,pos,item)   修改二叉堆bh里的第pos个数据的值
  18. #==============================================================================
  19. #  示例:
  20. #   eg = [10,30,24,34,38,30,20]
  21. #   eg = Binary_Head.new(eg)      
  22. #   p eg                          # => [10,30,20,34,38,30,24]
  23. #   eg = Binary_Head.add(eg,17)
  24. #   p eg                          # => [10,17,20,30,38,30,24,34]
  25. #   Binary_Head.del(eg,1)
  26. #   p eg                          # => [17,30,20,34,38,30,24]
  27. #
  28. #==============================================================================
  29. module Binary_Head
  30.   #--------------------------------------------------------------------------
  31.   # ● 转化
  32.   #--------------------------------------------------------------------------  
  33.   def self.new(ar) #数组转化为二叉堆
  34.    
  35.     ar_n = []
  36.     if ar.class != Array
  37.         p "数据类型不正确";exit
  38.     end

  39.     for i in ar
  40.       add(ar_n,i)
  41.     end
  42.    
  43.     return ar_n #生成堆
  44.   end
  45.   #--------------------------------------------------------------------------
  46.   # ● 添加
  47.   #--------------------------------------------------------------------------
  48.   def self.add(ar,item) #往堆里添加数据

  49.     ar.push item
  50.     pos = ar.size
  51.     loop do #比较父节点
  52.       if pos == 1 #已经在顶端
  53.         break
  54.       end
  55.       pos_f = pos/2
  56.       if ar[pos-1] >= ar[pos_f-1]
  57.         break
  58.       else
  59.         ar[pos_f-1],ar[pos-1]=ar[pos-1],ar[pos_f-1] #交换子结点与父节点
  60.         pos = pos_f #传递下标
  61.       end
  62.     end
  63.    
  64.     return ar
  65.   end
  66.   #--------------------------------------------------------------------------
  67.   # ● 删除
  68.   #--------------------------------------------------------------------------
  69.   def self.del(ar,pos) #从堆里删除数据
  70.    
  71.     if ar[pos - 1] !=nil
  72.     ar[pos - 1] = ar[ar.size - 1]; ar.pop
  73.    
  74.     loop do #比较子结点
  75.       pos_s1 = pos*2;pos_s2 = pos*2 + 1
  76.       if ar[pos_s1-1] == nil and ar[pos_s2-1] == nil #已经无子结点
  77.         break
  78.       end
  79.       if !fs(ar[pos-1],ar[pos_s1-1])
  80.         ar[pos-1],ar[pos_s1-1] = ar[pos_s1-1],ar[pos-1]
  81.         pos = pos_s1;next
  82.       end
  83.       if !fs(ar[pos-1],ar[pos_s2-1])
  84.         ar[pos-1],ar[pos_s2-1] = ar[pos_s2-1],ar[pos-1]
  85.         pos = pos_s2;next
  86.       end
  87.         break
  88.     end
  89.    
  90.     return ar
  91.   end
  92.   end
  93.   
  94.   def self.fs(father,son)
  95.     if son == nil
  96.       true
  97.     else
  98.       father <= son ? true : false
  99.     end
  100.   end
  101.   #--------------------------------------------------------------------------
  102.   # ● 修改
  103.   #--------------------------------------------------------------------------
  104.   def self.alt(ar,pos,item) #修改数据
  105.    
  106.     del(ar,pos);add(ar,item)
  107.     return ar   
  108.   end
  109.   
  110. end
复制代码

[LINE]1,#dddddd[/LINE]
以下为更新
早上进6r我这里网速还比较快,其他时间根本进不来,后来发现还有无图版能凑合凑合……趁现在能上有图版时编辑一下帖子= =

    我一直在思考一个问题。在生成最小堆的时候,或许可以用Array.sort来生成,会比较快,当然要是最大堆的话,再Array.reverse反转一下。由于最后生成的数组是升序排序(降序排序)的,其后面的数总大于等于(小于等于)前面的数。比如,array[p]<=array[p*2] array[p]<=array[p*2+1],满足二叉堆的性质。

  1.   #--------------------------------------------------------------------------
  2.   # ● 转化
  3.   #--------------------------------------------------------------------------  
  4.   def self.new(ar) #数组转化为二叉堆结构
  5.    
  6.     if ar.class != Array
  7.         p "数据类型不正确";exit
  8.     end

  9.     ar=ar.sort #生成最小堆
  10.    
  11.     return ar
  12.   end
复制代码

[LINE]1,#dddddd[/LINE]
    看了大家的讨论,自己动手测试了一下。
    仅就单次求最值的话,用ruby自带的sort排序会快很多,min和max也是。
    二叉堆的快速,只是体现在了之后维护新的数据节省的时间上吧。
[LINE]1,#dddddd[/LINE]
突然想起Ruby里可以直接用a,b=b,a来交换变量……脚本里传递变量的temp用不着了。编辑之。

作者: 禾西    时间: 2008-3-17 19:07
很强大的腳本,雖然暫時腦殘想不到用來寫甚麽==a
作者: 猫哥哥    时间: 2008-3-17 19:22
除了A*寻路能用到以外,我也没想到还能干嘛= =
……或许还能在战棋或者ARPG做怪物的AI时,用来判断一下行动价值之类的
作者: 水迭澜    时间: 2008-3-17 19:24
OJZ,没认真看,脑残了……
作者: 沉影不器    时间: 2008-3-17 19:37
提示: 作者被禁止或删除 内容自动屏蔽
作者: 晓真·哀雾    时间: 2008-3-17 19:43
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2008-3-17 19:51
提示: 作者被禁止或删除 内容自动屏蔽
作者: 雷欧纳德    时间: 2008-3-17 20:11
除了写A*的时候用过还米在别的地方用到过二叉堆-口-
不过用二叉堆的确是非常适合用来维护A*的开启列表{/qiang}

以下引用沉影不器于2008-3-17 11:37:24的发言:

瞅瞅...好像就是二叉树


二叉堆就是存放堆的二叉树{/hx}
作者: 美兽    时间: 2008-3-17 22:58
一般对于想快速需求最值的环境下,都能用的上,例如棋类搜索,评估等。

在处理大数据量时效果比较明显,数据量很小时反而显的累赘,与普通的二叉树相比强调节点顺序。
作者: 风雪优游    时间: 2008-3-18 00:09
= =我不行了...........
作者: 雷欧纳德    时间: 2008-3-18 01:09
以下引用美兽于2008-3-17 14:58:34的发言:

一般对于想快速需求最值的环境下,都能用的上,例如棋类搜索,评估等。

在处理大数据量时效果比较明显,数据量很小时反而显的累赘,与普通的二叉树相比强调节点顺序。


数据越多越能显示出其强大
但是如果只有几十个数据,那么用二叉堆是吃力不讨好的事情
比较适合有几千个数据的大型游戏使用

二叉堆的一个特点就是,只关心第一个数据是否是最值,不关心其后的数据的排列顺序

不过数据少的时候觉得用二分法插入数组其实更有效一点
作者: 沉影不器    时间: 2008-3-18 03:52
提示: 作者被禁止或删除 内容自动屏蔽
作者: dbshy    时间: 2008-3-18 04:02
多么熟悉的二叉堆,久违了,让我想起了高中时的OI生涯
作者: 雪流星    时间: 2008-5-8 14:24
我只是想提一下
堆的英文不是head而是heap{/pz}

MS沒人指出

另外在堆當中,MS不能有相等的值吧?
也就沒有父結點等於子結點的可能性了

還是我記錯了@@
作者: 雷欧纳德    时间: 2008-5-8 16:59
为啥不能有相等情况呢?
求最值并不需要保证最值得唯一性的说
作者: 雪流星    时间: 2008-5-8 17:51
想起了
二元搜索樹(Binary Search Tree)才是要求不能相等的
因為搜索為目的,因此每個結點之值都是獨一無二的
{/hx}
作者: 雷欧纳德    时间: 2008-5-8 18:21
二叉树的子孙太多鸟{/qiang}
作者: 御灵    时间: 2008-5-24 08:16
发布完毕。
http://rpg.blue/web/htm/news1067.htm
vip+4
作者: 沉影不器    时间: 2008-6-1 04:06
提示: 作者被禁止或删除 内容自动屏蔽
作者: 威廉华莱士    时间: 2009-4-21 02:24
刚才从技术区某帖找到了这一帖,猫哥哥总能写出一些很有趣的代码,呵呵~~~不过看了一下具体算法,感觉删除部分,存在问题....
运行一下这段:
ar = [1,2,3,4,5,6,7,8,9]
Binary_Head.new(ar)
ar = Binary_Head.del(ar,2)
p ar
ar = Binary_Head.del(ar,2)
p ar

出来的结果是:[1,4,3,8,5,6,7,9]
             [1,8,3,9,5,6,7]
显然第一个结果是我们想要的,但第二个,却与我们要的最小二叉堆不符了,因为最小二叉堆其父节点总小于其子节点,而明显8会大于其子节点5...所以这不是我们要的结果....
相信这只是猫哥哥一时疏忽造成的....改了一下代码,让它每次只与子节点中较小的比较,这样问题大概就能解决了...

  1. #--------------------------------------------------------------------------
  2.   # ● 删除
  3.   #--------------------------------------------------------------------------
  4.   def self.del(ar,pos) #从堆里删除数据
  5.    
  6.     if ar[pos - 1] !=nil
  7.     ar[pos - 1] = ar[ar.size - 1]; ar.pop
  8.    
  9.     loop do #比较子结点
  10.       pos_s1 = pos*2;pos_s2 = pos*2 + 1
  11.       if ar[pos_s1-1] == nil and ar[pos_s2-1] == nil #已经无子结点
  12.         break
  13.       elsif ar[pos_s2-1] == nil  #当右孩子为空时
  14.         pos_min = pos_s1     #最小节点值直接赋给左孩子
  15.       else
  16.        #比较出较小的节点
  17.        if ar[pos_s1-1] > ar[pos_s2-1]
  18.           pos_min = pos_s2
  19.         else
  20.           pos_min = pos_s1
  21.         end
  22.       end
  23.       if !fs(ar[pos-1],ar[pos_min-1])
  24.         ar[pos-1],ar[pos_min-1] = ar[pos_min-1],ar[pos-1]
  25.         pos = pos_min;next
  26.       end
  27.      
  28.         break
  29.     end
  30.    
  31.     return ar
  32.   end
  33. end
复制代码





欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1