数据结构——图的基础

数据结构——图


图是由点与边组成的,通常表示为G(V,E),其中G表示图,V为点的集合,E为边的集合。图差不多是树的升级版,树只能父母节点连接子女节点,但是图的话允许所有的节点都可以互连。如下是一种常见的图(社交关系网络图)

在这里插入图片描述

图的应用


图可以用来存储关系复杂的数据,例如典型的社交网络的数据。可以通过图来存储社交软件中各个联系人间的关系信息。还有就是平时手机上用的地图软件,地图中的路线信息估计也是由图来存储的,用有权重的图来存储之后可以通过一些特定的算法,如直接通过暴力枚举,来获取一个最短导航的路径。

图的分类


有向图
无向图
有向带权图
有向不带权图
无向带权图
无向不带权图

有向图

在这里插入图片描述

无向图

在这里插入图片描述

图的表示


邻接矩阵

首先可以利用矩阵来表示图,例如我们有下面一张图。
在这里插入图片描述

图中三个点,构造一个三阶矩阵, A 33 A_{33} A33,一开始时让所有元素都为0。当1点连上了2点时,就让 A 12 = 1 A_{12}=1 A12=1表示,后面的以此类推。最后能得到下面的邻接矩阵。

[ 0 1 0 0 0 0 1 1 0 ] \left[\begin{matrix} 0 & 1 & 0 \\ 0 & 0 & 0 \\ 1& 1&0 \end{matrix}\right] 001101000

邻接矩阵适合用来表示稠密图(点少边多)

C++代码
/* 
邻接矩阵建图 
适合稠密图(点少边多) 
*/  
const int maxn = 1e5+5; // 图中点的最大数量  
int g[maxn][maxn]; // 邻接矩阵 g[u][v]表示u->v的边的数量  
int main(){  
int n,m;  // 点数n 边数m  
memset(g,0,sizeof(g));  // 清空矩阵  
cout<<"请输入图的点数和边数"<<endl;  
cin>>n>>m;  
cout<<"请输入每一条边(示例:1 2表示1到2有一条边)"<<endl;  
for(int i=0,v,u;i<m;++i){  
	cin>>v>>u;  
	++g[v][u];  
    // g[u][v]=1;  // 若建无向图则要建立双向边  
	    }  
	}

Python代码
#此函数用于清空矩阵
def memset(g,value,maxn):
    for i in range(maxn):
        g.append([])
        for j in range(maxn):
            g[i].append(0)
#邻接矩阵建图
maxn = int(3) #图中点的最大数量
g = [] #邻接矩阵 g[u][v]表示u->v的边的数量
memset(g,0,maxn)
#输入点数与边数,后面可以利用边的数量来限制输入的关系数量
n,m = map(int,input("请输入图的点数和边数:").split())
for i in range(1,m+1):
    v,u = map(int, input("请输入每一条边(示例:1 2表示1到2有一条边)").split())
    g[v-1][u-1] = 1
print(g)

如下为运行结果

在这里插入图片描述

邻接表表示

在这里插入图片描述

邻接表的思路就是通过建立一个表来记录每个节点的信息,这些信息包括这个节点连接的节点的序号与权重等等。

邻接表建图适合稀疏图(点多边少)

