图的算法,创新了一种求有向图强联通分支的算法

1.使用链表或者数组来保存图的结构,bfs和dfs(用函数嵌套来解决记忆问题)
'''
图的表示:算法导论22章
图又2中表示方式:
1.是链表:一个节点跟他相连的节点都用一个链表穿起来.
         然后n个节点就用n个链表来储存这个图的信息
2.是矩阵:用节点数n,n*n的矩阵来储存,两个点如果有边就存1.
优势和劣势:
1.链表:适用于稀疏图,但是很难判断一个边是否存在
2.矩阵:反之.

`'''
'''
拓扑排序

一.定义

对一个有向无环图(Directed Acyclic Graph简称DAG)G进
行拓扑排序(Topological Sort),是将G中所有顶点排成一个线
性序列,使得对图中任意一对顶点u和v,若<u,v>∈E(G),则u在
线性序列中出现在v之前。通常将这样的线性序
列称为满足拓扑次序(Topolgical Order)的序列,简称拓扑序列。'''




'''
用链表来拓扑排序
入度是图论算法中重要的概念之一。它通常指有向
图中某点作为图中边的终点的次数之和。'''


'''
入度表

对于DAG的拓扑排序,显而易见的办法!!!!!!!!
这是一个好方法:

找出图中0入度的顶点;
依次在图中删除这些顶点,删除后再找出0入度的顶点;
然后再删除……再找出……
直至删除所有顶点,即完成拓扑排序
为了保存0入度的顶点,我们采用数据结构栈(亦可用队列);算法的可视化可参看这里。

图用邻接表(adjacency list)表示,用数组inDegreeArray[]记录结点的入度变化情况。C实现:'''


class Graph():
    def __init__(self,n):
        self.vertex_num=n
        self.edge=[]
        for i in range(n):#2-dim-empty list cannot use *99
         self.edge.append([])
        self.indegree=[0]*n
        self.count=0#作为dfs里面的flag
        
        self.whether_exist=[0]*self.vertex_num
#vertex_index=0,1,2,3,4....
    def addedge(self,n,m):
        self.edge[n].append(m)
        self.indegree[m]+=1
    def topological_sort(self):
        a=[]
        b=len(self.indegree)

        while (b):
         for i in range(len(self.indegree)):
            if 0 not in self.indegree:
                return False
            if self.indegree[i]==0:
                
                a.append(i)
                self.indegree[i]-=1
                
                for j in self.edge[i]:
                   
                    self.indegree[j]-=1
                b-=1
                break
    def bfs(self,root):#board first search广度优先遍历
        #广度优先遍历,深度优先遍历都是只从一个root点出发,遍历跟他有关系的点.
        #所以不连通的图需要多次从不同的root出发进行遍历才行.那个后话了.
        
        #这个很麻烦,可不可以做一个count让函数第一次被调用时候才跑这句话.
        #可以给class加一个变量.写在__init__里面
        #好像广度搜索不能写递归,一些递归就深度了.
        
        self.output=[]
        if root not in self.output:
            self.output.append(root)
        tmp=self.edge[root]
        tmp2=[]
        while 1:
            
            for i in range(len(tmp)):
                if tmp[i] not in self.output:
                    self.output.append(tmp[i])
            c=[]
            for i in range(len(tmp)):
                for j in range(len(self.edge[tmp[i]])):
                 if self.edge[tmp[i]][j] not in self.output:
                  c+=self.edge[tmp[i]]
            if c==[]:
                return self.output
            tmp=c
    def dfs(self,root):#类似也弄好dfs这次可以递归了.
        #跟树的遍历不同,图需要去重,很麻烦.
        #还是不行,可能必须要实现知道节点的数目才行.不然没法开记忆是否存储的数组
        self.tmp=[]#dfs中间变量    
            #用函数体外的self.tmp来做记忆,用嵌套函数来解决递归函数返回值问题
        def min_dfs(root):#这个函数嵌套不用self,可以只是一般函数
            
            b=[]
            if root not in self.tmp:
                self.tmp.append(root)
            for i in self.edge[root]: 
               if i not in self.tmp:
                   
                   b.append(i)
            c=[]
            for i in b: #这步体现的递归.和dfs
                c+=(min_dfs(i))
            tmp=[root]+c
            
            return tmp
        min_dfs(root)
        return self.tmp

            
            





            
g=Graph(60)

g.addedge(1,2)
g.addedge(1,3)
g.addedge(2,6)
g.addedge(2,5)
g.addedge(3,6)
g.addedge(4,3)
g.addedge(3,4)


print (g.dfs(1)) #[1, 2, 6, 5, 3, 4]

'''
下面继续学习图:发现博客上面的比算法导论上面的方法更先进.
http://blog.csdn.net/moshenglv/article/details/72954701'''
'''

强连通图(Strongly Connected Graph)是指在有向图G中,如果对
于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,
则称G是强连通图。'''

