00001 #!/usr/bin/env python 00002 # 00003 # This code adapted from C source from 00004 # Thomas Niemann's Sorting and Searching Algorithms: A Cookbook 00005 # 00006 # From the title page: 00007 # Permission to reproduce this document, in whole or in part, is 00008 # given provided the original web site listed below is referenced, 00009 # and no additional restrictions apply. Source code, when part of 00010 # a software project, may be used freely without reference to the 00011 # author. 00012 # 00013 # Adapted by Chris Gonnerman <chris.gonnerman@newcenturycomputers.net> 00014 # and Graham Breed 00015 # 00016 # Updated by Charles Tolman <ct@acm.org> 00017 # inheritance from object class 00018 # added RBTreeIter class 00019 # added lastNode and prevNode routines to RBTree 00020 # added RBList class and associated tests 00021 # 00022 # Updated by Stefan Fruhner <marycue@gmx.de> 00023 # Added item count to RBNode, which counts the occurence 00024 # of objects. The tree is kept unique, but insertions 00025 # of the same object are counted 00026 # changed RBList.count(): returns the number of occurences of 00027 # an item 00028 # Renamed RBList.count to RBList.elements, because of a name 00029 # mismatch with RBList.count() 00030 # changed RBTree.insertNode to count insertions of the same item 00031 # changed RBList.insert(): uncommented some superfluid code 00032 # changed RBList.remove(): If called with all=True, then all instances 00033 # of the node are deleted from the tree; 00034 # else only node.count is decremented, 00035 # if finally node.count is 1 the node 00036 # is deleted. all is True by default. 00037 # changed RBTree.deleteNode : same changes as for RBList.remove() 00038 # finally I've changed the __version__ string to '1.6' 00039 00040 __version__ = "1.6" 00041 00042 import string 00043 00044 BLACK = 0 00045 RED = 1 00046 00047 class RBNode(object): 00048 00049 def __init__(self, key = None, value = None, color = RED): 00050 self.left = self.right = self.parent = None 00051 self.color = color 00052 self.key = key 00053 self.value = value 00054 self.nonzero = 1 00055 self.count = 1 00056 00057 def __str__(self): 00058 return repr(self.key) + ': ' + repr(self.value) 00059 00060 def __nonzero__(self): 00061 return self.nonzero 00062 00063 ## 00064 # imitate sequence 00065 def __len__(self): 00066 return 2 00067 00068 ## 00069 # imitate sequence 00070 def __getitem__(self, index): 00071 if index==0: 00072 return self.key 00073 if index==1: 00074 return self.value 00075 raise IndexError('only key and value as sequence') 00076 00077 00078 class RBTreeIter(object): 00079 00080 def __init__ (self, tree): 00081 self.tree = tree 00082 self.index = -1 # ready to iterate on the next() call 00083 self.node = None 00084 self.stopped = False 00085 00086 ## 00087 # Return the current item in the container 00088 # 00089 def __iter__ (self): 00090 return self.node.value 00091 00092 ## 00093 # Return the next item in the container 00094 # Once we go off the list we stay off even if the list changes 00095 # 00096 def next (self): 00097 if self.stopped or (self.index + 1 >= self.tree.__len__()): 00098 self.stopped = True 00099 raise StopIteration 00100 # 00101 self.index += 1 00102 if self.index == 0: 00103 self.node = self.tree.firstNode() 00104 else: 00105 self.node = self.tree.nextNode (self.node) 00106 return self.node.value 00107 00108 00109 class RBTree(object): 00110 00111 def __init__(self, cmpfn=cmp, unique=True): 00112 self.sentinel = RBNode() 00113 self.sentinel.left = self.sentinel.right = self.sentinel 00114 self.sentinel.color = BLACK 00115 self.sentinel.nonzero = 0 00116 self.root = self.sentinel 00117 self.elements = 0 00118 00119 #SF: If self.unique is True, all elements in the tree have 00120 #SF to be unique and an exception is raised for multiple 00121 #SF insertions of a node 00122 #SF If self.unique is set to False, nodes can be added multiple 00123 #SF times. There is still only one node, but all insertions are 00124 #SF counted in the variable node.count 00125 self.unique = unique 00126 # changing the comparison function for an existing tree is dangerous! 00127 self.__cmp = cmpfn 00128 00129 def __len__(self): 00130 return self.elements 00131 00132 def __del__(self): 00133 # unlink the whole tree 00134 00135 s = [ self.root ] 00136 00137 if self.root is not self.sentinel: 00138 while s: 00139 cur = s[0] 00140 if cur.left and cur.left != self.sentinel: 00141 s.append(cur.left) 00142 if cur.right and cur.right != self.sentinel: 00143 s.append(cur.right) 00144 cur.right = cur.left = cur.parent = None 00145 cur.key = cur.value = None 00146 s = s[1:] 00147 00148 self.root = None 00149 self.sentinel = None 00150 00151 def __str__(self): 00152 return "<RBTree object>" 00153 00154 def __repr__(self): 00155 return "<RBTree object>" 00156 00157 def __iter__ (self): 00158 return RBTreeIter (self) 00159 00160 def rotateLeft(self, x): 00161 00162 y = x.right 00163 00164 # establish x.right link 00165 x.right = y.left 00166 if y.left != self.sentinel: 00167 y.left.parent = x 00168 00169 # establish y.parent link 00170 if y != self.sentinel: 00171 y.parent = x.parent 00172 if x.parent: 00173 if x == x.parent.left: 00174 x.parent.left = y 00175 else: 00176 x.parent.right = y 00177 else: 00178 self.root = y 00179 00180 # link x and y 00181 y.left = x 00182 if x != self.sentinel: 00183 x.parent = y 00184 00185 def rotateRight(self, x): 00186 00187 #*************************** 00188 # rotate node x to right 00189 #*************************** 00190 00191 y = x.left 00192 00193 # establish x.left link 00194 x.left = y.right 00195 if y.right != self.sentinel: 00196 y.right.parent = x 00197 00198 # establish y.parent link 00199 if y != self.sentinel: 00200 y.parent = x.parent 00201 if x.parent: 00202 if x == x.parent.right: 00203 x.parent.right = y 00204 else: 00205 x.parent.left = y 00206 else: 00207 self.root = y 00208 00209 # link x and y 00210 y.right = x 00211 if x != self.sentinel: 00212 x.parent = y 00213 00214 def insertFixup(self, x): 00215 #************************************ 00216 # maintain Red-Black tree balance * 00217 # after inserting node x * 00218 #************************************ 00219 00220 # check Red-Black properties 00221 00222 while x != self.root and x.parent.color == RED: 00223 00224 # we have a violation 00225 00226 if x.parent == x.parent.parent.left: 00227 00228 y = x.parent.parent.right 00229 00230 if y.color == RED: 00231 # uncle is RED 00232 x.parent.color = BLACK 00233 y.color = BLACK 00234 x.parent.parent.color = RED 00235 x = x.parent.parent 00236 00237 else: 00238 # uncle is BLACK 00239 if x == x.parent.right: 00240 # make x a left child 00241 x = x.parent 00242 self.rotateLeft(x) 00243 00244 # recolor and rotate 00245 x.parent.color = BLACK 00246 x.parent.parent.color = RED 00247 self.rotateRight(x.parent.parent) 00248 else: 00249 00250 # mirror image of above code 00251 00252 y = x.parent.parent.left 00253 00254 if y.color == RED: 00255 # uncle is RED 00256 x.parent.color = BLACK 00257 y.color = BLACK 00258 x.parent.parent.color = RED 00259 x = x.parent.parent 00260 00261 else: 00262 # uncle is BLACK 00263 if x == x.parent.left: 00264 x = x.parent 00265 self.rotateRight(x) 00266 00267 x.parent.color = BLACK 00268 x.parent.parent.color = RED 00269 self.rotateLeft(x.parent.parent) 00270 00271 self.root.color = BLACK 00272 00273 def insertNode(self, key, value): 00274 #********************************************** 00275 # allocate node for data and insert in tree * 00276 #********************************************** 00277 00278 # we aren't interested in the value, we just 00279 # want the TypeError raised if appropriate 00280 hash(key) 00281 00282 # find where node belongs 00283 current = self.root 00284 parent = None 00285 while current != self.sentinel: 00286 # GJB added comparison function feature 00287 # slightly improved by JCG: don't assume that == 00288 # is the same as self.__cmp(..) == 0 00289 rc = self.__cmp(key, current.key) 00290 if rc == 0: 00291 #SF This item is inserted for the second, 00292 #SF third, ... time, so we have to increment 00293 #SF the count 00294 if self.unique == False: 00295 current.count += 1 00296 else: # raise an Error 00297 print "Warning: This element is already in the list ... ignored!" 00298 #SF I don't want to raise an error because I want to keep 00299 #SF the code compatible to previous versions 00300 #SF But here would be the right place to do this 00301 #raise IndexError ("This item is already in the tree.") 00302 return current 00303 parent = current 00304 if rc < 0: 00305 current = current.left 00306 else: 00307 current = current.right 00308 00309 # setup new node 00310 x = RBNode(key, value) 00311 x.left = x.right = self.sentinel 00312 x.parent = parent 00313 00314 self.elements = self.elements + 1 00315 00316 # insert node in tree 00317 if parent: 00318 if self.__cmp(key, parent.key) < 0: 00319 parent.left = x 00320 else: 00321 parent.right = x 00322 else: 00323 self.root = x 00324 00325 self.insertFixup(x) 00326 return x 00327 00328 def deleteFixup(self, x): 00329 #************************************ 00330 # maintain Red-Black tree balance * 00331 # after deleting node x * 00332 #************************************ 00333 00334 while x != self.root and x.color == BLACK: 00335 if x == x.parent.left: 00336 w = x.parent.right 00337 if w.color == RED: 00338 w.color = BLACK 00339 x.parent.color = RED 00340 self.rotateLeft(x.parent) 00341 w = x.parent.right 00342 00343 if w.left.color == BLACK and w.right.color == BLACK: 00344 w.color = RED 00345 x = x.parent 00346 else: 00347 if w.right.color == BLACK: 00348 w.left.color = BLACK 00349 w.color = RED 00350 self.rotateRight(w) 00351 w = x.parent.right 00352 00353 w.color = x.parent.color 00354 x.parent.color = BLACK 00355 w.right.color = BLACK 00356 self.rotateLeft(x.parent) 00357 x = self.root 00358 00359 else: 00360 w = x.parent.left 00361 if w.color == RED: 00362 w.color = BLACK 00363 x.parent.color = RED 00364 self.rotateRight(x.parent) 00365 w = x.parent.left 00366 00367 if w.right.color == BLACK and w.left.color == BLACK: 00368 w.color = RED 00369 x = x.parent 00370 else: 00371 if w.left.color == BLACK: 00372 w.right.color = BLACK 00373 w.color = RED 00374 self.rotateLeft(w) 00375 w = x.parent.left 00376 00377 w.color = x.parent.color 00378 x.parent.color = BLACK 00379 w.left.color = BLACK 00380 self.rotateRight(x.parent) 00381 x = self.root 00382 00383 x.color = BLACK 00384 00385 def deleteNode(self, z, all=True): 00386 #**************************** 00387 # delete node z from tree * 00388 #**************************** 00389 00390 if not z or z == self.sentinel: 00391 return 00392 00393 #SF If the object is in this tree more than once the node 00394 #SF has not to be deleted. We just have to decrement the 00395 #SF count variable 00396 #SF we don't have to check for uniquness because this was 00397 #SF already captured in insertNode() and for this reason 00398 #SF z.count cannot be greater than 1 00399 #SF If all=True then the complete node has to be deleted 00400 if z.count > 1 and not all: 00401 z.count -= 1 00402 return 00403 00404 if z.left == self.sentinel or z.right == self.sentinel: 00405 # y has a self.sentinel node as a child 00406 y = z 00407 else: 00408 # find tree successor with a self.sentinel node as a child 00409 y = z.right 00410 while y.left != self.sentinel: 00411 y = y.left 00412 00413 # x is y's only child 00414 if y.left != self.sentinel: 00415 x = y.left 00416 else: 00417 x = y.right 00418 00419 # remove y from the parent chain 00420 x.parent = y.parent 00421 if y.parent: 00422 if y == y.parent.left: 00423 y.parent.left = x 00424 else: 00425 y.parent.right = x 00426 else: 00427 self.root = x 00428 00429 if y != z: 00430 z.key = y.key 00431 z.value = y.value 00432 00433 if y.color == BLACK: 00434 self.deleteFixup(x) 00435 00436 del y 00437 self.elements = self.elements - 1 00438 00439 def findNode(self, key): 00440 #****************************** 00441 # find node containing data 00442 #****************************** 00443 00444 # we aren't interested in the value, we just 00445 # want the TypeError raised if appropriate 00446 hash(key) 00447 00448 current = self.root 00449 00450 while current != self.sentinel: 00451 # GJB added comparison function feature 00452 # slightly improved by JCG: don't assume that == 00453 # is the same as self.__cmp(..) == 0 00454 rc = self.__cmp(key, current.key) 00455 if rc == 0: 00456 return current 00457 else: 00458 if rc < 0: 00459 current = current.left 00460 else: 00461 current = current.right 00462 00463 return None 00464 00465 def traverseTree(self, f): 00466 if self.root == self.sentinel: 00467 return 00468 s = [ None ] 00469 cur = self.root 00470 while s: 00471 if cur.left: 00472 s.append(cur) 00473 cur = cur.left 00474 else: 00475 f(cur) 00476 while not cur.right: 00477 cur = s.pop() 00478 if cur is None: 00479 return 00480 f(cur) 00481 cur = cur.right 00482 # should not get here. 00483 return 00484 00485 ## 00486 # return all nodes as a list 00487 def nodesByTraversal(self): 00488 result = [] 00489 def traversalFn(x, K=result): 00490 K.append(x) 00491 self.traverseTree(traversalFn) 00492 return result 00493 00494 ## 00495 # return all nodes as a list 00496 def nodes(self): 00497 cur = self.firstNode() 00498 result = [] 00499 while cur: 00500 result.append(cur) 00501 cur = self.nextNode(cur) 00502 return result 00503 00504 def firstNode(self): 00505 cur = self.root 00506 while cur.left: 00507 cur = cur.left 00508 return cur 00509 00510 def lastNode(self): 00511 cur = self.root 00512 while cur.right: 00513 cur = cur.right 00514 return cur 00515 00516 ## 00517 # returns None if there isn't one 00518 def nextNode(self, prev): 00519 cur = prev 00520 if cur.right: 00521 cur = prev.right 00522 while cur.left: 00523 cur = cur.left 00524 return cur 00525 while 1: 00526 cur = cur.parent 00527 if not cur: 00528 return None 00529 if self.__cmp(cur.key, prev.key)>=0: 00530 return cur 00531 00532 ## 00533 # returns None if there isn't one 00534 def prevNode(self, next): 00535 cur = next 00536 if cur.left: 00537 cur = next.left 00538 while cur.right: 00539 cur = cur.right 00540 return cur 00541 while 1: 00542 cur = cur.parent 00543 if cur is None: 00544 return None 00545 if self.__cmp(cur.key, next.key)<0: 00546 return cur 00547 00548 00549 ## 00550 # List class uses same object for key and value 00551 # Assumes you are putting sortable items into the list. 00552 # 00553 class RBList(RBTree): 00554 00555 def __init__(self, list=[], cmpfn=cmp, unique=True): 00556 #SF new option: unique trees, see RBTree.__init__() for 00557 #SF more information 00558 RBTree.__init__(self, cmpfn, unique) 00559 for item in list: 00560 self.insertNode (item, item) 00561 00562 def __getitem__ (self, index): 00563 node = self.findNodeByIndex (index) 00564 return node.value 00565 00566 def __delitem__ (self, index): 00567 node = self.findNodeByIndex (index) 00568 self.deleteNode (node) 00569 00570 def __contains__ (self, item): 00571 return self.findNode (item) is not None 00572 00573 def __str__ (self): 00574 # eval(str(self)) returns a regular list 00575 return '['+ string.join(map(lambda x: str(x.value), self.nodes()), ', ')+']' 00576 00577 def findNodeByIndex (self, index): 00578 if (index < 0) or (index >= self.elements): 00579 raise IndexError ("pop index out of range") 00580 # 00581 if index < self.elements / 2: 00582 # simple scan from start of list 00583 node = self.firstNode() 00584 currIndex = 0 00585 while currIndex < index: 00586 node = self.nextNode (node) 00587 currIndex += 1 00588 else: 00589 # simple scan from end of list 00590 node = self.lastNode() 00591 currIndex = self.elements - 1 00592 while currIndex > index: 00593 node = self.prevNode (node) 00594 currIndex -= 1 00595 # 00596 return node 00597 00598 def insert (self, item): 00599 #SF The function inserNode already checks for existing Nodes 00600 #SF so this code seems to be superfluid 00601 #node = self.findNode (item) 00602 #if node is not None: 00603 #self.deleteNode (node) 00604 # item is both key and value for a list 00605 self.insertNode (item, item) 00606 00607 def append (self, item): 00608 # list is always sorted 00609 self.insert (item) 00610 00611 #SF this function is not implemented as a common list in python 00612 #def count (self): 00613 #return len(self) 00614 00615 #SF Because we count all equal items in one node 00616 #SF we now can use the function count as used in 00617 #SF common python lists 00618 def count(self, item): 00619 node = self.findNode (item) 00620 return node.count 00621 00622 def index (self, item): 00623 index = -1 00624 node = self.findNode (item) 00625 while node is not None: 00626 node = self.prevNode (node) 00627 index += 1 00628 # 00629 if index < 0: 00630 raise ValueError ("RBList.index: item not in list") 00631 return index 00632 00633 def extend (self, otherList): 00634 for item in otherList: 00635 self.insert (item) 00636 00637 def pop (self, index=None): 00638 if index is None: 00639 index = self.elements - 1 00640 # 00641 node = self.findNodeByIndex (index) 00642 value = node.value # must do this before removing node 00643 self.deleteNode (node) 00644 return value 00645 00646 def remove (self, item, all=True): 00647 #SF When called with all=True then all occurences are deleted 00648 node = self.findNode (item) 00649 if node is not None: 00650 self.deleteNode (node, all) 00651 00652 def reverse (self): # not implemented 00653 raise AssertionError ("RBlist.reverse Not implemented") 00654 00655 def sort (self): # Null operation 00656 pass 00657 00658 ## 00659 # delete all entries 00660 def clear (self): 00661 self.__del__() 00662 #copied from RBTree constructor 00663 self.sentinel = RBNode() 00664 self.sentinel.left = self.sentinel.right = self.sentinel 00665 self.sentinel.color = BLACK 00666 self.sentinel.nonzero = 0 00667 self.root = self.sentinel 00668 self.elements = 0 00669 00670 def values (self): 00671 return map (lambda x: x.value, self.nodes()) 00672 00673 def reverseValues (self): 00674 values = map (lambda x: x.value, self.nodes()) 00675 values.reverse() 00676 return values 00677 00678 00679 class RBDict(RBTree): 00680 00681 def __init__(self, dict={}, cmpfn=cmp): 00682 RBTree.__init__(self, cmpfn) 00683 for key, value in dict.items(): 00684 self[key]=value 00685 00686 def __str__(self): 00687 # eval(str(self)) returns a regular dictionary 00688 return '{'+ string.join(map(str, self.nodes()), ', ')+'}' 00689 00690 def __repr__(self): 00691 return "<RBDict object " + str(self) + ">" 00692 00693 def __getitem__(self, key): 00694 n = self.findNode(key) 00695 if n: 00696 return n.value 00697 raise IndexError 00698 00699 def __setitem__(self, key, value): 00700 n = self.findNode(key) 00701 if n: 00702 n.value = value 00703 else: 00704 self.insertNode(key, value) 00705 00706 def __delitem__(self, key): 00707 n = self.findNode(key) 00708 if n: 00709 self.deleteNode(n) 00710 else: 00711 raise IndexError 00712 00713 def get(self, key, default=None): 00714 n = self.findNode(key) 00715 if n: 00716 return n.value 00717 return default 00718 00719 def keys(self): 00720 return map(lambda x: x.key, self.nodes()) 00721 00722 def values(self): 00723 return map(lambda x: x.value, self.nodes()) 00724 00725 def items(self): 00726 return map(tuple, self.nodes()) 00727 00728 def has_key(self, key): 00729 return self.findNode(key) <> None 00730 00731 ## 00732 # delete all entries 00733 def clear(self): 00734 00735 self.__del__() 00736 00737 #copied from RBTree constructor 00738 self.sentinel = RBNode() 00739 self.sentinel.left = self.sentinel.right = self.sentinel 00740 self.sentinel.color = BLACK 00741 self.sentinel.nonzero = 0 00742 self.root = self.sentinel 00743 self.elements = 0 00744 00745 ## 00746 # return shallow copy 00747 def copy(self): 00748 # there may be a more efficient way of doing this 00749 return RBDict(self) 00750 00751 ## 00752 # Add all items from the supplied mapping to this one. 00753 # 00754 # Will overwrite old entries with new ones. 00755 # 00756 # 00757 def update(self, other): 00758 for key in other.keys(): 00759 self[key] = other[key] 00760 00761 def setdefault(self, key, value=None): 00762 if self.has_key(key): 00763 return self[key] 00764 self[key] = value 00765 return value 00766 00767 00768 """ ---------------------------------------------------------------------------- 00769 TEST ROUTINES 00770 """ 00771 def testRBlist(): 00772 import random 00773 print "--- Testing RBList ---" 00774 print " Basic tests..." 00775 00776 initList = [5,3,6,7,2,4,21,8,99,32,23] 00777 rbList = RBList (initList) 00778 initList.sort() 00779 assert rbList.values() == initList 00780 initList.reverse() 00781 assert rbList.reverseValues() == initList 00782 # 00783 rbList = RBList ([0,1,2,3,4,5,6,7,8,9]) 00784 for i in range(10): 00785 assert i == rbList.index (i) 00786 00787 # remove odd values 00788 for i in range (1,10,2): 00789 rbList.remove (i) 00790 assert rbList.values() == [0,2,4,6,8] 00791 00792 # pop tests 00793 assert rbList.pop() == 8 00794 assert rbList.values() == [0,2,4,6] 00795 assert rbList.pop (1) == 2 00796 assert rbList.values() == [0,4,6] 00797 assert rbList.pop (0) == 0 00798 assert rbList.values() == [4,6] 00799 00800 # Random number insertion test 00801 rbList = RBList() 00802 for i in range(5): 00803 k = random.randrange(10) + 1 00804 rbList.insert (k) 00805 print " Random contents:", rbList 00806 00807 rbList.insert (0) 00808 rbList.insert (1) 00809 rbList.insert (10) 00810 00811 print " With 0, 1 and 10:", rbList 00812 n = rbList.findNode (0) 00813 print " Forwards:", 00814 while n is not None: 00815 print "(" + str(n) + ")", 00816 n = rbList.nextNode (n) 00817 print 00818 00819 n = rbList.findNode (10) 00820 print " Backwards:", 00821 while n is not None: 00822 print "(" + str(n) + ")", 00823 n = rbList.prevNode (n) 00824 00825 if rbList.nodes() != rbList.nodesByTraversal(): 00826 print "node lists don't match" 00827 print 00828 00829 def testRBdict(): 00830 import random 00831 print "--- Testing RBDict ---" 00832 00833 rbDict = RBDict() 00834 for i in range(10): 00835 k = random.randrange(10) + 1 00836 rbDict[k] = i 00837 rbDict[1] = 0 00838 rbDict[2] = "testing..." 00839 00840 print " Value at 1", rbDict.get (1, "Default") 00841 print " Value at 2", rbDict.get (2, "Default") 00842 print " Value at 99", rbDict.get (99, "Default") 00843 print " Keys:", rbDict.keys() 00844 print " values:", rbDict.values() 00845 print " Items:", rbDict.items() 00846 00847 if rbDict.nodes() != rbDict.nodesByTraversal(): 00848 print "node lists don't match" 00849 00850 # convert our RBDict to a dictionary-display, 00851 # evaluate it (creating a dictionary), and build a new RBDict 00852 # from it in reverse order. 00853 revDict = RBDict(eval(str(rbDict)),lambda x, y: cmp(y,x)) 00854 print " " + str(revDict) 00855 print 00856 00857 00858 if __name__ == "__main__": 00859 00860 import sys 00861 00862 if len(sys.argv) <= 1: 00863 testRBlist() 00864 testRBdict() 00865 sys.exit(0) 00866 00867 00868 # end of file. 00869 00870
© Copyright 2008-2009 Vyper Logix Corp., All Right Reserved; If you reference this document or any part of this document you must use the citation verbatim (including the link) "© Copyright 2008-2009 Vyper Logix Corp., All Right Reserved."
Notice: This source code contained in this document is NOT open source and is NOT being distributed as open source.
122,241 lines of code and growing...