【生物信息学算法】图算法1:概念和算法

news2024/12/23 0:39:53

文章目录

    • 1. 图的定义、分类、表达方式
      • 图的定义
      • 图的分类
      • 表达方式
      • Python实现
    • 2.相邻节点和度
      • 概念定义
      • python实现
    • 3.路径、距离和搜索
      • 路径和距离
      • 搜索
    • 4.图论中的欧拉定理

1. 图的定义、分类、表达方式

图的定义

图G可以由两个集合来定义,即G=(V,E)。其中,V是对象的集合,称为图的顶点或节点; E是V中(u,v)顶点对的集合,称为边或弧,表示u和v之间的关系存在。

图的分类

  1. 有向图:E有方向性,即顶点对是有序的。
  2. 无向图:E无方向性,即顶点对是无序的。
  3. 加权图:对E中的边赋予数值权重。

表达方式

图形,邻接矩阵,邻接列表

·邻接矩阵:行列表示图的节点;矩阵中的具体数值,在无权图中主要表示是否存在该边(以及该边的方向),在加权图中则会包含权重的信息
·当图是稀疏时,使用基于邻接列表的实现存储更有效

Python实现

class MyGraph:
    # 定义图的类

    def __init__(self, g={}):
        '''
        构造函数,接受一个字典作为输入来填充图的结构;
        默认为一个空字典。
        :param g: 图的初始结构,默认为空字典
        '''
        self.graph = g

    # 获取图的基本信息
    def get_nodes(self):
        '''
        获取图中的所有节点(顶点)。
        :return: 节点列表
        '''
        return list(self.graph.keys())

    def get_edges(self):
        '''
        获取图中的所有边。
        :return: 边的列表,列表中的每个元素是一个元组,表示两个相连的节点
        '''
        edges = []
        # 遍历所有节点
        for v in self.graph.keys():
            # 遍历每个节点的邻接列表
            for d in self.graph[v]:
                # 将每条边(节点对)添加到边列表中
                edges.append((v, d))
        return edges

    def size(self):
        '''
        返回图的节点数和边数。
        :return: 一个元组,包含节点数和边数
        '''
        return len(self.get_nodes()), len(self.get_edges())

    def print_graph(self):
        '''
        打印图的邻接列表表示法。
        每个节点及其相邻节点列表都会输出。
        '''
        for v in self.graph.keys():
            print(v, " -> ", self.graph[v])

    def add_vertex(self, v):
        '''
        向图中添加一个新的节点(顶点)。
        如果节点已存在,则不添加。
        :param v: 要添加的节点
        '''
        if v not in self.graph.keys():
            self.graph[v] = []

    def add_edge(self, o, d):
        '''
        向图中添加一条新的边。
        如果边的两个节点(顶点)不存在,则会自动添加这些节点。
        :param o: 边的起始节点
        :param d: 边的目标节点
        '''
        # 如果起始节点不存在,则添加该节点
        if o not in self.graph.keys():
            self.add_vertex(o)
        # 如果目标节点不存在,则添加该节点
        if d not in self.graph.keys():
            self.add_vertex(d)
        # 如果目标节点不在起始节点的邻接列表中,则添加该边
        if d not in self.graph[o]:
            self.graph[o].append(d)

2.相邻节点和度

概念定义

有向图G=(V,E)中,若边的集合E中存在有序对(s,v),则顶点v是顶点s的后继(successor),s称为v的前身;两个顶点s和v被命名为邻接,即如果一个顶点是另一个顶点的后继,则两个顶点是邻接的。

节点度给定节点的相邻节点数,在有向图中:入度为计算一个节点的前置数出度为一个节点的后继数

python实现