'''

1.图的存储结构:

    1.邻接矩阵
    2.邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,
    这种结构存在空间上的极大浪费,
    因此找到一种数组与链表相结合的存储方法称为邻接表。
    3. 十字链表的好处就是因为把邻接表和逆邻接表整合在一起,这样既容易
    找到以v为尾
    的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。
    而且除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同
    的,因此,在有向图应用中,十字链表是非常好的数据结构模型。(相当复杂)
'''
'''
下面先写bfs'''
View Code

 2.创新了一种求有向图强联通分支的算法(可能别人也早给出过)

'''
图的表示:算法导论22章
图又2中表示方式:
1.是链表:一个节点跟他相连的节点都用一个链表穿起来.
         然后n个节点就用n个链表来储存这个图的信息
2.是矩阵:用节点数n,n*n的矩阵来储存,两个点如果有边就存1.
优势和劣势:
1.链表:适用于稀疏图,但是很难判断一个边是否存在
2.矩阵:反之.

`'''
'''
拓扑排序

一.定义

对一个有向无环图(Directed Acyclic Graph简称DAG)G进
行拓扑排序(Topological Sort),是将G中所有顶点排成一个线
性序列,使得对图中任意一对顶点u和v,若<u,v>∈E(G),则u在
线性序列中出现在v之前。通常将这样的线性序
列称为满足拓扑次序(Topolgical Order)的序列,简称拓扑序列。'''




'''
用链表来拓扑排序
入度是图论算法中重要的概念之一。它通常指有向
图中某点作为图中边的终点的次数之和。'''


'''
入度表

对于DAG的拓扑排序,显而易见的办法!!!!!!!!
这是一个好方法:

找出图中0入度的顶点;
依次在图中删除这些顶点,删除后再找出0入度的顶点;
然后再删除……再找出……
直至删除所有顶点,即完成拓扑排序
为了保存0入度的顶点,我们采用数据结构栈(亦可用队列);算法的可视化可参看这里。

图用邻接表(adjacency list)表示,用数组inDegreeArray[]记录结点的入度变化情况。C实现:'''


class Graph():
    def __init__(self,n):
        self.vertex_num=n
        self.edge=[]
        for i in range(n):#2-dim-empty list cannot use *99
         self.edge.append([])
        self.indegree=[0]*n
        self.count=0#作为dfs里面的flag
        
        self.whether_exist=[0]*self.vertex_num
#vertex_index=0,1,2,3,4....
    def addedge(self,n,m):
        self.edge[n].append(m)
        self.indegree[m]+=1
    def Transpose(self):
        self.edgenew=[]
        n=self.vertex_num
        for i in range(n):#2-dim-empty list cannot use *99
          self.edgenew.append([])
        for i in range(len(self.edge)):
            if self.edge[i]!=[]:
                tmp=self.edge[i]
                for j in tmp:
                    self.edgenew[j].append(i)
        
        new=Graph(n)#重新分配一个对象,这样就跟以前不干扰了
        new.edge=self.edgenew
        return new
    def topological_sort(self):
        a=[]
        b=len(self.indegree)

        while (b):
         for i in range(len(self.indegree)):
            if 0 not in self.indegree:
                return False
            if self.indegree[i]==0:
                
                a.append(i)
                self.indegree[i]-=1
                
                for j in self.edge[i]:
                   
                    self.indegree[j]-=1
                b-=1
                break
    def bfs(self,root):#board first search广度优先遍历
        #广度优先遍历,深度优先遍历都是只从一个root点出发,遍历跟他有关系的点.
        #所以不连通的图需要多次从不同的root出发进行遍历才行.那个后话了.
        
        #这个很麻烦,可不可以做一个count让函数第一次被调用时候才跑这句话.
        #可以给class加一个变量.写在__init__里面
        #好像广度搜索不能写递归,一些递归就深度了.
        
        self.output=[]
        if root not in self.output:
            self.output.append(root)
        tmp=self.edge[root]
        tmp2=[]
        while 1:
            
            for i in range(len(tmp)):
                if tmp[i] not in self.output:
                    self.output.append(tmp[i])
            c=[]
            for i in range(len(tmp)):
                for j in range(len(self.edge[tmp[i]])):
                 if self.edge[tmp[i]][j] not in self.output:
                  c+=self.edge[tmp[i]]
            if c==[]:
                return self.output
            tmp=c
    def dfs(self,root):#类似也弄好dfs这次可以递归了.
        #跟树的遍历不同,图需要去重,很麻烦.
        #还是不行,可能必须要实现知道节点的数目才行.不然没法开记忆是否存储的数组
        self.tmp=[]#dfs中间变量    
            #用函数体外的self.tmp来做记忆,用嵌套函数来解决递归函数返回值问题
        def min_dfs(root):#这个函数嵌套不用self,可以只是一般函数
            
            b=[]
            if root not in self.tmp:
                self.tmp.append(root)
            for i in self.edge[root]: 
               if i not in self.tmp:
                   
                   b.append(i)
            c=[]
            for i in b: #这步体现的递归.和dfs
                c+=(min_dfs(i))
            tmp=[root]+c
            
            return tmp
        min_dfs(root)
        return self.tmp
    def dfs_all(self):#所有节点都跑一边的dfs算法.为了下面的实用强联通分支,我们把
                      #返回值写作一个数组
        n=self.vertex_num
        c=[]
        d=[]
        head=0
        while 1:
            if len(d)==n:
                return c
            tmp=self.dfs(head)
            k=[]
            for i in tmp:
                if i not in d:
                    k.append(i)
            d+=k
            c.append(k)
            for i in range(n):
                if i not in d:
                    head=i
                    break
     
    def strong_connected_components(self):
         a=self#self指的是对象
         b=self.Transpose()
         list1=self.dfs_all()
         self=b#类还能这么玩,直接类的方法里面改变这个对象
         list2=self.dfs_all()
         
         def in_the_same(c,d,L):
             for i in L:
                 if c in i and d in i:
                     return True
             return False
