首页 > 代码库 > 红黑树的Python实现

红黑树的Python实现

想用红黑树,怎么搜都搜不到现成的Python实现。干脆自己写一个。

算法的结构按照Sedgewick的《算法(4th)》一书第三章写成,略有改动。

完整的API实现,也用了一些测试case,暂时没发现问题。

这玩意就是好用,谁用谁知道。

废话不多说直接上代码。

#注意事项: 重载RBT.Node.reduce(self,new_val)来实现 append()方法中对已存在主键对应值的自定义合并操作。默认为调用list.extend()方法。

  1 #!/usr/bin/env python  2 #coding: gbk  3   4 ########################################################################  5 #Author: Feng Ruohang  6 #Create: 2014/10/06 11:38  7 #Digest: Provide a common data struct: Red Black Tree  8 ########################################################################  9  10 class RBT(object): 11     class Node(object): 12         ‘‘‘ 13         Node used in RBTree 14         ‘‘‘ 15         def __init__(self,key,value=http://www.mamicode.com/None,color=False,N=1): 16             self.key = key 17             self.val = value 18             self.color = color   #False for Black, True for Red 19             self.N = N           #Total numbers of nodes in this subtree 20             self.left = None 21             self.right = None 22  23         def __cmp__(l,r): 24             return cmp(l.key,r.key) 25  26         def __eq__(l,r): 27             return True if l.key == r.key else False 28  29         def __add__(l,r): 30             l.value + r.value 31  32         def reduce(self,new_val): 33             self.val.extend(new_val) 34  35     def __init__(self): 36         self.root = None 37  38     #====================APIs====================# 39     #=====Basic API 40  41     def get(self,key): 42         return  self.__get(self.root,key) 43  44     def put(self,key,val): 45         self.root = self.__put(self.root,key,val) 46         self.root.color = False  47  48     def append(self,key,val): 49         self.root = self.__append(self.root,key,val) 50         self.root.color = False 51  52     def delete(self,key): 53         if not self.contains(key): 54             raise LookupError(No such keys in rbtree. Fail to Delete) 55         if self.__is_black(self.root.left) and self.__is_black(self.root.right): 56             self.root.color = True 57         self.root = self.__delete(self.root,key) 58         if not self.is_empty():  59             self.root.color = False 60  61     def del_min(self): 62         if self.is_empty():  63             raise LookupError(Empty Red-Black Tree. Can\‘t delete min) 64         if self.__is_black(self.root.left) and self.__is_black(self.root.right): 65             self.root.color = True 66         self.root = self.__del_min(self.root) 67         if not self.is_empty(): self.root.color = False 68  69     def del_max(self): 70         if self.is_empty(): 71             raise LookupError(Empty Red-Black Tree. Can\‘t delete max) 72         if self.__is_black(self.root.left) and self.__is_black(self.root.right): 73             self.root.color = True 74         self.root = self.__del_max(self.root) 75         if not self.is_empty(): self.root.color = False  76  77     def size(self): 78         return self.__size(self.root) 79  80     def is_empty(self): 81         return not self.root 82  83     def contains(self,key): 84         return bool(self.get(key)) 85  86     #=====Advance API 87     def min(self): 88         if self.is_empty(): 89             return None 90         return self.__min(self.root).key 91  92     def max(self): 93         if self.is_empty(): 94             return None 95         return self.__max(self.root).key 96  97     def floor(self,key): 98         x = self.__floor(self.root,key) 99         if x:100             return x.key,x.val101         else:102             return None,None103 104     def ceil(self,key):105         x = self.__ceil(self.root,key)106         if x:107             return x.key,x.val108         else:109             return None,None110 111     def below(self,key):112         index = self.index(key)113         if not 0 <= index - 1 < self.size():114             return None,None    #Return None if out of range115         x = self.__select(self.root,index - 1)116         return x.key,x.val117 118     def above(self,key):119         index = self.index(key)120         if self.contains(key):121             if not 0 <= index + 1 < self.size():122                 return None,None    #Return None if out of range123             else:124                 x = self.__select(self.root,index+1)125                 return x.key,x.val126         else:#if key is not in tree. then select(i) is what we need127             if not 0 <= index < self.size():128                 return None,None    #Return None if out of range129             else:130                 x = self.__select(self.root,index)131                 return x.key,x.val132 133     def index(self,key):134         return self.__index(self.root,key) 135 136     def keys(self):137         ‘‘‘Return All Keys in the tree ‘‘‘138         return self.range(self.min(),self.max())139 140     def range(self,lo,hi):141         ‘‘‘Take two keys. return keys between them‘‘‘142         q = []143         self.__range(self.root,q,lo,hi)144         return q145 146     def select(self,index):147         ‘‘‘Given Index Return Corresponding key ‘‘‘148         if not 0 <= index < self.size():149             return None150         return self.__select(self.root,index).key151 152     def width(self,lo,hi):153         ‘‘‘Return the numbers of keys between lo and hi ‘‘‘154         if lo > hi: 155             return 0156         if self.contains(hi):157             return self.index(hi) - self.index(lo) + 1158         else:159             return self.index(hi) - self.index(lo)160 161 162     #===============Private Method===============#163     #=====Basic164     def __get(self,x,key):165         while x:166             tag = cmp(key,x.key)167             if tag < 0 : x = x.left168             elif tag > 0 :x = x.right169             else: return x.val170 171     def __put(self,h,key,val):172         if not h: 173             return self.Node(key,val,True,1)174         tag = cmp(key,h.key)175         if tag < 0: 176             h.left = self.__put(h.left,key,val)177         elif tag > 0: 178             h.right = self.__put(h.right,key,val)179         else: 180             h.val = val   #Update181 182         if self.__is_black(h.left) and self.__is_red(h.right):183             h = self.__rotate_left(h)184         if self.__is_red(h.left) and self.__is_red(h.left.left):185             h = self.__rotate_right(h)186         if self.__is_red(h.left) and self.__is_red(h.right):187             self.__flip_colors(h)188         h.N = self.__size(h.left) + self.__size(h.right) + 1189         return h190 191     def __append(self,h,key,val):192         if not h: 193             return self.Node(key,val,True,1)194         tag = cmp(key,h.key)195         if tag < 0: 196             h.left = self.__append(h.left,key,val)197         elif tag > 0: 198             h.right = self.__append(h.right,key,val)199         else: 200             h.reduce(val)   #append.201 202         if self.__is_black(h.left) and self.__is_red(h.right):203             h = self.__rotate_left(h)204         if self.__is_red(h.left) and self.__is_red(h.left.left):205             h = self.__rotate_right(h)206         if self.__is_red(h.left) and self.__is_red(h.right):207             self.__flip_colors(h)208         h.N = self.__size(h.left) + self.__size(h.right) + 1209         return h210 211     def __del_min(self,h):212         if not h.left: #if h is empty:return None213             return None214 215         if self.__is_black(h.left) and self.__is_black(h.left.left):216             self.__move_red_left(h)217         h.left = self.__del_min(h.left) #Del recursive218         return self.__balance(h)219 220     def __del_max(self,h):221         if self.__is_red(h.left): 222             h = self.__rotate_right(h)223         if not h.right: 224             return None225         if self.__is_black(h.right) and self.__is_black(h.right.left):226             h = self.__move_red_right(h)227         h.right = self.__del_max(h.right)228         return self.__balance(h)229 230     def __delete(self,h,key):231         if key < h.key:232             if self.__is_black(h.left) and self.__is_black(h.left.left):233                 h = self.__move_red_left(h)234             h.left = self.__delete(h.left,key)235         else:236             if self.__is_red(h.left):237                 h = self.__rotate_right(h)238             if key == h.key and not h.right:239                 return None240             if self.__is_black(h.right) and self.__is_black(h.right.left):241                 h = self.__move_red_right(h)242             if key == h.key:#replace h with min of right subtree243                 x = self.__min(h.right)244                 h.key = x.key245                 h.val = x.val246                 h.right = self.__del_min(h.right)247             else:248                 h.right = self.__delete(h.right,key)249         h = self.__balance(h)250         return h251     252     #=====Advance253     def __min(self,h):254         #Assume h is not null255         if not h.left:256             return h257         else:258             return self.__min(h.left)259 260     def __max(self,h):261         #Assume h is not null262         if not h.right:263             return h264         else:265             return self.__max(h.right)266 267     def __floor(self,h,key):268         ‘‘‘Find the NODE with key <= given key in the tree rooted at h ‘‘‘269         if not h:270             return None271         tag = cmp(key,h.key)272         if tag == 0:273             return h274         if tag < 0:275             return self.__floor(h.left,key)276         t = self.__floor(h.right,key)277         if t:#if find in right tree278             return t279         else:#else return itself280             return h281 282     def __ceil(self,h,key):283         ‘‘‘Find the NODE with key >= given key in the tree rooted at h ‘‘‘284         if not h:285             return None286         tag = cmp(key,h.key)287         if tag == 0:288             return h289         if tag > 0: # key is bigger290             return self.__ceil(h.right,key)291         t = self.__ceil(h.left,key)#key is lower.Try to find ceil left292         if t:#if find in left tree293             return t294         else:#else return itself295             return h296 297     def __index(self,h,key):298         if not h:299             return 0300         tag = cmp(key,h.key)301         if tag < 0:302             return self.__index(h.left,key)303         elif tag > 0:   #Key is bigger304             return self.__index(h.right,key) + 1 + self.__size(h.left)305         else:   #Eq306             return self.__size(h.left)307 308     def __select(self,h,index):309         ‘‘‘assert h. assert 0 <= index < size(tree) ‘‘‘310         l_size = self.__size(h.left)311         if l_size > index:312             return self.__select(h.left,index)313         elif l_size < index:314             return self.__select(h.right,index - l_size - 1)315         else:316             return h317 318     def __range(self,h,q,lo,hi):319         if not h: 320             return321         tag_lo = cmp(lo,h.key)322         tag_hi = cmp(hi,h.key)323         if tag_lo < 0:#lo key is lower than h.key324             self.__range(h.left,q,lo,hi)325         if tag_lo <= 0 and tag_hi >= 0:326             q.append(h.key)327         if tag_hi > 0 :# hi key is bigger than h.key328             self.__range(h.right,q,lo,hi)329 330 331     #===============Adjust Functions=============#332     def __rotate_right(self,h):333         x = h.left334         h.left,x.right = x.right,h335         x.color,x.N = h.color,h.N336         h.color,h.N = True,self.__size(h.left) + self.__size(h.right) + 1337         return x338 339     def __rotate_left(self,h):340         x = h.right341         h.right,x.left = x.left,h342         x.color,x.N = h.color,h.N343         h.color,h.N = True,self.__size(h.left) + self.__size(h.right) + 1344         return x345 346     def __flip_colors(self,h):347         h.color = not h.color348         h.left.color = not h.left.color349         h.right.color = not h.right.color350 351     def __move_red_left(self,h):352         self.__flip_colors(h)353         if self.__is_red(h.right.left):354             h = self.__rotate_left(h)355         return h356 357     def __move_red_right(self,h):358         self.__flip_colors(h)359         if self.__is_red(h.left.left):360             h = self.__rotate_right(h)361         return h362 363     def __balance(self,h):364         if self.__is_red(h.right): 365             h = self.__rotate_left(h)366         if self.__is_red(h.left) and self.__is_red(h.left.left): 367             h = self.__rotate_right(h)368         if self.__is_red(h.left) and self.__is_red(h.right):369             self.__flip_colors(h)370         h.N = self.__size(h.left) + self.__size(h.right) + 1371         return h372     373     #Class Method374     @staticmethod375     def __is_red(x):376         return False if not x else x.color377 378     @staticmethod379     def __is_black(x):380         return True  if not x else not x.color381 382     @staticmethod383     def __size(x):384         return 0 if not x else x.N385 386 387 def RBT_testing():388     ‘‘‘API Examples ‘‘‘389     t = RBT()390     test_data = http://www.mamicode.com/"SEARCHXMPL"391 392     print =====testing is_empty()\nBefore Insertion393     print t.is_empty()394 395     for letter in test_data:396         t.put(letter,[ord(letter)])397         print "Test Inserting:%s, tree size is %d" % (letter,t.size())398     print "After insertion it return:"399     print t.is_empty()400     print  "====test is_empty complete\n"401 402 403     print "=====Tesing Get method:"404     print "get ‘s‘ is "405     print t.get(S)406     print "get ‘H‘ is "407     print t.get(H)408 409     print ==Trying get null key: get "F" is410     print t.get(F)411     print "=====Testing Get method end\n\n"412 413     print "=====Testing ceil and floor"414     print "Ceil(‘L‘)"415     print t.ceil(L)416     print "Ceil(‘F‘) *F is not in tree"417     print t.ceil(F)418 419     print "Floor(‘L‘)"420     print t.ceil(L)421     print "Floor(‘F‘)"422     print t.ceil(F)423 424     print ======test append method425     print Orient key e is correspond with426     print t.get(E)427     t.append(E,[4])428     print ==After append429     print t.get(E)430     print "=====Testing Append method end\n\n" 431 432     print "=====Testing index()"433     print "index(E)" 434     print t.index(E)435     print "index(L),select(4)"436     print t.index(L),t.select(4)437     print "index(‘M‘),select(5)"438     print t.index(M),t.select(5)439     print "index a key not in tree:\n index(‘N‘),select(6)"440     print t.index(N),t.select(6)441     print "index(‘P‘)"442     print t.index(P)443     444     445     print "=====Testing select"446     print "select(3) = "447     print t.select(3)448     print "select and index end...\n\n"449 450     print "====Tesing Min and Max"451     print "min key is:"452     print t.min()453     print "max key is"454     print t.max()455 456     print "==How much between min and max:"457     print t.width(t.min(),t.max())458     print "keys between min and max:"459     print t.keys()460     print "keys in ‘E‘ and ‘M‘ "461     print t.range(E,M)462 463 464     print "try to delete min_key:"465     print "But we could try contains(‘A‘) first"466     print t.contains(A)467     t.del_min()468     print "After deletion t.contains(‘A‘) is "469     print t.contains(A)470 471     print t.min()472     print "try to kill one more min key:"473     t.del_min()474     print t.min()475     print "try to delete max_key,New Max key is :"476     t.del_max()477     print t.max()478     print "=====Tesing Min and Max complete\n\n"479 480 481 482     print =====Deleting Test483     print t.size()484     t.delete(H)485     print t.size()486 487     print Delete a non-exists key:488     try:    489         t.delete(F)490     except:491         print "*Look up error occur*"492 493     print "=====Testing Delete method complete"494 495 def test_basic_api():496     print "==========Testing Basic API=========="497     t = RBT()498     print "Test Data: FENGDIJKABCLM"499     test_data = http://www.mamicode.com/"FENGDIJKABCLM" #from A-N,without H500 501     #=====put()502     print "==========put() test begin!=========="503     for letter in test_data:504         t.put(letter,[ord(letter)]) #Value is [ascii order of letter]505         print "put(%s); Now tree size is %d"%(letter,t.size())506     print Final tree size is %d%t.size()507     print "==========put() test complete!==========\n"508 509     #=====get()510     print "==========get() test begin!=========="511     print "get(‘F‘):\t%s"%repr(t.get(F))512     print "get(‘A‘):\t%s"%repr(t.get(A))513     print "get a non-exist key Z: get(‘Z‘):\t%s"%repr(t.get(Z))514     print "==========get() test complete!==========\n"515 516     #=====append()517     print "=====append() test begin!=========="518     print "First append to a exist key:[F]"519     print "Before Append:get(‘F‘):\t%s"%repr(t.get(F))520     print "append(‘F‘,[3,‘haha‘]):\t%s"%repr(t.append(F,[3,haha]))521     print "After Append:get(‘F‘):\t%s\n"%repr(t.get(F))522     print "Second append to a non-exist key:[O]"523     print "Before Append:get(‘O‘):\t%s"%repr(t.get(O))524     print "append a non-exist key O: append(‘O‘,[‘value of O‘]):\t%s"%repr(t.append(O,[value of O]))525     print "After Append:get(‘O‘):\t%s\n"%repr(t.get(O))526     print "==========append() test complete!==========\n"527 528     #=====delete()529     print "==========delete() test begin!=========="530     test_data2 = [x for x in test_data]531     test_data2.reverse()532     for letter in test_data2:533         t.delete(letter)534         print "delete(%s); Now tree size is %d"%(letter,t.size())535     print Final tree size is %d%t.size()536     print "==========delete() test complete!==========\n"537 538     print "==========Basic API Test Complete==========\n\n"539 540 def test_advance_api():541     print "==========Testing min max floor ceil above below =========="542     t = RBT()543     print "Test Data: FENGDIJKABCLM"544     test_data = http://www.mamicode.com/"FENGDIJKABCLM" #from A-N,without H545     for letter in test_data:546         t.put(letter,[ord(letter)]) #Value is [ascii order of letter]547 548     #=====min() and del_min()549     print "==========min() and del_min() test begin!=========="550     print "Original min():\t%s"%repr(t.min())551     print "run del_min()"552     t.del_min()553     print "After del_min:min()\t%s"%repr(t.min())554 555     print "run del_min() again"556     t.del_min()557     print "After del_min run again:min()\t%s"%repr(t.min())558 559     print "=====max() and del_max() test begin!"560     print "Original max():\t%s"%repr(t.max())561     print "run del_max()"562     t.del_max()563     print "After del_max:max()\t%s"%repr(t.max())564 565     print "run del_max() again"566     t.del_max()567     print "After del_max run again:max()\t%s"%repr(t.max())568     print "==========min() max() del_min() del_max() test complete!==========\n"569 570 def test_int_api():571     #======ceil floor above below572     print "==========Testing ceil floor above below =========="573     t = RBT()574     print "Test Data: FENGDIJKABCLM - [AHN] = FEGDIJKBCLM"575     test_data = http://www.mamicode.com/"FEGDIJKBCLM" #from A-N, Del A H N576 577     for letter in test_data:578         t.put(letter,[ord(letter)]) #Value is [ascii order of letter]579     print "Node\tceil\t\tfloor\t\tabove\t\tbelow"580     for P in [A,B,C,G,H,I,L,M,N]:581         print "%s\t%s\t%s\t%s\t%s"%(P,t.ceil(P),t.floor(P),t.above(P),t.below(P))582 583 if __name__ == __main__:584     test_basic_api()585     test_advance_api()586     test_int_api()
View Code

查找操作的数据结构不断进化,才有了红黑树:从链表到二叉平衡树,再到2-3树,最后到红黑树。

红黑树本质上是用二叉平衡树的形式来模拟2-3树的功能

《算法导论》也好,其他什么乱七八糟算法书博客也罢,讲红黑树都没讲到本质。

Sedgewick的《算法(4th)》这本书就很不错:起码他告诉你红黑树是怎么来的。 

 仔细理解2-3树与红黑树的相同之处,才能对那些乱七八糟的插入删除调整操作有直观的认识。

红黑树的Python实现