接上

    def get_successors(self, v):
        '''
        获取节点v的所有后继节点(邻接节点)。
        返回v的邻接列表的副本,避免列表被覆盖。
        :param v: 节点
        :return: 节点v的所有后继节点的列表
        '''
        return list(self.graph[v])  # 返回节点v的邻接列表的副本,避免原列表被修改

    def get_predecessors(self, v):
        '''
        获取图中所有指向节点v的前驱节点。
        :param v: 节点
        :return: 节点v的所有前驱节点的列表
        '''
        res = []  # 用于存储前驱节点的列表
        # 遍历所有节点,检查它们的邻接列表
        for k in self.graph.keys():
            if v in self.graph[k]:  # 如果节点v在节点k的邻接列表中,说明k是v的前驱节点
                res.append(k)
        return res

    def get_adjacents(self, v):
        '''
        获取与节点v相连的所有相邻节点,包括前驱和后继。
        :param v: 节点
        :return: 节点v的所有相邻节点的列表
        '''
        suc = self.get_successors(v)  # 获取v的后继节点
        pred = self.get_predecessors(v)  # 获取v的前驱节点
        res = pred  # 将前驱节点列表赋值给res
        # 检查所有后继节点,如果它们不在前驱节点列表中,则添加到结果列表中
        for p in suc:
            if p not in res:
                res.append(p)
        return res

    def out_degree(self, v):
        '''
        计算节点v的出度(从该节点出发的边的数量)。
        :param v: 节点
        :return: 节点v的出度
        '''
        return len(self.graph[v])  # 节点v的邻接列表的长度即为出度

    def in_degree(self, v):
        '''
        计算节点v的入度(指向该节点的边的数量)。
        :param v: 节点
        :return: 节点v的入度
        '''
        return len(self.get_predecessors(v))  # 前驱节点的数量即为入度

    def degree(self, v):
        '''
        计算节点v的度数(与该节点相连的边的数量,包括入度和出度)。
        :param v: 节点
        :return: 节点v的度数
        '''
        return len(self.get_adjacents(v))  # 相邻节点的数量即为度数
    def all_degrees(self, deg_type="inout"):
        '''
        计算所有节点的度数(入度、出度或总度数)。
        :param deg_type: 度数类型,可以是 "in"(入度)、"out"(出度)或 "inout"(总度数)
        :return: 一个字典,键是节点,值是对应的度数
        '''
        degs = {}  # 创建一个空字典用于存储每个节点的度数

        # 遍历所有节点,计算出度或总度数
        for v in self.graph.keys():
            # 如果度数类型是出度("out")或总度数("inout")
            if deg_type == "out" or deg_type == "inout":
                degs[v] = len(self.graph[v])  # 节点v的出度是其邻接列表的长度
            else:
                degs[v] = 0  # 如果不是计算出度,初始化为0

        # 遍历所有节点,计算入度或总度数
        if deg_type == "in" or deg_type == "inout":
            for v in self.graph.keys():
                # 遍历节点v的邻接节点
                for d in self.graph[v]:
                    # 如果度数类型是入度("in")或者v不在d的邻接列表中(避免重复计算)
                    if deg_type == "in" or v not in self.graph[d]:
                        degs[d] = degs[d] + 1  # 对应节点的度数加1

        return degs  # 返回包含所有节点度数的字典

3.路径、距离和搜索

路径和距离

路径(path):在有向图中,定义为节点的有序列表,其中列表中的连续节点需要通过边连接。即这个过程中的每一步都是从一个节点沿着图中的一条“边”走到另一个节点。所以路径就是这些节点的有序排列。