##         import copy
##         listnew=[]
##         for i in list1:
##             if len(i)==1:
##                 continue
##             for j in range(len(i)):
##                 if in_the_same(i[j],i[j+1],a) !=in_the_same(i[j],i[j+1],b)
         #突然发现应该用集合来做这个2个list的求交运算.
         a_set=[]
         b_set=[]
         for i in list1:
             a_set.append(set(i))
         for i in list2:
             b_set.append(set(i))
         
         #然后直接求交即可.
         c=[]
         for i in a_set:
             for j in b_set:
                 d=(i&j)
                 if len(d)!=0:
                  c.append(d)
         return c
         
                 







        
#针对有向图要有一个强联通分支算法.
#自己设计了一个算法:首先把G的Transpose记做Gt.
    '''然后,做dfs在G上做一次,类似无向图的dfs分解连通分支的方法,把定点分类,
然后在Gt上再做一次,又把定点分类了.然后我们取2次分类的交即可,就是要的分支.
这里面交指的是2次划分的更细的子划分.也就是比如第一次把12一类,3一类.
第二次把13一类,2一类,那么交就是1,2,3分成3类.
'''
            
            





            
g=Graph(7)

g.addedge(1,2)

g.addedge(2,6)
g.addedge(5,2)
g.addedge(3,6)

g.addedge(4,3)
g.addedge(3,4)
g.addedge(6,5)


print (g.dfs(3)) #[1, 2, 6, 5, 3, 4]
print (g.Transpose().edge[1])
print (g.edge[1])
print (g.dfs_all())
print (g.Transpose().dfs_all())
print (g.strong_connected_components())#成功得到了4个分支,并且还是按照标号
#的顺序进行排列的分支.然后随意修改edge,都成功了.
'''
下面继续学习图:发现博客上面的比算法导论上面的方法更先进.
http://blog.csdn.net/moshenglv/article/details/72954701'''
'''

强连通图(Strongly Connected Graph)是指在有向图G中,如果对
于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,
则称G是强连通图。'''

'''

1.图的存储结构:

    1.邻接矩阵
    2.邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,
    这种结构存在空间上的极大浪费,
    因此找到一种数组与链表相结合的存储方法称为邻接表。
    3. 十字链表的好处就是因为把邻接表和逆邻接表整合在一起,这样既容易
    找到以v为尾
    的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。
    而且除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同
    的,因此,在有向图应用中,十字链表是非常好的数据结构模型。(相当复杂)
'''
'''
下面先写bfs'''
View Code

 算法的好处是理解简单,证明的画画出全部情况看一眼就显然,缺点没有现成的算法kosaraju的效率高.但是没有慢太多,就是慢在了set操作上.

3.最小生成树算法

'''
把图改成无向图:'''
import numpy as np
import numpy
class Graph():
    def __init__(self,n):
        self.vertex_num=n
        self.edge=[]
        for i in range(n):#2-dim-empty list cannot use *99
         self.edge.append([])
        self.indegree=[0]*n
        self.count=0#作为dfs里面的flag
        self.weight=numpy.array([None]*n**2).reshape(n,n)
        self.whether_exist=[0]*self.vertex_num
