自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python高級(jí)算法與數(shù)據(jù)結(jié)構(gòu):使用treap實(shí)現(xiàn)雙索引之一

開發(fā) 后端 大數(shù)據(jù) 算法
前面介紹的堆結(jié)構(gòu)只能對(duì)數(shù)據(jù)進(jìn)行部分排序,也就是它只能知道部分元素的排序,例如從根節(jié)點(diǎn)出發(fā),沿著左孩子或右孩子前行,我們能得知所遍歷的元素一定是遞增(小堆)或是遞減(大堆)關(guān)系,但是我們無法得知左子樹與右子樹兩部分節(jié)點(diǎn)的排序關(guān)系。

\

前面介紹的堆結(jié)構(gòu)只能對(duì)數(shù)據(jù)進(jìn)行部分排序,也就是它只能知道部分元素的排序,例如從根節(jié)點(diǎn)出發(fā),沿著左孩子或右孩子前行,我們能得知所遍歷的元素一定是遞增(小堆)或是遞減(大堆)關(guān)系,但是我們無法得知左子樹與右子樹兩部分節(jié)點(diǎn)的排序關(guān)系。

在很多應(yīng)用場景下,我們不但需要堆的特性,例如快速知道數(shù)據(jù)最大值或最小值,同時(shí)還需要知道元素的排序信息,因此本節(jié)我們看看如何實(shí)現(xiàn)魚和熊掌如何兼得。假設(shè)我們有一系列數(shù)據(jù),它的元素由兩部分組成,一部分對(duì)應(yīng)商品的名稱,其類型為字符串,一部分對(duì)應(yīng)商品的貨存數(shù)量,類型為整形,我們既需要將商品根據(jù)其名稱排序,同時(shí)我們又需要快速查詢當(dāng)前貨存最小的商品,我們?nèi)绾卧O(shè)計(jì)相應(yīng)的算法和數(shù)據(jù)結(jié)構(gòu)來滿足這樣特性呢。

舉個(gè)例子,如下圖:

從上圖看,它對(duì)應(yīng)元素字符串是排序二叉樹,因此根節(jié)點(diǎn)左子樹對(duì)應(yīng)元素的字符串都小于根字符串,同時(shí)右子樹對(duì)應(yīng)的字符串都大于根節(jié)點(diǎn)字符串,同時(shí)每個(gè)元素還對(duì)應(yīng)著相應(yīng)商品的貨存數(shù)量,我們需要及時(shí)掌握當(dāng)前貨存最少的商品,這樣才能在其耗盡之前迅速補(bǔ)貨。但是從上圖可以看到,要保證字符串的排序性就得犧牲對(duì)于商品數(shù)量的小堆性質(zhì),例如上圖中water對(duì)應(yīng)的貨存與wine對(duì)應(yīng)的貨存違背了小堆的性質(zhì),現(xiàn)在問題是如何在保證字符串排序的情況下,確保數(shù)量同時(shí)能滿足小堆性質(zhì)。

