【问题标题】:Breadth first traversal of arbitrary graph with minimal memory使用最小内存的任意图的广度优先遍历
【发布时间】:2021-02-16 22:52:58
【问题描述】:

我需要遍历一个巨大的有向图,以寻找从给定起点到特定节点的最短路径。有问题的图表不明确存在;子节点由父节点通过算法确定。
(举例说明:想象一个国际象棋位置图。每个节点都是一个国际象棋位置,其子节点是从该位置开始的所有合法移动。)
所以我有一个开放节点队列,每次处理队列中的下一个节点时,我都会将其所有子节点排入队列。但由于图表可能有循环,我还需要维护所有访问过的节点的哈希集,以便检查我之前是否访问过一个。
这没问题,但由于这个图表太大,我遇到了内存问题。队列中的所有节点也存储在哈希集中,在我的例子中,这往往是总数或访问节点总数的 50% 左右。
有没有什么神奇的方法可以在保持哈希集速度的同时摆脱这种冗余? (显然,我可以通过不散列和只进行线性搜索来消除冗余,但这是不可能的。)

【问题讨论】:

    标签: graph hashset breadth-first-search


    【解决方案1】:

    我通过编写一个类来解决这个问题,该类将键存储在列表中,并将键的索引存储在哈希表中。 “队列中”的下一个节点始终是列表中的下一个节点,直到您找到要查找的内容或遍历了整个图表。

    class IndexMap<T>
    {
        private List<T> values;
        private LinkedList<int>[] buckets;
        public int Count { get; private set; } = 0;
    
        public IndexMap(int capacity)
        {
            values = new List<T>(capacity);
            buckets = new LinkedList<int>[NextPowerOfTwo(capacity)];
            for (int i = 0; i < buckets.Length; ++i)
                buckets[i] = new LinkedList<int>();
        }
    
        public void Add(T item) //assumes item is not yet in map
        {
            if (Count == buckets.Length)
                ReHash();
            int bucketIndex = item.GetHashCode() & (buckets.Length - 1);
            buckets[bucketIndex].AddFirst(Count++);
            values.Add(item);
        }
    
        public bool Contains(T item)
        {
            int bucketIndex = item.GetHashCode() & (buckets.Length - 1);
            foreach(int i in buckets[bucketIndex])
            {
                if (values[i].Equals(item))
                    return true;
            }
            return false;
        }
    
        public T this[int index]
        {
            get => values[index];
        }
    
        private void ReHash()
        {
            LinkedList<int>[] newBuckets = new LinkedList<int>[2 * buckets.Length];
            for (int i = 0; i < newBuckets.Length; ++i)
                newBuckets[i] = new LinkedList<int>();
            for (int i = 0; i < buckets.Length; ++i)
            {
                foreach (int index in buckets[i])
                {
                    int bucketIndex = values[index].GetHashCode() & (newBuckets.Length - 1);
                    newBuckets[bucketIndex].AddFirst(index);
                }
                buckets[i] = null;
            }
            buckets = newBuckets;
        }
    
        private int NextPowerOfTwo(int n)
        {
            if ((n & n-1) == 0)
                return n;
            int output = 0;
            while (n > output)
            {
                output <<= 1;
            }
            return output;
        }
    }
    

    维护开放节点数组和访问节点哈希表的旧方法需要 n*(1+a)*size(T) 空间,其中 a 是 nodes_in_the_queue 的比率 total_nodes_found 和 size(T) 是节点的大小。
    此方法需要 n*(size(T) + size(int))。如果您的节点比 int 大很多,这可以节省很多。

    【讨论】:

      猜你喜欢
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 2012-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-15
      • 2019-08-10
      相关资源
      最近更新 更多