#vertex_index=0,1,2,3,4....
    def addedge(self,n,m):
        self.edge[n].append(m)
        self.edge[m].append(n)
        self.indegree[m]+=1
        self.indegree[n]+=1
    def Transpose(self):
        self.edgenew=[]
        n=self.vertex_num
        for i in range(n):#2-dim-empty list cannot use *99
          self.edgenew.append([])
        for i in range(len(self.edge)):
            if self.edge[i]!=[]:
                tmp=self.edge[i]
                for j in tmp:
                    self.edgenew[j].append(i)
        
        new=Graph(n)#重新分配一个对象,这样就跟以前不干扰了
        new.edge=self.edgenew
        return new
    def topological_sort(self):
        a=[]
        b=len(self.indegree)

        while (b):
         for i in range(len(self.indegree)):
            if 0 not in self.indegree:
                return False
            if self.indegree[i]==0:
                
                a.append(i)
                self.indegree[i]-=1
                
                for j in self.edge[i]:
                   
                    self.indegree[j]-=1
                b-=1
                break
    def bfs(self,root):#board first search广度优先遍历
        #广度优先遍历,深度优先遍历都是只从一个root点出发,遍历跟他有关系的点.
        #所以不连通的图需要多次从不同的root出发进行遍历才行.那个后话了.
        
        #这个很麻烦,可不可以做一个count让函数第一次被调用时候才跑这句话.
        #可以给class加一个变量.写在__init__里面
        #好像广度搜索不能写递归,一些递归就深度了.
        
        self.output=[]
        if root not in self.output:
            self.output.append(root)
        tmp=self.edge[root]
        tmp2=[]
        while 1:
            
            for i in range(len(tmp)):
                if tmp[i] not in self.output:
                    self.output.append(tmp[i])
            c=[]
            for i in range(len(tmp)):
                for j in range(len(self.edge[tmp[i]])):
                 if self.edge[tmp[i]][j] not in self.output:
                  c+=self.edge[tmp[i]]
            if c==[]:
                return self.output
            tmp=c
    def dfs(self,root):#类似也弄好dfs这次可以递归了.
        #跟树的遍历不同,图需要去重,很麻烦.
        #还是不行,可能必须要实现知道节点的数目才行.不然没法开记忆是否存储的数组
        self.tmp=[]#dfs中间变量    
            #用函数体外的self.tmp来做记忆,用嵌套函数来解决递归函数返回值问题
        def min_dfs(root):#这个函数嵌套不用self,可以只是一般函数
            
            b=[]
            if root not in self.tmp:
                self.tmp.append(root)
            for i in self.edge[root]: 
               if i not in self.tmp:
                   
                   b.append(i)
            c=[]
            for i in b: #这步体现的递归.和dfs
                c+=(min_dfs(i))
            tmp=[root]+c
            
            return tmp
        min_dfs(root)
        return self.tmp
    def dfs_all(self):#所有节点都跑一边的dfs算法.为了下面的实用强联通分支,我们把
                      #返回值写作一个数组
        n=self.vertex_num
        c=[]
        d=[]
        head=0
        while 1:
            if len(d)==n:
                return c
            tmp=self.dfs(head)
            k=[]
            for i in tmp:
                if i not in d:
                    k.append(i)
            d+=k
            c.append(k)
            for i in range(n):
                if i not in d:
                    head=i
                    break
     
    def strong_connected_components(self):
         a=self#self指的是对象
         b=self.Transpose()
         list1=self.dfs_all()
         self=b#类还能这么玩,直接类的方法里面改变这个对象
         list2=self.dfs_all()
         
         def in_the_same(c,d,L):
             for i in L:
                 if c in i and d in i:
                     return True
             return False
##         import copy
##         listnew=[]
##         for i in list1:
##             if len(i)==1:
##                 continue
##             for j in range(len(i)):
##                 if in_the_same(i[j],i[j+1],a) !=in_the_same(i[j],i[j+1],b)
         #突然发现应该用集合来做这个2个list的求交运算.
         a_set=[]
         b_set=[]
         for i in list1:
             a_set.append(set(i))
         for i in list2:
             b_set.append(set(i))
         
         #然后直接求交即可.
         c=[]
         for i in a_set:
             for j in b_set:
                 d=(i&j)
                 if len(d)!=0:
                  c.append(d)
         return c
         
                 







        
#针对有向图要有一个强联通分支算法.
#自己设计了一个算法:首先把G的Transpose记做Gt.
    '''然后,做dfs在G上做一次,类似无向图的dfs分解连通分支的方法,把定点分类,
然后在Gt上再做一次,又把定点分类了.然后我们取2次分类的交即可,就是要的分支.
这里面交指的是2次划分的更细的子划分.也就是比如第一次把12一类,3一类.
第二次把13一类,2一类,那么交就是1,2,3分成3类.
'''

    def addweight(self,a,b,weight):
        
            self.weight[a][b]=weight
            self.weight[b][a]=weight
            return 
#A wise man changes his mind,a fool never.

    def minimal_spanning_tree(self):
  
    #具体方法就是https://blog.csdn.net/luoshixian099/article/details/51908175
    #一个是kruskal加点发,prim加边法.
        new=Graph(6)
        c=[]
        num=1
        
        while 1:
            d=[]
            for i in range(6):
                a=new.dfs_all()
                if len(a)==1:
                    return c
            d=(np.argwhere(g.weight==num))#这个很方便,返回下表
            for t in range(len(d)):
                k=d[t]
                i,j=k[0],k[1]
                e=new.dfs(i)
                
                f=new.dfs(j)
                
                print (e)
                print (f)
                print (set(e))
                print (set(f))
                
                
                if set(e)&set(f)==set():
                    c.append((i,j))
                    new.addedge(i,j)
                    
                    print (c)
                    num+=1
                    break
                


            