C++代码
	/* 
	 *  使用vector实现邻接表 
	 *  邻接表建图适合稀疏图(点多边少) 
	 */  
	class Edge{  
	public:  
	    int from;   // 始点  
	    int to;     // 终点  
	    Edge(){}  
	    Edge(int _from,int _to):from(_from),to(_to) {}  
	};  
	class Graph{  
	    int point_nums;  // 点的数量  
	    int edge_nums;   // 边的数量  
	    vector<vector<Edge> > G;  
	public:  
	    Graph(int n,int m):point_nums(n), edge_nums(m),G(point_nums) {}  
	    int add_edge(int from,int to){   // 增加一条从from点到to点的单向边  
	        G[from].push_back(Edge(from,to));   // 尾插法在点from的出边表插入边  
	    }  
	};  
	int main(){  
	    int n,m;  // 点数n 边数m  
	    cout<<"请输入图的点数和边数"<<endl;  
	    cin>>n>>m;  
	    Graph g(n,m);  
	    cout<<"请输入每一条边(示例:1 2表示1到2有一条边)"<<endl;  
	    for(int i=0,v,u;i<m;++i){  
	        cin>>v>>u;  
	        g.add_edge(v,u);  
	        // g.add_edge(u,v);  // 若建无向图则要建立双向边  

Python代码
# 节点类,此类用来存储每个点的数据 包括这个点连接了哪条边权重多少等等
class Vertex:

    def __init__(self, id):
        # 点的ID
        self.id = id
        # 点的连接信息
        self.connect_to = {}

    # 添加连接,一开始默认权重是0的,所以连接时要设置一下权重
    def add_neighbor(self, nbr_id, weight=0):
        self.connect_to[nbr_id] = weight

    # 获取这个点所连接的点的所有数据
    def get_connections(self):
        return self.connect_to.keys()

    # 放回此点的id号
    def get_id(self):
        return self.id

    # 获取连接某个点的权重
    def get_weight(self, nbr_id):
        return self.connect_to[nbr_id]

    def __str__(self):
        return f"点{self.id}"


# 图类,上面的类只是用来存储一个点的数据的,那么下面这个类则是存储图中所有点的数据的
class Graph:

    def __init__(self):
        # 存储的主体,用字典来存储
        self.vert_list = {}
        # 用于表示图中有多少个点
        self.num_vertices = 0

    # 添加点
    def add_vertex(self, id):
        # 图中点的数量加一
        self.num_vertices += 1
        # 新建一个点类
        new_vert = Vertex(id)
        # 将点类添加到图中
        self.vert_list[id] = new_vert
        return new_vert

    # 获取某个点的数据
    def get_vertex(self, id):
        # 设置一个判断语句,若输入的Id确实存在再返回,否则返回none
        if id in self.vert_list:
            return self.vert_list[id]
        else:
            return None

    # 添加边,此函数的from_代表连接的点类,to表示被连接的点类,weight表示这条边的权重
    def add_edge(self, from_, to, weight=0):
        # 首先需要判断from_与to两个点是否存在,若不存在则创建
        if from_ not in self.vert_list:
            new_vertex = self.add_vertex(from_)
        if to not in self.vert_list:
            new_vertex = self.add_vertex(to)
        # 调用点类的add_neighbor方法来进行添加
        self.vert_list[from_].add_neighbor(self.vert_list[to], weight)

    def get_all_vertices(self):
        # 返回所有节点的信息
        return self.vert_list.keys()

    def __contains__(self, id):
        return id in self.vert_list

    # 此函数是用来迭代的,若不设置的话图对象就无法直接迭代
    def __iter__(self):
        return iter(self.vert_list.values())

    def __str__(self):
        return "图类"


if __name__ == "__main__":
    #创建图类
    g = Graph()
    #打印图类 这里就用到了 __str__函数
    print(g)
    #迭代添加节点
    for i in range(6):
        g.add_vertex(i)
    #查看是否添加成功
    print(g.get_all_vertices())
    #添加边
    g.add_edge(0, 1, 1)
    g.add_edge(0, 2, 1)
    g.add_edge(1, 3, 1)
    #查看0节点连接的对象 对象是类所以返回地址
    print(g.vert_list[0].get_connections())
    #遍历图里面的点数据
    for v in g:
        print('v=', v)
        for w in v.get_connections():
            # print('w=', w)
            print("({},{})".format(v.get_id(), w.get_id()))
            
#以下为运行结果
'''
图类
dict_keys([0, 1, 2, 3, 4, 5])
dict_keys([<__main__.Vertex object at 0x00000110A5DDBC70>, <__main__.Vertex object at 0x00000110A5DE4BB0>])
v= 点0
(0,1)
(0,2)
v= 点1
(1,3)
v= 点2
v= 点3
v= 点4
v= 点5
'''

图的遍历


深度优先遍历

在这里插入图片描述

广度优先遍历

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值