当前位置: 首页 > 知识库问答 >
问题:

迪克斯特拉有一堆。如何在松弛后更新堆?

宋华美
2023-03-14

我正在尝试实现Dijkstra算法。

 foreach distance d
   d = INFINITY
 d[source] = 0

 create_heap_based_on_Distances();

 while(true)
   bestedge = heap[0]

   remove_minimum_from_heap //it will be heap[0]

   foreach adjacency of bestedge
      if (weight + bestedge_distance < current_distance)
      {
          current_distance = weight + bestedge_distance

          // Now I have to update heap, how can I do that?
      }
   if (heap_empty) break

那么,在松弛中,我如何更新堆,使其具有正确的顺序?在该步骤中,我没有该节点的堆索引。这是否意味着我必须创建一个新的数组,如node[edge]=heapIndex,以便我可以获得该节点的堆索引?但这似乎效率很低,因为我需要更新insert_to_heapremove_minimum函数。

C或JAVA代码也可以。

共有2个答案

洪鸿博
2023-03-14
Does that mean I have to create a new array like nodes[edge] = heapIndex, 
so I could get a heap's index for that node?

我不知道node[edge]的确切含义。在我看来,它应该是一个Map(确实是一个数组)f[node]=HeapIndex(它在Heap中给出了该节点的索引)。节点[edge]的存储效率不高。

那如何实现MapHeap呢?我实现了一个高效的MapHeap,但是代码中没有太多注意事项:

template<class DT>
struct MapHeap
{
    DT f[HEAP_SIZE+5];//store the distance
    int mp1[HEAP_SIZE+5];//val -> index
    // I assume the val is unique.
    // In the dijk, the val is the nodeId,so it must be unique. 
    // mp1[nodeId] gives the index of that node in my heap
    int mp2[HEAP_SIZE+5];//index -> val
    int nv;// number of node in my heap now
    MapHeap():nv(0)
    {
        memset(mp1,-1,sizeof(mp1));
        memset(mp2,-1,sizeof(mp2));
    }
    void print(int n)
    {
        for(int i=1;i<=n;i++) printf("%d ",f[i]);
        puts("");
        for(int i=1;i<=n;i++) printf("%d ",mp1[i]);
        puts("");
        for(int i=1;i<=n;i++) printf("%d ",mp2[i]);
        puts("");
    }
    void print(){print(nv);}
    bool resize(int n)
    {
        if (nv<0||nv>HEAP_SIZE) return 0;
        for(int i=n+1;i<=nv;i++)
        {
            mp1[mp2[i]]=-1;
            mp2[i]=-1;
        }
        nv=n;
        return 1;
    }
    DT top()//get the smallest element
    {
        if (nv<1) return DT(-1);
        return f[1];
    }
    DT get(int idx)
    {
        if (idx<1||idx>nv) return DT(-1);
        return f[idx];
    }
    // it's for unpdating the heap. It should be pravite method. 
    // Because I write this code for competition, so I just ignore the accsee controling
    void movedown(int now,int val,const DT &x)//this node is larger than son
    {
        for(;now*2<=nv;)
        {
            int a=now*2;
            int b=now*2+1;
            if (b<=nv&&f[b]<f[a]) a=b;
            if (f[a]>=x) break;
            f[now]=f[a];
            mp1[mp2[a]]=now;
            mp2[now]=mp2[a];
            now=a;
        }
        f[now]=x;
        mp1[val]=now;
        mp2[now]=val;
    }
    void moveup(int now,int val,const DT &x)//this node is smaller than father
    {
        for(;now>1;now>>=1)
        {
            int par=now>>1;
            if (f[par]<=x) break;
            f[now]=f[par];
            mp1[mp2[par]]=now;
            mp2[now]=mp2[par];
        }
        f[now]=x;
        mp1[val]=now;
        mp2[now]=val;
    }
    bool pop(int idx=1)//pop a element, pop the smallest element by default
    {
        if (idx<1||idx>nv) return 0;
        DT &x=f[nv];
        int v1=mp2[nv];
        int v2=mp2[idx];
        mp1[v1]=idx;
        mp2[idx]=v1;
        mp1[v2]=-1;
        mp2[nv]=-1;
        nv--;
        if (idx!=nv+1) movedown(idx,v1,x);
        x=0;
        return 1;
    }
    bool push(const DT &x,int val)//push a node, and with the value of val(in dijk, the val is the nodeId of that node)
    {
        int now=++nv;
        if (now>HEAP_SIZE) return 0;
        moveup(now,val,x);
        return 1;
    }
};
狄宜然
2023-03-14