g=Graph(6)
g.addedge(0,1)
g.addedge(0,2)
g.addedge(0,3)
g.addedge(1,2)
g.addedge(2,3)
g.addedge(1,4)
g.addedge(2,4)
g.addedge(2,5)
g.addedge(3,5)
g.addedge(4,5)
g.addweight(0,1,6)
g.addweight(0,3,5)
g.addweight(0,2,1)
g.addweight(1,4,3)
g.addweight(4,5,6)
g.addweight(2,1,5)
g.addweight(3,2,5)
g.addweight(2,4,6)
g.addweight(2,5,4)
g.addweight(3,5,2)
print (g.dfs_all())

print (g.minimal_spanning_tree())  #[(0, 2), (3, 5), (1, 4), (2, 5), (1, 2)]搞定了,trivial问题.




#成功得到了4个分支,并且还是按照标号
#的顺序进行排列的分支.然后随意修改edge,都成功了.
'''
下面继续学习图:发现博客上面的比算法导论上面的方法更先进.
http://blog.csdn.net/moshenglv/article/details/72954701'''
'''

强连通图(Strongly Connected Graph)是指在有向图G中,如果对
于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,
则称G是强连通图。'''

'''

1.图的存储结构:

    1.邻接矩阵
    2.邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,
    这种结构存在空间上的极大浪费,
    因此找到一种数组与链表相结合的存储方法称为邻接表。
    3. 十字链表的好处就是因为把邻接表和逆邻接表整合在一起,这样既容易
    找到以v为尾
    的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。
    而且除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同
    的,因此,在有向图应用中,十字链表是非常好的数据结构模型。(相当复杂)
'''
'''
下面先写bfs'''
View Code

 理论分析版:

'''
把图改成无向图:'''
import numpy as np
import numpy
class Graph():
    def __init__(self,n):
        self.vertex_num=n
        self.edge=[]
        for i in range(n):#2-dim-empty list cannot use *99
         self.edge.append([])
        self.indegree=[0]*n
        self.count=0#作为dfs里面的flag
        self.weight=numpy.array([None]*n**2).reshape(n,n)
        self.whether_exist=[0]*self.vertex_num
#vertex_index=0,1,2,3,4....
    def addedge(self,n,m):
        self.edge[n].append(m)
        self.edge[m].append(n)
        self.indegree[m]+=1
        self.indegree[n]+=1
    def Transpose(self):
        self.edgenew=[]
        n=self.vertex_num
        for i in range(n):#2-dim-empty list cannot use *99
          self.edgenew.append([])
        for i in range(len(self.edge)):
            if self.edge[i]!=[]:
                tmp=self.edge[i]
                for j in tmp:
                    self.edgenew[j].append(i)
        
        new=Graph(n)#重新分配一个对象,这样就跟以前不干扰了
        new.edge=self.edgenew
        return new
    def topological_sort(self):
        a=[]
        b=len(self.indegree)

        while (b):
         for i in range(len(self.indegree)):
            if 0 not in self.indegree:
                return False
            if self.indegree[i]==0:
                
                a.append(i)
                self.indegree[i]-=1
                
                for j in self.edge[i]:
                   
                    self.indegree[j]-=1
                b-=1
                break
    def bfs(self,root):#board first search广度优先遍历
        #广度优先遍历,深度优先遍历都是只从一个root点出发,遍历跟他有关系的点.
        #所以不连通的图需要多次从不同的root出发进行遍历才行.那个后话了.
        
        #这个很麻烦,可不可以做一个count让函数第一次被调用时候才跑这句话.
        #可以给class加一个变量.写在__init__里面
        #好像广度搜索不能写递归,一些递归就深度了.
        
        self.output=[]
        if root not in self.output:
            self.output.append(root)
        tmp=self.edge[root]
        tmp2=[]
        while 1:
            
            for i in range(len(tmp)):
                if tmp[i] not in self.output:
                    self.output.append(tmp[i])
            c=[]
            for i in range(len(tmp)):
                for j in range(len(self.edge[tmp[i]])):
                 if self.edge[tmp[i]][j] not in self.output:
                  c+=self.edge[tmp[i]]
            if c==[]:
                return self.output
            tmp=c
    def dfs(self,root):#类似也弄好dfs这次可以递归了.
        #跟树的遍历不同,图需要去重,很麻烦.
        #还是不行,可能必须要实现知道节点的数目才行.不然没法开记忆是否存储的数组
        self.tmp=[]#dfs中间变量    
            #用函数体外的self.tmp来做记忆,用嵌套函数来解决递归函数返回值问题
        def min_dfs(root):#这个函数嵌套不用self,可以只是一般函数
            
            b=[]
            if root not in self.tmp:
                self.tmp.append(root)
            for i in self.edge[root]: 
               if i not in self.tmp:
                   
                   b.append(i)
            c=[]
            for i in b: #这步体现的递归.和dfs
                c+=(min_dfs(i))
            tmp=[root]+c
            
            return tmp
        min_dfs(root)
        return self.tmp
    def dfs_all(self):#所有节点都跑一边的dfs算法.为了下面的实用强联通分支,我们把
                      #返回值写作一个数组
        n=self.vertex_num
        c=[]
        d=[]
        head=0
        while 1:
            if len(d)==n:
                return c
            tmp=self.dfs(head)
            k=[]
            for i in tmp:
                if i not in d:
                    k.append(i)
            d+=k
            c.append(k)
            for i in range(n):
                if i not in d:
                    head=i
                    break
     
    def strong_connected_components(self):
         a=self#self指的是对象
         b=self.Transpose()
         list1=self.dfs_all()
         self=b#类还能这么玩,直接类的方法里面改变这个对象
         list2=self.dfs_all()
         
         def in_the_same(c,d,L):
             for i in L:
                 if c in i and d in i:
                     return True
             return False