在图 G=(V, E) 中:

  • 有向图中,节点 x 和任意节点 y 之间的路径 P 是列表 P = p 1 , p 2 , … , p n P = p_1, p_2, \ldots, p_n P=p1,p2,,pn,其中 p 1 = x p_1 = x p1=x, p n = y p_n = y pn=y,以及 P P P 上的所有连续节点对 ( p i , p i + 1 ) ∈ E (p_i, p_{i+1}) \in E (pi,pi+1)E 。即路径上的每一对相邻节点 p i , p i + 1 p_i, p_{i+1} pi,pi+1 都必须是有向边集合 E E E 中的一条边。
  • 无向图中,则 ( p i , p i + 1 ) ∈ E (p_i, p_{i+1}) \in E (pi,pi+1)E ( p i + 1 , p i ) ∈ E (p_{i+1}, p_i) \in E (pi+1,pi)E
    最短路径:两个节点之间边数最少的路径,最短路径的长度称为两点间的距离
    代码实现:
    def distance(self, s, d):
        '''
        计算从节点s到节点d的最短路径的距离(使用广度优先搜索算法)。
        :param s: 起始节点
        :param d: 目标节点
        :return: 从s到d的最短距离,如果没有路径返回None
        '''
        if s == d:  # 如果起始节点等于目标节点,距离为0
            return 0
        l = [(s, 0)]  # 初始化队列l,包含起始节点和初始距离0
        visited = [s]  # 初始化已访问列表,包含起始节点

        # 当队列不为空时,继续搜索
        while len(l) > 0:
            node, dist = l.pop(0)  # 弹出队列的第一个元素,获取当前节点和当前距离
            # 遍历当前节点的邻接节点
            for elem in self.graph[node]:
                if elem == d:  # 如果找到目标节点,返回距离加1
                    return dist + 1
                elif elem not in visited:  # 如果邻接节点未访问过
                    l.append((elem, dist + 1))  # 将邻接节点加入队列,距离加1
                    visited.append(elem)  # 标记邻接节点为已访问

        return None  # 如果没有找到路径,返回None

    def shortest_path(self, s, d):
        '''
        查找从节点s到节点d的最短路径(使用广度优先搜索算法)。
        :param s: 起始节点
        :param d: 目标节点
        :return: 从s到d的最短路径(节点列表),如果没有路径返回None
        '''
        if s == d:  # 如果起始节点等于目标节点,返回空路径
            return 0
        l = [(s, [])]  # 初始化队列l,包含起始节点和初始路径(空列表)
        visited = [s]  # 初始化已访问列表,包含起始节点

        # 当队列不为空时,继续搜索
        while len(l) > 0:
            node, preds = l.pop(0)  # 弹出队列的第一个元素,获取当前节点和路径
            # 遍历当前节点的邻接节点
            for elem in self.graph[node]:
                if elem == d:  # 如果找到目标节点,返回完整路径
                    return preds + [node, elem]
                elif elem not in visited:  # 如果邻接节点未访问过
                    l.append((elem, preds + [node]))  # 将邻接节点加入队列,更新路径
                    visited.append(elem)  # 标记邻接节点为已访问

        return None  # 如果没有找到路径,返回None

搜索

广度优先搜索(BFS):从源节点开始,然后访问其所有后续节点,然后访问这些后续节点的后续节点直到访问所有可能的节点
深度优先搜索(DFS):从源节点开始,先搜索第一个后继节点,然后再搜索其第一个后继节点直到无法进行进一步的搜索,然后回溯以探索其他替代方案

代码实现:

    def reachable_bfs(self, v):
        '''
        使用广度优先搜索(BFS)算法找到从节点v可以到达的所有节点。
        :param v: 起始节点
        :return: 从v可以到达的所有节点的列表
        '''
        l = [v]  # 初始化列表l,包含起始节点v,作为搜索队列
        res = []  # 初始化结果列表,用于存储已访问的节点

        # 当搜索队列不为空时,继续搜索
        while len(l) > 0:
            node = l.pop(0)  # 取出队列的第一个节点
            # 如果节点不是起始节点,添加到结果列表
            if node != v:
                res.append(node)
            # 遍历当前节点的所有邻接节点
            for elem in self.graph[node]:
                # 如果邻接节点不在结果列表和搜索队列中,加入队列
                if elem not in res and elem not in l:
                    l.append(elem)

        return res  # 返回所有从节点v可以到达的节点

    def reachable_dfs(self, v):
        '''
        使用深度优先搜索(DFS)算法找到从节点v可以到达的所有节点。
        :param v: 起始节点
        :return: 从v可以到达的所有节点的列表
        '''
        l = [v]  # 初始化列表l,包含起始节点v,作为搜索堆栈
        res = []  # 初始化结果列表,用于存储已访问的节点

        # 当搜索堆栈不为空时,继续搜索
        while len(l) > 0:
            node = l.pop(0)  # 取出堆栈的第一个节点
            # 如果节点不是起始节点,添加到结果列表
            if node != v:
                res.append(node)
            s = 0  # 位置变量,用于控制新元素插入的位置(保持DFS的堆栈顺序)
            # 遍历当前节点的所有邻接节点
            for elem in self.graph[node]:
                # 如果邻接节点不在结果列表和搜索堆栈中,插入堆栈顶部
                if elem not in res and elem not in l:
                    l.insert(s, elem)  # 在索引s的位置插入元素
                    s += 1  # 更新位置变量

        return res  # 返回所有从节点v可以到达的节点

·如果一条路径在同一个顶点上开始和结束,则该路径被定义为闭合的
·如果在闭合路径中没有重复的节点或边,则该路径称为。(主要为了排除两个节点间的一来一回)
代码实现:

