2-3树(Python实现)

我们希望该树的高度能够维持在 lgN 左右,这样我们就能保证只需要lgN次比较操作就可以查找到想要的值。不幸的是,每次插入元素之后维持树的平衡状态太昂贵。




其中的每一个结点都具有一个或两个key,两个孩子或三个孩子
一个2结点包含一个key和两个孩子(或没有孩子)。要么就有两个,不能只有一个孩子。左子树包含小于较小元素的元素,右子树包含大于较大元素的元素
一个3结点包含一小一大两个key和三个孩子(或没有孩子)。要么就有三个,如果某个3结点有孩子的话,左子树包含小于较小元素的元素,右子树包含大于较大元素的元素,中间子树包含介于两元素之间的元素



查找



插入

­首先要进行查找,然后将节点挂到未找到的节点上。
2-3树之所以能够保证在最差的情况下的效率的原因在于其插入之后仍然能够保持平衡状态。

插入2-node结点

如果查找后未找到的节点是一个2-node节点,那么很容易,我们只需要将新的元素放到这个2-node节点里面使其变成一个3-node节点即可。



分割叶节点

往一个3-node节点插入一个新的节点可能会遇到很多种不同的情况

关于结点的上移



节点是3-node,父节点是2-node





节点是3-node,父节点也是3-node






根结点分离上移




当根节点到字节点都是3-node节点的时候,这是如果我们要在字节点插入新的元素的时候,会一直查分到跟节点,在最后一步的时候,跟节点变成了一个4-node节点,这个时候,就需要将跟节点查分为两个2-node节点,树的高度加1
­
完全平衡的2-3查找树如下图,每个根节点到叶子节点的距离是相同的:




2-3树的查找效率与树的高度是息息相关的。
•在最坏的情况下,也就是所有的节点都是2-node节点,查找效率为lgN
•在最好的情况下,所有的节点都是3-node节点,查找效率为log3N约等于0.631lgN
­
距离来说,对于1百万个节点的2-3树,树的高度为12-20之间,对于10亿个节点的2-3树,树的高度为18-30之间。
对于插入来说,只需要常数次操作即可完成,因为他只需要修改与该节点关联的节点即可,不需要检查其他节点,所以效率和查找类似。下面是2-3查找树的效率:



实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# coding:utf-8
class Node(object):
def __init__(self, key):
self.key1 = key
self.key2 = None
self.left = None
self.middle = None
self.right = None
def isLeaf(self):
return self.left is None and self.middle is None and self.right is None
def isFull(self):
return self.key2 is not None
def hasKey(self, key):
if (self.key1 == key) or (self.key2 is not None and self.key2 == key):
return True
else:
return False
def getChild(self, key):
if key < self.key1:
return self.left
elif self.key2 is None:
return self.middle
elif key < self.key2:
return self.middle
else:
return self.right
class two_three_Tree(object):
def __init__(self):
self.root = None
def get(self, key):
if self.root is None:
return None
else:
return self._get(self.root, key)
def _get(self, node, key):
if node is None:
return None
elif node.hasKey(key):
return node
else:
child = node.getChild(key)
return self._get(child, key)
def put(self, key):
if self.root is None:
self.root = Node(key)
else:
pKey, pRef = self._put(self.root, key)
if pKey is not None:
newnode = Node(pKey)
newnode.left = self.root
newnode.middle = pRef
self.root = newnode
def _put(self, node, key):
# 不重复的key
if node.hasKey(key):
return None, None
# 叶节点,直接插入
elif node.isLeaf():
return self._addtoNode(node, key, None)
# 遍历子树,直到遇到叶节点
else:
child = node.getChild(key)
pKey, pRef = self._put(child, key)
# 有 pKey, pRef 表示进行了分割
# pKey 表示往上移的key
# pRef 表示newnode
# 每次 _addtoNode 到一个 3-Node的时候,肯定会有pKey和pRef(因为需要分割上移)
if pKey is None:
return None, None
else:
return self._addtoNode(node, pKey, pRef)
def _addtoNode(self, node, key, pRef):
# 3-Node,分割
if node.isFull():
return self._splitNode(node, key, pRef)
# 2-Node
else:
# 放入该node的左边
if key < node.key1:
node.key2 = node.key1
node.key1 = key
# 如果该node有子树
if pRef is not None:
node.right = node.middle
node.middle = pRef
else:
node.key2 = key
if pRef is not None:
node.right = pRef
# pKey, pRef
return None, None
def _splitNode(self, node, key, pRef):
# 这是难点,请对照插入的图例,认真学习
newnode = Node(None)
if key < node.key1:
# x是最小的key,把key1移上去
pKey = node.key1
node.key1 = key
newnode.key1 = node.key2
if pRef is not None:
newnode.left = node.middle
newnode.middle = node.right
node.middle = pRef
elif key < node.key2:
# x是中间的key,把自己移上去
pKey = key
newnode.key1 = node.key2
if pRef is not None:
newnode.left = pRef
newnode.middle = node.right
else:
# x是最大的key,把key2
pKey = node.key2
newnode.key1 = key
if pRef is not None:
newnode.left = node.right
newnode.middle = pRef
node.key2 = None
return pKey, newnode
文章目录
  1. 1. 查找
  2. 2. 插入
    1. 2.1. 插入2-node结点
    2. 2.2. 分割叶节点
      1. 2.2.1. 关于结点的上移
      2. 2.2.2. 节点是3-node,父节点是2-node
      3. 2.2.3. 节点是3-node,父节点也是3-node
  3. 3. 实现
|