##         import copy
##         listnew=[]
##         for i in list1:
##             if len(i)==1:
##                 continue
##             for j in range(len(i)):
##                 if in_the_same(i[j],i[j+1],a) !=in_the_same(i[j],i[j+1],b)
         #突然发现应该用集合来做这个2个list的求交运算.
         a_set=[]
         b_set=[]
         for i in list1:
             a_set.append(set(i))
         for i in list2:
             b_set.append(set(i))
         
         #然后直接求交即可.
         c=[]
         for i in a_set:
             for j in b_set:
                 d=(i&j)
                 if len(d)!=0:
                  c.append(d)
         return c
         
                 







        
#针对有向图要有一个强联通分支算法.
#自己设计了一个算法:首先把G的Transpose记做Gt.
    '''然后,做dfs在G上做一次,类似无向图的dfs分解连通分支的方法,把定点分类,
然后在Gt上再做一次,又把定点分类了.然后我们取2次分类的交即可,就是要的分支.
这里面交指的是2次划分的更细的子划分.也就是比如第一次把12一类,3一类.
第二次把13一类,2一类,那么交就是1,2,3分成3类.
'''

    def addweight(self,a,b,weight):
        
            self.weight[a][b]=weight
            self.weight[b][a]=weight
            return 
#A wise man changes his mind,a fool never.

    def minimal_spanning_tree(self):
  
    #具体方法就是https://blog.csdn.net/luoshixian099/article/details/51908175
    #一个是kruskal加点发,prim加边法.
        new=Graph(6)
        c=[]
        num=1
        
        while 1:
            d=[]
            for i in range(6):
                a=new.dfs_all()
                if len(a)==1:
                    return c
            d=(np.argwhere(g.weight==num))#这个很方便,返回下表
            for t in range(len(d)):
                k=d[t]
                i,j=k[0],k[1]
                e=new.dfs(i)
                
                f=new.dfs(j)
                
                print (e)
                print (f)
                print (set(e))
                print (set(f))
                
                
                if set(e)&set(f)==set():
                    c.append((i,j))
                    new.addedge(i,j)
                    
                    print (c)
                    num+=1
                    break
'''
写点,kruskal算法的思想,其实很trivial,就是贪心.
他的贪心原则就是抓住本质,你每一次加一个边就是为了多加点,而少加weight,
所以从weight排序里面找,加入点最多的,也就是生成树之间没有交集的,这样每一次就都加入了
2个点.而不是1个点.因为最后所有的点还是要进行相连接的,所以只需要进行每一次的贪心就可以保证
最后的所有点的数目.这种贪心正确.'''


            
g=Graph(6)
g.addedge(0,1)
g.addedge(0,2)
g.addedge(0,3)
g.addedge(1,2)
g.addedge(2,3)
g.addedge(1,4)
g.addedge(2,4)
g.addedge(2,5)
g.addedge(3,5)
g.addedge(4,5)
g.addweight(0,1,6)
g.addweight(0,3,5)
g.addweight(0,2,1)
g.addweight(1,4,3)
g.addweight(4,5,6)
g.addweight(2,1,5)
g.addweight(3,2,5)
g.addweight(2,4,6)
g.addweight(2,5,4)
g.addweight(3,5,2)
print (g.dfs_all())

print (g.minimal_spanning_tree())  #[(0, 2), (3, 5), (1, 4), (2, 5), (1, 2)]搞定了,trivial问题.




#成功得到了4个分支,并且还是按照标号
#的顺序进行排列的分支.然后随意修改edge,都成功了.
'''
下面继续学习图:发现博客上面的比算法导论上面的方法更先进.
http://blog.csdn.net/moshenglv/article/details/72954701'''
'''

强连通图(Strongly Connected Graph)是指在有向图G中,如果对
于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,
则称G是强连通图。'''