这是否意味着我必须创建一个新的数组,如nodes[edge] = heapIndex,这样我才能获得该节点的堆索引?

是的。

但是这看起来非常低效,因为我需要更新insert_to_heap,remove_minimum函数。

无论是在理论上还是在实践中,数组更新都是非常廉价的,并且每个堆操作中您只需要做一些这样的事情,所以这一点也不低效。此外,与图形数据结构的存储成本相比,这种数组的内存使用非常便宜。

 类似资料:
  • 主要内容:迪杰斯特拉算法的实现思路,迪杰斯特拉算法的具体实现迪杰斯特拉算法用于查找图中某个顶点到其它所有顶点的最短路径,该算法既适用于无向加权图,也适用于有向加权图。 注意,使用迪杰斯特拉算法查找最短路径时,必须保证图中所有边的权值为非负数,否则查找过程很容易出错。 迪杰斯特拉算法的实现思路 图 1 是一个无向加权图,我们就以此图为例,给大家讲解迪杰斯特拉算法的实现思路。 图 1 无向加权图 假设用迪杰斯特拉算法查找从顶点 0 到其它顶点的最短路径,具体过

  • 经过大量谷歌搜索,我发现大多数消息来源说迪克斯特拉算法比贝尔曼-福特算法“更有效”。但是在什么情况下Bellman-Ford算法比Dijkstra算法更好呢? 我知道“更好”是一个宽泛的说法,所以具体来说,我指的是速度和空间,如果适用的话。当然,在某些情况下,贝尔曼-福特方法比迪克斯特拉方法更好。

  • 我有一个未加权的连接图形G,具有n个顶点和m条边。 m=O(n 对数 n)。 我想找到从顶点s到顶点v的最短路径 ,我想知道BFS遍历或Dijkstra的算法是否会渐近更快。 BFS将从s. Dijkstra算法开始,从s开始,并实现斐波那契堆。 BFS的运行时间是θ(n m)= O(n n log n)= O(n log n)< br >而迪杰斯特拉的运行时间是O(m n log n)= O(n

  • 在一个类作业中,我被要求用Java编写Eratostenes筛选代码,我的代码效率非常低。运行时间不长,但我相当肯定,除了像我一样列出所有内容外,还有其他循环的空间。。 这是我的代码: 基本上,我所做的是将所有不是质数的元素设置为true… 所以我的主要2个问题是1。有没有办法实现一个循环,使代码更短2。如何打印此数组中所有为 true(质数)的元素

  • 我正在尝试使用ScalaTest和ScalaCheck进行基于属性的测试。我的测试概述如下: 现在我看到的是,如果我一遍又一遍地运行PropSpec1中的测试,有时第二个测试会通过,但大多数时候会失败。现在,如果0没有被b测试,那么很明显它会通过,但我认为这是它会一直尝试的事情之一。重复运行sbt clean test时,我看到了相同的行为;有时两项测试都通过了。 这对于基于属性的测试是正常的吗,

  • 这可能是一个简单的问题,但我无法解决。 我使用Slack Python Api在频道中提到用户,我在这里指的是文档,https://api.slack.com/methods/chat.postMessage,我的代码很简单, 这将向频道发送消息,但我找不到任何提及用户的选项。我试图在信息中加入,例如 该消息将被发布,但以纯文本形式发布,而不是真正提及某人。顺便说一句,我正在使用测试令牌。(或者也