def node_has_cycle(self, v):
    '''
    检查从给定节点v开始的图中是否存在环(使用广度优先搜索算法)。
    :param v: 起始节点
    :return: 如果存在环,返回True;否则返回False
    '''
    l = [v]  # 初始化队列l,包含起始节点v
    res = False  # 初始化结果为False,表示暂未发现环
    visited = [v]  # 初始化已访问列表,包含起始节点v

    # 当队列不为空时,继续搜索
    while len(l) > 0:
        node = l.pop(0)  # 弹出队列的第一个元素,获取当前节点
        # 遍历当前节点的所有邻接节点
        for elem in self.graph[node]:
            if elem == v:  # 如果邻接节点等于起始节点,说明存在环
                return True
            elif elem not in visited:  # 如果邻接节点未访问过
                l.append(elem)  # 将邻接节点加入队列
                visited.append(elem)  # 标记邻接节点为已访问

    return res  # 如果没有发现环,返回False

def has_cycle(self):
    '''
    测试图中是否存在环(从任意节点开始)。
    :return: 如果存在环,返回True;否则返回False
    '''
    res = False  # 初始化结果为False,表示暂未发现环
    # 遍历所有节点,测试每个节点是否是环的起点
    for v in self.graph.keys():
        if self.node_has_cycle(v):  # 如果从节点v开始存在环
            return True
    return res  # 如果没有发现环,返回False

4.图论中的欧拉定理

欧拉迹(欧拉路径):在图论中,欧拉迹是指一条经过图中所有边且恰好一次的路径。这条路径可以重复访问顶点,但不能重复访问边。
欧拉回路:在图论中,欧拉回路指的是一种通过图中所有边恰好一次,并且最终回到起点的闭合路径
连通图:如果图中的任意两个顶点之间都有路径相连,那么这个图被称为连通图。换句话说,连通图是一个没有孤立部分的图,图中的所有顶点都是相互可达的。
欧拉定理连通图存在欧拉迹当且仅当图中奇度数的点的个数至多为 2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2113575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MapSet之二叉搜索树

系列文章: 1. 先导片--Map&Set之二叉搜索树 2. Map&Set之相关概念 目录 前言 1.二叉搜索树 1.1 定义 1.2 操作-查找 1.3 操作-新增 1.4 操作-删除(难点) 1.5 总体实现代码 1.6 性能分析 前言 TreeMap 和 TreeSet 是 Java 中基于搜索树实现的 M…

申万宏源证券完善金融服务最后一公里闭环,让金融服务“零距离、全天候”

在数字化转型的浪潮中,申万宏源作为金融行业的先锋,持续探索科技如何赋能金融服务,以提升企业效率并优化客户服务体验。面对日益增长的视频化需求,传统的图文形式已难以满足市场与用户的新期待。为了应对这一挑战,申万…

简单梳理一个历史脉络

B 站上王山水老师的一个视频引发的思考:没有司马篡国,能避免300年的大乱世吗? 我的答案如下: 视野放宽到欧亚大陆,广义上公元184年黄巾军起义开启内乱,狭义上公元220年正式进入三国,280年晋统一…

新手做短视频素材在哪里找?做短视频素材工具教程网站有哪些?

本文将为你提供一系列新手友好的视频制作资源,包括素材网站和编辑工具,帮助你快速成为短视频领域的新星。让我们从国内知名的蛙学网开始介绍。 蛙学网:新手的视频素材天堂 对于短视频新手而言,蛙学网绝对是一个宝库。该网站提供了…

1-10 图像增强对比度 opencv树莓派4B 入门系列笔记

目录 一、提前准备 二、代码详解 enhanced_image cv2.convertScaleAbs(image, alpha1.5, beta0) 三、运行现象 四、完整工程贴出 一、提前准备 1、树莓派4B 及 64位系统 2、提前安装opencv库 以及 numpy库 3、保存一张图片 二、代码详解 import cv2 # 增强图像的对比度 …

环境配置!

一 安装CUDA 在安装CUDA之前,建议先看下pytorch的更新版本,应为pytorch更新较慢,请保证CUDA的版本,对应的pytorch版本存在。 去pytorch官网查看电脑支持的cuda版本最高是多少。PyTorch 我这边在网站上看最高支持的CUDA版本为12.…

SpringDataJPA系列(7)Jackson注解在实体中应用