'''

1.图的存储结构:

    1.邻接矩阵
    2.邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,
    这种结构存在空间上的极大浪费,
    因此找到一种数组与链表相结合的存储方法称为邻接表。
    3. 十字链表的好处就是因为把邻接表和逆邻接表整合在一起,这样既容易
    找到以v为尾
    的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。
    而且除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同
    的,因此,在有向图应用中,十字链表是非常好的数据结构模型。(相当复杂)
'''
'''
下面先写bfs'''
View Code

 4.图的单源最小距离的路径的求法.屌丝bellman_ford方法.

'''
把图改成无向图:'''
import numpy as np
import numpy
class Graph():
    def __init__(self,n):
        self.vertex_num=n
        self.edge=[]
        for i in range(n):#2-dim-empty list cannot use *99
         self.edge.append([])
        self.indegree=[0]*n
        self.count=0#作为dfs里面的flag
        self.weight=numpy.array([None]*n**2).reshape(n,n)
        self.whether_exist=[0]*self.vertex_num
#vertex_index=0,1,2,3,4....
    def addedge(self,n,m):
        self.edge[n].append(m)
        self.edge[m].append(n)
        self.indegree[m]+=1
        self.indegree[n]+=1
    def Transpose(self):
        self.edgenew=[]
        n=self.vertex_num
        for i in range(n):#2-dim-empty list cannot use *99
          self.edgenew.append([])
        for i in range(len(self.edge)):
            if self.edge[i]!=[]:
                tmp=self.edge[i]
                for j in tmp:
                    self.edgenew[j].append(i)
        
        new=Graph(n)#重新分配一个对象,这样就跟以前不干扰了
        new.edge=self.edgenew
        return new
    def topological_sort(self):
        a=[]
        b=len(self.indegree)

        while (b):
         for i in range(len(self.indegree)):
            if 0 not in self.indegree:
                return False
            if self.indegree[i]==0:
                
                a.append(i)
                self.indegree[i]-=1
                
                for j in self.edge[i]:
                   
                    self.indegree[j]-=1
                b-=1
                break
    def bfs(self,root):#board first search广度优先遍历
        #广度优先遍历,深度优先遍历都是只从一个root点出发,遍历跟他有关系的点.
        #所以不连通的图需要多次从不同的root出发进行遍历才行.那个后话了.
        
        #这个很麻烦,可不可以做一个count让函数第一次被调用时候才跑这句话.
        #可以给class加一个变量.写在__init__里面
        #好像广度搜索不能写递归,一些递归就深度了.
        
        self.output=[]
        if root not in self.output:
            self.output.append(root)
        tmp=self.edge[root]
        tmp2=[]
        while 1:
            
            for i in range(len(tmp)):
                if tmp[i] not in self.output:
                    self.output.append(tmp[i])
            c=[]
            for i in range(len(tmp)):
                for j in range(len(self.edge[tmp[i]])):
                 if self.edge[tmp[i]][j] not in self.output:
                  c+=self.edge[tmp[i]]
            if c==[]:
                return self.output
            tmp=c
    def dfs(self,root):#类似也弄好dfs这次可以递归了.
        #跟树的遍历不同,图需要去重,很麻烦.
        #还是不行,可能必须要实现知道节点的数目才行.不然没法开记忆是否存储的数组
        self.tmp=[]#dfs中间变量    
            #用函数体外的self.tmp来做记忆,用嵌套函数来解决递归函数返回值问题
        def min_dfs(root):#这个函数嵌套不用self,可以只是一般函数
            
            b=[]
            if root not in self.tmp:
                self.tmp.append(root)
            for i in self.edge[root]: 
               if i not in self.tmp:
                   
                   b.append(i)
            c=[]
            for i in b: #这步体现的递归.和dfs
                c+=(min_dfs(i))
            tmp=[root]+c
            
            return tmp
        min_dfs(root)
        return self.tmp
    def dfs_all(self):#所有节点都跑一边的dfs算法.为了下面的实用强联通分支,我们把
                      #返回值写作一个数组
        n=self.vertex_num
        c=[]
        d=[]
        head=0
        while 1:
            if len(d)==n:
                return c
            tmp=self.dfs(head)
            k=[]
            for i in tmp:
                if i not in d:
                    k.append(i)
            d+=k
            c.append(k)
            for i in range(n):
                if i not in d:
                    head=i
                    break
     
    def strong_connected_components(self):
         a=self#self指的是对象
         b=self.Transpose()
         list1=self.dfs_all()
         self=b#类还能这么玩,直接类的方法里面改变这个对象
         list2=self.dfs_all()
         
         def in_the_same(c,d,L):
             for i in L:
                 if c in i and d in i:
                     return True
             return False
##         import copy
##         listnew=[]
##         for i in list1:
##             if len(i)==1:
##                 continue
##             for j in range(len(i)):
##                 if in_the_same(i[j],i[j+1],a) !=in_the_same(i[j],i[j+1],b)
         #突然发现应该用集合来做这个2个list的求交运算.
         a_set=[]
         b_set=[]
         for i in list1:
             a_set.append(set(i))
         for i in list2:
             b_set.append(set(i))
         
         #然后直接求交即可.
         c=[]
         for i in a_set:
             for j in b_set:
                 d=(i&j)
                 if len(d)!=0:
                  c.append(d)
         return c
         
                 







        
