[英]Most optimal way of writing sparse array not using any libraries nor hash table
给定一个大数组,其中绝大多数元素为零。 创建一个 class 可以更有效地存储这些元素的空间。 你的class必须有以下方法:
constructor(originalArr)
- 你传入原始数组来存储set(i, val)
- 在索引 i 处设置值 valget(i)
- 获取索引 i 处的值想出一个不使用字典/hash 表的数据结构。
EXAMPLE(S) sparseArr = new SparseArray([0, 0, 1, 0, 0, 0, 0, 2]) sparseArr.get(0) // returns 0 sparseArr.set(0, 3) sparseArr.get(0) // returns 3 sparseArr.get(2) // returns 1 FUNCTION SIGNATURE class SparseArray: constructor(original) set(i, val) get(i)
我正在考虑拥有三个 arrays、non_zero_arr、non_zero_indices_arr 和 zero_indices_arr 但是当我想到将零值设置为非零的情况时,我会在 zero_indices_arr 中使用二进制搜索来查找给定索引是否存在于 zero_indices_arr 中,删除数组中的索引(使用数组切片?)再次通过二进制搜索在non_zero_indices_arr中找到position,然后使用数组切片添加放置新的更新索引并使用其在non_zero_indices_arr中的索引通过使用数组更新其在non_zero_arr中的non_zero_value再次切片。
我想每个操作的时间复杂度是
构造函数的 O(n) [用于在适当命名的数组中记录索引、零和非零]
O(log n) for get [用于非零或零索引数组中的二进制搜索索引]
O(log n + n) [搜索然后切片数组并可能添加更新值]
但对我来说,这感觉有点混乱或坚韧不拔。 有没有更优化更清洁的方法来做到这一点? 鉴于这是一个面试问题? 请随时纠正我的思路或提出您的最佳解决方案和时间复杂度。
先感谢您
有很多数据结构可以处理这些需求,包括:
另一个是按位 trie 。
这是一个准系统按位 trie 实现,其中每个节点只处理给定索引的一位,因此它是一棵二叉树。 树的高度将等于数据结构必须为其存储值的最大索引的二进制表示中的位数。
class SparseArray:
def __init__(self, original=None):
self.height = self.tree = 0
if original:
for i, val in enumerate(original):
if val:
self.set(i, val)
def remove(self, i):
def recur(node, shift, i):
j = (i >> shift) & 1
node[j] = shift and recur(node[j], shift - 1, i & ((1 << shift) - 1))
return node if any(node) else 0
if i.bit_length() <= self.height:
self.tree = recur(self.tree, self.height - 1, i)
# Reduce height if possible
if self.tree != 0:
while self.height > 1 and self.tree[1] == 0:
self.height -= 1
self.tree = self.tree[0]
else:
self.height = 0
def set(self, i, val):
if val == 0: # We don't store zeroes...
return self.remove(i)
# Increase tree height if this index needs more bits
width = i.bit_length()
if self.height < width and self.tree != 0:
for _ in range(self.height, width):
self.tree = [self.tree, 0]
self.height = max(width, self.height)
# Drill down
if not self.tree:
self.tree = [0]*2
node = self.tree
for bit in range(self.height - 1, 0, -1):
side = (i >> bit) & 1
if node[side] == 0:
node[side] = [0]*2
node = node[side]
node[i & 1] = val # Set leaf's value
def get(self, i):
node = self.tree
if i.bit_length() > self.height: # Out of current range
return 0
node = self.tree
for bit in range(self.height - 1, 0, -1):
side = (i >> bit) & 1
node = node[side]
if node == 0: # No subtree here
return 0
return node[i & 1] # Get leaf's value
def items(self):
def recur(node, i):
if isinstance(node, list):
yield from recur(node[0], i*2)
yield from recur(node[1], i*2+1)
elif node != 0:
yield i, node
yield from recur(self.tree, 0)
最坏情况下的空间复杂度为 O(),其中 是存储在稀疏数组中的非零值的个数, 是用于非零值的最大索引的二进制表示中的位数。 最坏情况的一个例子是所有索引都是 2 的连续幂。
最好的情况空间复杂度是 O()。 这是二叉树完美的时候,这意味着数组中没有间隙(它不是稀疏的)。
get
和set
的时间复杂度是O()。
当节点获得更大的分支因子(而不是 2)并因此处理更大的位块而不是 1 时,性能将会提高。比如 4 位。
存在几种改进 memory 和速度的变体。 请参阅 bitmap 的按位树,包括 CTPOP 位破解实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.