SpringDataJPA系列(7)Jackson注解在实体中应用 常用的Jackson注解 Springboot中默认集成的是Jackson,我们可以在jackson依赖包下看到Jackson有多个注解 一般常用的有下面这些: 一个实体的示例 测试方法如下: 按照上述图片中的序号做个简…

【python】Python中如何通过rembg实现图片背景去除

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

Java预备知识 - day2

1.IDEA的简单使用与介绍 1.1 IDEA的项目工程介绍 Day2_0904:项目名称 E:\0_code\Day2_0904:表示当前项目所在路径 .idea:idea软件自动生成的文件夹,最好不要动 src:srcsourse→源,我们的源代码就放在这…

计算机网络知识点复习——TCP协议的三次握手与四次挥手(连接与释放)

TCP协议的三次握手与四次挥手(连接与释放) 一、前言二、简单的知识准备1. TCP协议的主要特点2. TCP报文段 三、TCP连接的建立(三次握手)四、TCP连接的释放(四次挥手)五、TCP连接与释放的总结六、结束语 一、…

计算机基础知识复习9.6

点对点链路:两个相邻节点通过一个链路相连,没有第三者 应用:PPP协议,常用于广域网 广播式链路:所有主机共享通信介质 应用:早期的总线以太网,无线局域网,常用于局域网 典型拓扑结…

qtdraw-使用qt绘图之开源源码学习

1. 资源介绍 功能:使用qt在画板上绘制各种形状,并保持绘制内容到xml文件中。 项目源码:https://github.com/egan2015/qdraw 软件界面: 1.1 支持shape 6种 1.2 支持的功能 6种,分别是对绘制的图形进行撤销undo&…

计算机网络(四) —— 简单Tcp网络程序

目录 一,服务器初始化 1.0 部分文件代码 1.1 关于Tcp协议 1.2 创建和绑定套接字 1.3 监听 二,服务器启动 2.1 获取连接 2.2 提供服务 2.3 客户端启动源文件 Main.cc 二,客户端编写 2.1 关于Tcp客户端 2.2 客户端代码 2.3 效果…

Linux第十一节课 - 进程

一个程序从磁盘以文件的形式加载到内存之后,已经变成了进程! 引入管理者和被管理者 1、管理者和被管理者不需要见面!(例如学生和校长!) 2、管理者在不见被管理者的情况下,如何做好管理呢&…

隐私计算实训营:SplitRec:当拆分学习遇上推荐系统

拆分学习的概念 拆分学习的核心思想是拆分网络结构。每一个参与方拥有模型结构的一部分,所有参与方的模型合在一起形成一个完整的模型。训练过程中,不同参与方只对本地模型进行正向或者反向传播计算,并将计算结果传递给下一个参与方。多个参…

文件操作与隐写

一、文件类型的识别 1、文件头完好情况: (1)file命令 使用file命令识别:识别出file.doc为jpg类型 (2)winhex 通过winhex工具查看文件头类型,根据文件头部内容去判断文件的类型 eg:JPG类型 &a…

Wni11 下 WSL 安装 CentOS

Wni11 下 WSL 安装 CentOS 方法一、安装包安装下载包安装安装打开 CentOS1. 从 Windows 终端 打开2. 从 PowerShell 打开 方法二、导入 CentOS 的 tar 文件进行安装0. 查看版本(可选)1. 导出 Docker 容器到 tar 文件2. 将 tar 文件导入 WSL2.1. 导入 tar…

macos安装ArgoCD

本文主要介绍如何在macos上安装并访问argo 我环境上已经安装了minikube,所以只需要启动minikube然后通过命令行安装argocd。 minikube start kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/st…

OpenGL(二)-更详细版的三角形

在上篇blog中已经画了一个三角形了,这篇讲解一下一个三角形的渲染过程。 上篇blog中的glbegin搭配glend的流程,在OpenGL3.2中已经被弃用了,3.3以后推荐使用VBOEBOVAO的流程。 图形渲染管线 作用:将三维坐标经过一系列变换&#x…

【Day09】

目录 Mybatis-基础操作-环境准备 Mybatis-基础操作-删除 Mybatis-基础操作-删除(预编译SQL) Mybatis-基础操作-新增 Mybatis-基础操作-新增(主键返回) Mybatis-基础操作-更新 Mybatis-基础操作-查询(根据ID查询) Mybatis-基…