#针对有向图要有一个强联通分支算法.
#自己设计了一个算法:首先把G的Transpose记做Gt.
    '''然后,做dfs在G上做一次,类似无向图的dfs分解连通分支的方法,把定点分类,
然后在Gt上再做一次,又把定点分类了.然后我们取2次分类的交即可,就是要的分支.
这里面交指的是2次划分的更细的子划分.也就是比如第一次把12一类,3一类.
第二次把13一类,2一类,那么交就是1,2,3分成3类.
'''

    def addweight(self,a,b,weight):
        
            self.weight[a][b]=weight
            self.weight[b][a]=weight
            return 
#A wise man changes his mind,a fool never.
    def function(self):
        pass
    def minimal_spanning_tree(self):
  
    #具体方法就是https://blog.csdn.net/luoshixian099/article/details/51908175
    #一个是kruskal加点发,prim加边法.
        new=Graph(6)
        c=[]
        num=1
        
        while 1:
            d=[]
            for i in range(6):
                a=new.dfs_all()
                if len(a)==1:
                    return c
            d=(np.argwhere(self.weight==num))#这个很方便,返回下表
            for t in range(len(d)):
                k=d[t]
                i,j=k[0],k[1]
                e=new.dfs(i)
                
                f=new.dfs(j)
                
                print (e)
                print (f)
                print (set(e))
                print (set(f))
                
                
                if set(e)&set(f)==set():
                    c.append((i,j))
                    new.addedge(i,j)
                    
                    print (c)
                    num+=1
                    break
        return 
    def bellman_ford(self,start_point):
        b=[None]*self.vertex_num #用来存储前驱节点,来输出最后的最短路径.
        c=[float('inf')]*self.vertex_num#用来存储start_point到点i的距离给b[i]存储.
        print (c)
        b[start_point]=start_point
        c[start_point]=0
        d=np.argwhere(g.weight!=None)
        print (d)
        for i in range(len(b)-1):
            for j in range(len(d)):
                start=d[j][0]
                
                end=d[j][1]
                if c[start]+self.weight[start][end]<c[end]:
                    c[end]=c[start]+self.weight[start][end]
                    b[end]=start#到这里路径就完事了,松弛技术写起来也挺trivial
                #b是前驱节点表
        #然后通过前驱节点,来做路径表,因为贪心原则.可以做到.
                    #一个最短路径上的任意两点之间的路径也必然是这两点的最短路径.
        #下面输出路径表k
        k=[None]*self.vertex_num
        k[start_point]=start_point
        tmp=[]
        for i in range(len(k)):
            if i !=start_point:
                tmp=[i]
                now=b[i]
                while 1:
                     
                     if now==start_point:
                         tmp.append(now)
                         k[i]=tmp[::-1]
                         
                         
                         break
                        
                     tmp.append(now)
                     now=b[now]
        return c,b,k #返回的k就是路径.从开始到结尾
        

        










g=Graph(6)
g.addedge(0,1)
g.addedge(0,2)
g.addedge(0,3)
g.addedge(1,2)
g.addedge(2,3)
g.addedge(1,4)
g.addedge(2,4)
g.addedge(2,5)
g.addedge(3,5)
g.addedge(4,5)
g.addweight(0,1,6)
g.addweight(0,3,5)
g.addweight(0,2,1)
g.addweight(1,4,3)
g.addweight(4,5,6)
g.addweight(2,1,5)
g.addweight(3,2,5)
g.addweight(2,4,6)
g.addweight(2,5,4)
g.addweight(3,5,2)

print (g.bellman_ford(0))

#成功得到了4个分支,并且还是按照标号
#的顺序进行排列的分支.然后随意修改edge,都成功了.
'''
下面继续学习图:发现博客上面的比算法导论上面的方法更先进.
http://blog.csdn.net/moshenglv/article/details/72954701'''
'''

强连通图(Strongly Connected Graph)是指在有向图G中,如果对
于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,
则称G是强连通图。'''

'''

1.图的存储结构:

    1.邻接矩阵
    2.邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,
    这种结构存在空间上的极大浪费,
    因此找到一种数组与链表相结合的存储方法称为邻接表。
    3. 十字链表的好处就是因为把邻接表和逆邻接表整合在一起,这样既容易
    找到以v为尾
    的弧,也容易找到以v为头的弧,因而比较容易求得顶点的出度和入度。
    而且除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同
    的,因此,在有向图应用中,十字链表是非常好的数据结构模型。(相当复杂)
'''
'''
下面先写bfs'''
View Code

 还有一个djkstra的高速方法.

这个4里面的算法太有用了:比如一个地图里面很多个点之间的距离都有,求一条a到b的最短路径.就可以用这个方法求得.

原文地址:https://www.cnblogs.com/zhangbo2008/p/8617827.html