O(1)在非连续内存中查找?

时间:2010-01-20 01:31:03

标签: performance arrays data-structures memory-management big-o

是否有任何已知的数据结构提供O(1)随机访问,而不使用大小为O(N)或更大的连续内存块?这是受到this answer的启发,并且被要求出于好奇而不是任何特定的实际用例,尽管它可能在一个严重碎片堆的情况下有用。

4 个答案:

答案 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 时出现了一些问题的:

  1. O(1)push_front
  2. O(1)push_back
  3. O(1)op []

  4. 也许我误解了“没有使用大小为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)表现。