查询
- 顺序查询
过程:从头到为遍历数组,直到查询到所要查询的值未止
时间复杂度:O(n)
实现:for each循环遍历即可
- 折半查询
目的:顺序查询效率慢,对顺序查询进行优化
前提:序列S已有序;存储采用数组
过程:
- 取数组中间值a,中间索引half,开始索引start,结尾索引end
- 若end = start + 1,跳出3循环,返回-1——未找到
- 若a满足要求,返回a的索引;若a<目的值,序列S缩短为S[start:end],重复1操作;若a>目的值,S缩短未S[half:end],重复1操作
代码实现:
class Search:
@classmethod
def half_search(cls, array, value):
start = 0
end = len(array)
while start+1 < end:
half = (start+end)>>1
if array[half] == value:
return half
elif array[half] < value:
start = half
else:
end = half
pass
if value == array[start]:
return start
return -1
- 插值查询
目的:对折半查找优化,当数据均匀分布时,查询效率高于折半查询
基础:序列S采用数组存储;序列已有序
思想:一本500页的书(数组,已排序),若想找到第10页内容(查询数据),我们第10页定位在前面一部分范围内
区别:折半查找每次选取数组中位数作为分割点进行比较,插值法则是自适应的选择分割点。
代码实现:
import random
from collections import Iterable
array = [x for x in range(0, 100)]
random.shuffle(array)
class Search:
@classmethod
def insert_search(cls, array, key):
start = 0
end = len(array)
# 自适应计算中间点,而不是傻瓜式取最中间
while start+1 < end:
half = start + int((key-array[start])/(array[end-1] - array[start]) * (end-start))
if half >= end or half < start:
return -1
if array[half] == key:
return half
elif array[half] < key:
# 问题补丁(若key不存在,且key位于array[start: end]区间内,
#
if start == half:
return -1
start = half
else:
end = half
pass
if key == array[start]:
return start
if start == half:
return -1
start = half
else:
end = half
pass
if key == array[start]:
return start
- 斐波那契查询
- 类似折半查找和插值查找,确定mid方式不同
- 斐波那契序列a0 = 1, a1 = 1, an = an-1 + an-2, 当n-->无穷时,an-1/an趋近于黄金比例。
- 将序列S扩增到长度未an - 1(a为i斐波那契序列),mid取an-1 - 1
- 树查询
采用排序树数据结构,存储数据,加快查询性能(最大查询次数即为树的深度)。最基本的数据结构为二叉排序树。基于此优化,又有2-3查找树,红黑树,B/B+树,解决二叉排序树可能出现时间性能退化为O(n)的问题
二叉排序树
数据结构:二叉排序树
特点:任意节点值>其左子树所有值,且<右子树所有值
时间复杂度:二叉树为完全二叉树甚至时,查询性能最佳O(log2n)。但是,当树“歪”左或者“歪”右时,时间性能下降,最差退化为O(n)【所有数据都偏向一边,如同链表】。
空间复杂度:维护二叉树基本结构即可。
代码实现:
class BinaryTree:
def __init__(self, val, **kargs):
try:
left = kargs['left']
right = kargs['right']
except KeyError:
self.val = val
self.left = None
self.right = None
return
if not isinstance(left, BinaryTree) or not isinstance(right, BinaryTree):
raise Exception('left or right must be the object of BinaryTree')
self.left = left
self.right = right
self.val = val
pass
# 中序遍历
def b_tree(self):
if self.left is not None:
self.left.b_tree()
print(self.val)
if self.right is not None:
self.right.b_tree()
def insert(self, val):
if val < self.val:
if self.left is None:
left = BinaryTree(val)
self.left = left
return
self.left.insert(val)
elif val > self.val:
if self.right is None:
right = BinaryTree(val)
self.right = right
return
self.right.insert(val)
else:
return
def get(self, key):
if key==self.val:
return self
elif key<self.val:
if self.left is not None:
return self.left.get(key)
elif key>self.val:
if self.right is not None:
return self.right.get(key)
return None
- 树查询
采用排序树数据结构,存储数据,加快查询性能(最大查询次数即为树的深度)。最基本的数据结构为二叉排序树。基于此优化,又有2-3查找树,红黑树,B/B+树,解决二叉排序树可能出现时间性能退化为O(n)的问题
二叉排序树
数据结构:二叉排序树
特点:任意节点值>其左子树所有值,且<右子树所有值
时间复杂度:二叉树为完全二叉树甚至时,查询性能最佳O(log2n)。但是,当树“歪”左或者“歪”右时,时间性能下降,最差退化为O(n)【所有数据都偏向一边,如同链表】。
空间复杂度:维护二叉树基本结构即可。
代码实现:
class BinaryTree:
def __init__(self, val, **kargs):
try:
left = kargs['left']
right = kargs['right']
except KeyError:
self.val = val
self.left = None
self.right = None
return
if not isinstance(left, BinaryTree) or not isinstance(right, BinaryTree):
raise Exception('left or right must be the object of BinaryTree')
self.left = left
self.right = right
self.val = val
pass
def b_tree(self):
if self.left is not None:
self.left.b_tree()
if self.val is not None:
print(self.val)
if self.right is not None:
self.right.b_tree()
def insert(self, val):
if val < self.val:
if self.left is None:
left = BinaryTree(val)
self.left = left
return
self.left.insert(val)
elif val > self.val:
if self.right is None:
right = BinaryTree(val)
self.right = right
return
self.right.insert(val)
else:
return
def get(self, key):
if key==self.val:
return self
elif key<self.val:
if self.left is not None:
return self.left.get(key)
elif key>self.val:
if self.right is not None:
return self.right.get(key)
return None
二三树
数据结构:二三树。每个节点拥有哦2/3个字节点。
特色:
- 2节点:key1,左子树,右子树。Key1>左子树上所有key且<右子树上所有key,同普通二叉树。
- 3节点:key1,key2,左子树,中子树,右子树。Key1>左子树上所有key,key1>中子树上所有key>key2,key2<右子树上所有key
- 自平衡树(插入规则保证树平衡)
- 插入数据时,查询二叉树为向下生长,二三树为向上生长
时间复杂度:最坏情况,所有节点均为2节点,查询时间复杂度O(log2n),最佳情况为所有节点均为3节点,查询时间复杂度O(log3n)
插入(val)(叶子节点操作)(过程逻辑较为复杂,此描述暂时有问题):
a.
b.
c.
d.
- 若根结点为None,实例化root(2节点)
- 若节点为2节点,则变为3节点(key1 not None, key2 None)
- 若节点为3节点:
- 短暂变为4节点(key1,key2,key_tmp)
- 提取mid(key1, key2, key_tmp),插入到parent,key1与key2分裂为2个2节点(key1_node, key2_node)
- 若parent为2-->3节点,mid(key1, key2, key_tmp),key2_node(分裂前结点为parent的left,反之亦然)节点挂载到parent的mid节点,key1_node保持为parent的left,插入完成,跳到4;
- 若parent为3-->4节点,(假设此时节点为parent的right节点),将parent分裂为parent_left_node,parent_right_node,将mid(parent_key1,parent_key2, key_tmp【子孙插入】)继续上抛,跳到c;原parent的mid节点和left节点归为parent_left_node子孙; 原left,right归入到parent_right_node;parent_left_node与parent_right_node
4. end
红黑树
特性:
- 二叉树
- 节点分为红色/黑色 (默认插入为红色)
- Root节点为黑色
- 红色节点子节点为黑色【无两个连续红点】
- 任意节点到其叶子节点所经过黑色节点数目相同哦【保证其平衡】
自平衡(大致平衡):
当插入或者删除其中某个节点后(红),可能出现不符合红黑树定义。通过变色,左旋转,右旋转,重新平衡红黑树
与平衡二叉树区别:
放弃完全平衡,追求大致平衡。完全平衡条件苛刻,每次修改后旋转次数未知。红黑树放弃了完全平衡,每次插入最多3次旋转(逻辑简化),而性能保持在对数级
时间复杂度:O(log2n),最差不超过O(2log2n)
应用:当Java中HashMap某个桶的数据(哈希分桶分到同一个桶)超过了门槛,则将桶由链表重构为红黑树,提高时间性能
实现:
Ps:参考Java中HashMap对红黑树的实现(python其实现逻辑)【经过测试:插入10,11,12,13,14,15,16,17,18,19,20,21后,根右子树深度比左子树+2】:
class Red_Black_Tree:
class Node:
def __init__(self, val):
self.parent = None
self.left = None
self.right = None
self.red = True
self.key = val
pass
def root(self):
r = self
p = r.parent
while p is not None:
r = p
p = r.parent
return r
def putNode(self, node):
if self.parent==None:
r = self
root = self
else:
r = self.root()
root = r
node.red = True
dir = 0
while True:
if r.key > node.key:
dir = -1
if r.left is not None:
r = r.left
else:
break
elif r.key == node.key and node is not None:
return node
else:
dir = 1
if r.right is not None:
r = r.right
else:
break
pass
xp = r
if dir <= 0:
xp.left = node
pass
else:
xp.right = node
pass
node.parent = r
self.balance_insert(node, root)
pass
def balance_insert(self, node, root):
node.red = True
while True:
if node.parent is None:
node.red = False
return node
elif not node.parent.red or node.parent.parent is None:
return root
xppl = node.parent.parent.left
xppr = node.parent.parent.right
if node.parent == xppl:
if xppr is not None and xppr.red:
xppr.red = False
node.parent.red = False
xppr.parent.red = True
node = xppr.parent
pass
else:
if node == node.parent.right:
node = node.parent
# xpp = (xp = x.parent) == null ? null : xp.parent;
self.rotateLeft(root, node)
if node.parent != None:
node.parent.red = False
if node.parent.parent != None:
node.parent.parent.red = True
self.rotateRight(root, node.parent.parent)
pass
pass
pass
pass
else:
if xppl is not None and xppl.red:
xppl.red = False
xppl.parent.red = True
node.parent.red = False
node = xppl.parent
pass
else:
if node == node.parent.left:
node = node.parent
self.rotateRight(root, node)
pass
if node.parent is not None:
node.parent.red = False
if node.parent.parent is not None:
node.parent.parent.red = True
root = self.rotateLeft(root, node.parent.parent)
pass
pass
pass
pass
pass
def rotateRight(self, root, node):
l = node.left
if node is not None and l is not None:
node.left = l.right
lr = node.left
if lr is not None:
lr.parent = node
l.parent = node.parent
pp = l.parent
if pp is None:
root = l
root.red = False
elif pp.right==node:
pp.right = l
else:
pp.left = l
l.right = node
node.parent = l
return root
def rotateLeft(self, root, node):
r = node.right
if node is not None and r is not None:
node.right = r.left
rl = node.right
if rl is not None:
rl.parent = node
pass
r.parent = node.parent
pp = r.parent
if pp is None:
root = r
root.red = False
pass
elif pp.left == node:
pp.left = r
pass
else:
pp.right = r
pass
r.left = node
node.parent = r
return root
def __init__(self, val):
self.root = self.Node(val)
self.root.red = False
pass
def insert(self, val):
self.root.putNode(self.Node(val))
self.root = self.root.root()
pass
pass
B-Tree
二三树扩展。二三树每个结点至多3个子节点。B-Tree每个节点至多M个节点,M-1个key,其他规则与二三树相同(包括插入,查询)。
有点复杂:不会实现(插入过程见图1)
图1
B+Tree
对于B-Tree的变形,区别:
- 值保存在叶子节点中,非叶子节点中的key只起到索引作用【B-Tree中节点的key值,既是索引,也是值】
- 所有叶子节点由指针,连接成一条有序链表
插入过程见图2
图2
- 分块查询
基本思想:水洼找鱼总比海底捞针容易
做法:将数据(水)倒入几个有序排列桶中,每次查询只需要在某一个桶进行查找就好(桶有序,而桶中数据无序)
自我理解:只是通过分桶,将数据量减少,从而提高查询效率
@classmethod
def bult_search(cls, array, val):
pool = list()
for i in range(7):
pool.append(list())
# 分块,我此处分为7个有序的桶
for i in range(0, len(array)):
index = array[i]//7
if index > 6:
index = 6
elif index < 0:
index = 0
pool[index].append(array[i])
index = val//7
if index > 6:
index = 6
elif index < 0:
index = 0
length = len(pool[index])
for i in range(length):
if pool[index][i]==val:
return (index, i)
return (-1, -1)
- Hash查询
原理:若在获取查询值的时候,直接计算出所应该在的位置,则查询速度将会大大提高
行为:利用hash函数,传入查询值val,直接计算出位置index。
理解:冲突问题,若两个不同的值映射到同一个位置(产生冲突),如何解决【后移一位,或者链表】;效率问题:理论上,一个合理的hash函数能将时将性能提升到O(1),但当hash函数不合理,导致频频冲突,会降低查询性能【原因:每次冲突,都需要去访问下一个节点】。
Java 中HashMap<K, V>便是对该算法的实现