首先我們先定義一下數(shù)據(jù)結(jié)構(gòu):

  1. class Node: 
  2.     def __init__(self, key: str, priority: float): 
  3.         self._key = key 
  4.         self._priority = priority 
  5.         self._left: Node = None 
  6.         self._right: Node = None 
  7.         self._parent: Node = None 
  8.  
  9.     @property 
  10.     def left(self): 
  11.         return self._left 
  12.  
  13.     @property 
  14.     def right(self): 
  15.         return self._right 
  16.  
  17.     @property 
  18.     def parent(self): 
  19.         return self._parent 
  20.  
  21.     @left.setter 
  22.     def left(self, node): 
  23.         self._left = node 
  24.         if node is not None: 
  25.             node.parent = self 
  26.  
  27.     @right.setter 
  28.     def right(self, node): 
  29.         self._right = node 
  30.         if node is not None: 
  31.             node.parent = self 
  32.  
  33.     @parent.setter 
  34.     def parent(self, node): 
  35.         self._parent = node 
  36.  
  37.     def is_root(self) -> bool: 
  38.         if self.parent is None: 
  39.             return True 
  40.         return False 
  41.  
  42.     def __repr__(self): 
  43.         return "({}, {})".format(self._key, self._priority) 
  44.  
  45.     def __str__(self): 
  46.         repr_str: str = "" 
  47.         repr_str += repr(self) 
  48.         if self.parent is not None: 
  49.             repr_str += " parent: " + repr(self.parent) 
  50.         else
  51.             repr_str += " parent: None" 
  52.  
  53.         if self.left is not None: 
  54.             repr_str += " left: " + repr(self.left
  55.         else
  56.             repr_str += " left: None" 
  57.  
  58.         if self.right is not None: 
  59.             repr_str += " right: " + repr(self.right
  60.         else
  61.             repr_str += " right: None" 
  62.  
  63.         return repr_str 
  64.  
  65. class Treap: 
  66.     def  __init__(self): 
  67.         self.root : Node = None 

當(dāng)前問題是,當(dāng)上圖所示的矛盾出現(xiàn)時(shí),我們?nèi)绾握{(diào)整,使得字符串依然保持排序性質(zhì),同時(shí)貨存數(shù)值能滿足小堆性質(zhì)。我們需要根據(jù)幾種情況采取不同操作,首先看第一種,如下圖:

從上圖看到,一種情況是父節(jié)點(diǎn)與左孩子在數(shù)值上違背了堆的性質(zhì),此時(shí)我們執(zhí)行一種叫右旋轉(zhuǎn)操作,其步驟是,1,Beer節(jié)點(diǎn)逆時(shí)針旋轉(zhuǎn),替換其父節(jié)點(diǎn);2,父節(jié)點(diǎn)Cabbage順時(shí)針旋轉(zhuǎn),成為Beer的右孩子節(jié)點(diǎn);3,原來Beer的右孩子節(jié)點(diǎn)轉(zhuǎn)變?yōu)镃abbage的左孩子節(jié)點(diǎn);完成后結(jié)果如下圖所示:

可以看到,此時(shí)字符串依然保持排序二叉樹性質(zhì),同時(shí)數(shù)值對(duì)應(yīng)的小堆性質(zhì)也得到了滿足。我們看看代碼實(shí)現(xiàn):

  1. class Treap: 
  2.     def __init__(self): 
  3.         self._root: Node = None 
  4.  
  5.     def right_rotate(self, x: Node): 
  6.         if x is None or x.is_root() is True
  7.             return 
  8.  
  9.         y = x.parent 
  10.         if y.left != x:  # 必須是左孩子才能右旋轉(zhuǎn) 
  11.             return 
  12.  
  13.         p = y.parent 
  14.         if p is not None:  # 執(zhí)行右旋轉(zhuǎn) 
  15.             if p.left == y: 
  16.                 p.left = x 
  17.             else
  18.                 p.right = x 
  19.         else
  20.             self._root = x 
  21.  
  22.         y.left = x.right 
  23.         x.right = y 

接下來我們構(gòu)造一些數(shù)據(jù)測試一下上面的實(shí)現(xiàn)是否正確:

  1. def setup_right_rotate(): 
  2.     flour: Node = Node("Flour", 10) 
  3.     cabbage: Node = Node("Cabbage", 77) 
  4.     beer: Node = Node("Beer", 76) 
  5.     bacon: Node = Node("Bacon", 95) 
  6.     butter: Node = Node("Butter", 86) 
  7.  
  8.     flour.parent = None 
  9.     flour.left = cabbage 
  10.     flour.right = None 
  11.     cabbage.left = beer 
  12.  
  13.  
  14.     beer.left = bacon 
  15.     beer.right = butter 
  16.  
  17.     return flour, beer 
  18.  
  19. def print_treap(n: Node): 
  20.     if n is None: 
  21.         return 
  22.  
  23.     print(n) 
  24.     print_treap(n.left
  25.     print_treap(n.right
  26.  
  27. treap = Treap() 
  28. root, x , cabbage = setup_right_rotate() 
  29. print("---------before right rotate---------:"
  30. print_treap(root) 
  31. treap.right_rotate(x) 
  32. print("-------after right rotate-------"
  33. print_treap(root) 

上面代碼執(zhí)行后輸出內(nèi)容如下:

  1. ---------before right rotate---------: 
  2. (Flour, 10) parent: None left: (Cabbage, 77) right: None 
  3. (Cabbage, 77) parent: (Flour, 10) left: (Beer, 76) right: (Eggs, 129) 
  4. (Beer, 76) parent: (Cabbage, 77) left: (Bacon, 95) right: (Butter, 86) 
  5. (Bacon, 95) parent: (Beer, 76) left: None right: None 
  6. (Butter, 86) parent: (Beer, 76) left: None right: None 
  7. (Eggs, 129) parent: (Cabbage, 77) left: None right: None 
  8. -------after right rotate------- 
  9. (Flour, 10) parent: None left: (Beer, 76) right: None 
  10. (Beer, 76) parent: (Flour, 10) left: (Bacon, 95) right: (Cabbage, 77) 
  11. (Bacon, 95) parent: (Beer, 76) left: None right: None 
  12. (Cabbage, 77) parent: (Beer, 76) left: (Butter, 86) right: (Eggs, 129) 
  13. (Butter, 86) parent: (Cabbage, 77) left: None right: None 
  14. (Eggs, 129) parent: (Cabbage, 77) left: None right: None 

對(duì)比右旋轉(zhuǎn)前后輸出的二叉樹看,旋轉(zhuǎn)后的二叉樹打印信息的確跟上面我們旋轉(zhuǎn)后對(duì)應(yīng)的圖像是一致的。接下來我們實(shí)現(xiàn)左旋轉(zhuǎn),先把上圖中cabbage節(jié)點(diǎn)對(duì)應(yīng)的值改成75,這樣它與父節(jié)點(diǎn)就違背了小堆性質(zhì):

我們要做的是:1,把cabbage節(jié)點(diǎn)向“左”旋轉(zhuǎn)到beer的位置;2,beer的父節(jié)點(diǎn)設(shè)置為cabbage;3:beer的右孩子設(shè)置為cabbage的左孩子;4,cabbage的左孩子變成beer;左旋轉(zhuǎn)后二叉樹應(yīng)該成形如下:

從上圖看,左旋轉(zhuǎn)后,字符串依然保持二叉樹排序性,同時(shí)數(shù)值的排放也遵守小堆原則,我們看相應(yīng)的代碼實(shí)現(xiàn):

  1. class Treap: 
  2.    ... 
  3.  
  4.     def left_rotate(self, x : Node): 
  5.         if x is None or x.is_root() is True
  6.             return 
  7.  
  8.         y = x.parent 
  9.         if y.right is not x: # 只有右孩子才能左旋轉(zhuǎn) 
  10.             return 
  11.  
  12.         p = y.parent 
  13.         if p is not None: 
  14.             if p.left is y: 
  15.                 p.left = x 
  16.             else
  17.                 p.right = x 
  18.         else
  19.             self._root = x 
  20.  
  21.         y.right = x.left 
  22.         x.left = y 

為了測試上面代碼實(shí)現(xiàn),我們首先把cabbage的值修改,然后調(diào)用上面代碼:

  1. cabbage._priority = 75 
  2. print("-------before left rotate--------"
  3. print_treap(root) 
  4. treap.left_rotate(cabbage) 
  5. print("-------after left rotate---------"
  6. print_treap(root) 

代碼運(yùn)行后輸出結(jié)果為:

  1. -------before left rotate-------- 
  2. (Flour, 10) parent: None left: (Beer, 76) right: None 
  3. (Beer, 76) parent: (Flour, 10) left: (Bacon, 95) right: (Cabbage, 75) 
  4. (Bacon, 95) parent: (Beer, 76) left: None right: None 
  5. (Cabbage, 75) parent: (Beer, 76) left: (Butter, 86) right: (Eggs, 129) 
  6. (Butter, 86) parent: (Cabbage, 75) left: None right: None 
  7. (Eggs, 129) parent: (Cabbage, 75) left: None right: None 
  8. -------after left rotate--------- 
  9. (Flour, 10) parent: None left: (Cabbage, 75) right: None 
  10. (Cabbage, 75) parent: (Flour, 10) left: (Beer, 76) right: (Eggs, 129) 
  11. (Beer, 76) parent: (Cabbage, 75) left: (Bacon, 95) right: (Butter, 86) 
  12. (Bacon, 95) parent: (Beer, 76) left: None right: None 
  13. (Butter, 86) parent: (Beer, 76) left: None right: None 
  14. (Eggs, 129) parent: (Cabbage, 75) left: None right: None 

輸出結(jié)果的描述與上圖左旋轉(zhuǎn)后的結(jié)果是一致的。由于Treap相對(duì)于元素的key是排序二叉樹,因此在給定一個(gè)字符串后,我們很容易查詢字符串是否在Treap中,其本質(zhì)就是排序二叉樹的搜索,其實(shí)現(xiàn)我們暫時(shí)忽略。

雖然查詢很簡單,但是插入節(jié)點(diǎn)則稍微麻煩,因?yàn)椴迦牒?,新?jié)點(diǎn)與其父節(jié)點(diǎn)可能會(huì)違背小堆性質(zhì),因此在完成插入后,我們還需使用上面實(shí)現(xiàn)的左旋轉(zhuǎn)或右旋轉(zhuǎn)來進(jìn)行調(diào)整。

 

責(zé)任編輯:武曉燕 來源: Coding迪斯尼
相關(guān)推薦

2023-09-25 12:23:18

Python

2011-07-11 15:03:36

MySQL索引數(shù)據(jù)結(jié)構(gòu)

2020-10-21 14:57:04

數(shù)據(jù)結(jié)構(gòu)算法圖形

2023-03-08 08:03:09

數(shù)據(jù)結(jié)構(gòu)算法歸并排序

2023-10-27 07:04:20

2021-07-15 06:43:12

Python數(shù)據(jù)結(jié)構(gòu)

2022-01-09 17:41:37

python算法

2023-03-07 08:02:07

數(shù)據(jù)結(jié)構(gòu)算法數(shù)列

2023-03-02 08:15:13

2023-03-10 08:07:39

數(shù)據(jù)結(jié)構(gòu)算法計(jì)數(shù)排序

2023-10-06 20:21:28

Python鏈表

2023-04-27 09:13:20

排序算法數(shù)據(jù)結(jié)構(gòu)

2023-02-08 07:52:36

跳躍表數(shù)據(jù)結(jié)構(gòu)

2023-10-30 08:31:42

數(shù)據(jù)結(jié)構(gòu)算法

2023-03-13 10:08:31

數(shù)據(jù)結(jié)構(gòu)算法

2023-11-06 06:43:23

單鏈表查詢數(shù)據(jù)結(jié)構(gòu)

2017-08-31 09:45:43

JavaArrayList數(shù)據(jù)

2023-09-15 10:33:41

算法數(shù)據(jù)結(jié)構(gòu)

2011-07-11 16:05:42

MySQL索引

2021-05-12 09:07:09

Java數(shù)據(jù)結(jié)構(gòu)算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)