继续练习图论算法。上次写了一个最小生成树算法kruskal和prim。这次该轮到最短路径了。dijkstra和bellman-ford。这两个算法实在是大名鼎鼎,尤其是迪克特斯拉。

标准的最短路算法只记录最小路径的值,我想把路径也记录下来。那么我需要一种数据结构把单源最短路表示出来。

从几何上,可以把这些路径想象成一个以出发点s为根的一棵树。这棵数上的每个节点代表图中的一个点。从树根到这个节点所经过的点就是一条最短路径。因为最短路径上任何一点都是源点到该中间点的最短路径。树中公共的部分说明两个点的最短路径有交集,从某个点开始分开。所以,这棵树实际上也是一个trie。

另外,因为算法中初始化的时候会把为扫描的点标记为无限大。所以我还需要把infinity表示出来。python里面,我可以把infinity做成一个类,然后重载他的运算,让除了infinity本身以外的任何值都小于infinity的实例。python的这种特性用起来感觉真是很棒。有点泛代数的感觉。因为每个infinity都是一样的,所以我干脆把他做成了单例模式。

这些都是做最短路之前的准备工作,但是当我开始做dijkstra的时候,又发现了一个比较严重的问题。算法要用到一个优先级队列来把当前以解决的点外的最短路径弹出来。我打算用我做好的heap。但是之后的松弛操作需要修改队列中的点的优先级值。我最开始做heap的想法是把heap中的元素封装起来,只允许加入和弹出,根本没考虑到这种情况。囧。。。下周请了三天假出去爬山,也没什么时间练习了。只能推到以后再做了。

代码没有做任何检查,不保证正确性。想到哪就写到哪。

def singleton(cls,*args,**kw):
    instances={}
    def _singleton():
        if cls not in instances:
            instances[cls]=cls(*args,**kw)
        return instances[cls]
    return _singleton
@singleton
class infinity(object):
    def __cmp__(self,other):
        if other.__class__==infinity:
            return 0
        else:
            return 1
    def __add__(self,v):
        return self
    def __radd__(self,v):
        return self
    def __str__(self):
        return 'infinity'
    def __repr__(self):
        return 'infinity'


class PathNode(object):
    def __init__(self,v=None):
        self.v=v
        self.e=None
        self.par=None
        self.depth=0
        self.clist=[]
class PathTree(object):
    def __init__(self,v):
        self.root=PathNode(v)
        self.vcount=1
        self.point={v:self.root}
    def length(self,v):
        try:
            n=self.point[v]
        except:
            return infinity()
        else:
            return n.depth
    def insert(self,s,e):
        if s is not e.v1 and s is not e.v2 :
            raise ValueError,'vertex %s is not in edge %s'%(s,e)
        if s not in self.point:
            raise ValueError,'vertex %s is not in this path'
        n=self.point[s]
        a=e.another_v(s)
        newnode=PathNode(a)
        newnode.par=n
        newnode.e=e
        newnode.depth=n.depth+e.weight
        n.clist.append(newnode)
        self.point[a]=newnode
        self.vcount+=1
    def path(self,d):
        r=[]
        try:
            n=self.point[d]
        except KeyError:
            return r
        else:
            while True:
                if n is self.root:
                    break
                else:
                    r.append(n.e)
                    n=n.par
        r.reverse()
        return r
    def move_point(self,v,e):
        try:
            n1=self.point[v]
            t=e.another_v(v)
            n2=self.point[t]
        except KeyError:
            return -1
        else:
            n1.par.clist.remove(n)
            n1.e=e
            n1.par=n2
            n1.depth=n2.depth+e.weight
            n2.clist.append(n1)