是否有任何已知的数据结构提供O(1)随机访问,而不使用大小为O(N)或更大的连续内存块?这是受到this answer的启发,并且被要求出于好奇而不是任何特定的实际用例,尽管它可能在一个严重碎片堆的情况下有用。
答案 0 :(得分:5)
是的,这是C ++中的一个例子:
template<class T>
struct Deque {
struct Block {
enum {
B = 4*1024 / sizeof(T), // use any strategy you want
// this gives you ~4KiB blocks
length = B
};
T data[length];
};
std::vector<Block*> blocks;
T& operator[](int n) {
return blocks[n / Block::length]->data[n % Block::length]; // O(1)
}
// many things left out for clarity and brevity
};
与std :: deque的主要区别在于它有O(n)push_front而不是O(1),实际上在实现std :: deque以使 all 时出现了一些问题的:
也许我误解了“没有使用大小为O(N)或更大的连续内存块”,这看起来很尴尬。你能澄清一下你想要的吗?我已经解释为“没有单个分配包含所表示序列中每个项目的一个项目”,例如有助于避免大量分配。 (尽管我确实为矢量分配了一个N / B大小。)
如果我的答案不符合您的定义,那么除非您人为地限制容器的最大尺寸,否则什么都不会。 (例如,我可以将您限制为LONG_MAX项,将上述块存储在树中,并调用O(1)查找。)
答案 1 :(得分:3)
您可以使用密钥长度有限的trie。由于使用长度为m
的密钥在trie中查找为O(m)
,如果我们绑定密钥的长度,则绑定m
,现在查找为O(1)
。
所以想想一下这里的键是字符串{ 0, 1 }
上的字符串(即,我们认为键是整数的二进制表示)。如果我们将键的长度限制为32个字母,我们可以将结构视为由32位整数索引,并且可以在O(1)
时间内随机访问。
以下是C#中的实现:
class TrieArray<T> {
TrieArrayNode<T> _root;
public TrieArray(int length) {
this.Length = length;
_root = new TrieArrayNode<T>();
for (int i = 0; i < length; i++) {
Insert(i);
}
}
TrieArrayNode<T> Insert(int n) {
return Insert(IntToBinaryString(n));
}
TrieArrayNode<T> Insert(string s) {
TrieArrayNode<T> node = _root;
foreach (char c in s.ToCharArray()) {
node = Insert(c, node);
}
return _root;
}
TrieArrayNode<T> Insert(char c, TrieArrayNode<T> node) {
if (node.Contains(c)) {
return node.GetChild(c);
}
else {
TrieArrayNode<T> child = new TrieArray<T>.TrieArrayNode<T>();
node.Nodes[GetIndex(c)] = child;
return child;
}
}
internal static int GetIndex(char c) {
return (int)(c - '0');
}
static string IntToBinaryString(int n) {
return Convert.ToString(n, 2);
}
public int Length { get; set; }
TrieArrayNode<T> Find(int n) {
return Find(IntToBinaryString(n));
}
TrieArrayNode<T> Find(string s) {
TrieArrayNode<T> node = _root;
foreach (char c in s.ToCharArray()) {
node = Find(c, node);
}
return node;
}
TrieArrayNode<T> Find(char c, TrieArrayNode<T> node) {
if (node.Contains(c)) {
return node.GetChild(c);
}
else {
throw new InvalidOperationException();
}
}
public T this[int index] {
get {
CheckIndex(index);
return Find(index).Value;
}
set {
CheckIndex(index);
Find(index).Value = value;
}
}
void CheckIndex(int index) {
if (index < 0 || index >= this.Length) {
throw new ArgumentOutOfRangeException("index");
}
}
class TrieArrayNode<TNested> {
public TrieArrayNode<TNested>[] Nodes { get; set; }
public T Value { get; set; }
public TrieArrayNode() {
Nodes = new TrieArrayNode<TNested>[2];
}
public bool Contains(char c) {
return Nodes[TrieArray<TNested>.GetIndex(c)] != null;
}
public TrieArrayNode<TNested> GetChild(char c) {
return Nodes[TrieArray<TNested>.GetIndex(c)];
}
}
}
以下是示例用法:
class Program {
static void Main(string[] args) {
int length = 10;
TrieArray<int> array = new TrieArray<int>(length);
for (int i = 0; i < length; i++) {
array[i] = i * i;
}
for (int i = 0; i < length; i++) {
Console.WriteLine(array[i]);
}
}
}
答案 2 :(得分:0)
好吧,因为我花了很多时间思考它,并且可以认为所有哈希表都是一个连续的大小块&gt; N或者有一个与N成比例的桶列表,并且罗杰的Block
s的顶级数组是O(N),系数小于1,我在他的问题的评论中提出了一个解决方法,这里是:
int magnitude( size_t x ) { // many platforms have an insn for this
for ( int m = 0; x >>= 1; ++ m ) ; // return 0 for input 0 or 1
return m;
}
template< class T >
struct half_power_deque {
vector< vector< T > > blocks; // max log(N) blocks of increasing size
int half_first_block_mag; // blocks one, two have same size >= 2
T &operator[]( size_t index ) {
int index_magnitude = magnitude( index );
size_t block_index = max( 0, index_magnitude - half_first_block_mag );
vector< T > &block = blocks[ block_index ];
size_t elem_index = index;
if ( block_index != 0 ) elem_index &= ( 1<< index_magnitude ) - 1;
return block[ elem_index ];
}
};
template< class T >
struct power_deque {
half_power_deque forward, backward;
ptrdiff_t begin_offset; // == - backward.size() or indexes into forward
T &operator[]( size_t index ) {
ptrdiff_t real_offset = index + begin_offset;
if ( real_offset < 0 ) return backward[ - real_offset - 1 ];
return forward[ real_offset ];
}
};
half_power_deque
实现了擦除除最后一个块之外的所有块,适当地改变了half_first_block_mag。这允许O(最大时间N)内存使用,两端分摊O(1)插入,永不使引用无效,以及O(1)查找。
答案 3 :(得分:-1)
地图/字典怎么样?最后我查了一下,那是O(1)表现。