【追求卓越13】算法--深度和广度优先算法

引导

        前面的几个章节,我们介绍了树这种数据结构,二叉搜索树在进行查找方面比较高效;有二叉树演变来的堆数据结构在处理优先级队列,top K,中位数等问题比较优秀;今天我们继续介绍新的数据结构——图。它在解决多对多的问题上占有优势,比如:存储微信,微博等用户之间的好友,粉丝关系。

图的相关概念

结合图来介绍:

顶点:在树结构中,每个元素我们称为节点,图数据结构中我们称为顶点。比如,图中的A,B,C,D,E,F六个顶点

:图中的一个节点可以和任意其他节点建立关系,我们称这个关系为边。比如图中A->C,就是一个边。

:描述的是一个顶点相连边的个数。比如A顶点的度是3
上图是无向图,实际中我们一般处理的是有向图。

在有向图中,度分为入度和出度。

入度:表示多少个边指向该顶点。比如,A顶点的入度是1.

出度:表示多少个边是以这个顶点为起点指向其它顶点。比如,A顶点的出度是2.
在QQ中我们还有并表示用户之间的亲密度的功能,这个我们可以使用带权图来表示。

每个边上带有权重,表示亲密度。比如A有两个好友B和C,与C的亲密度要高。

如何表示图

        图的基本概念我们已经介绍完了,代码中我们如何表示图呢?图的表示方法有两种,分别是邻接矩阵邻接表
邻接矩阵底层依赖二维数组进行存储。如果有N个顶点,就建立Array[N][N]的二维数组。表示方式:
        如果是无向图,如果顶点i和顶点j之间有边,我们就将Array[i][j]和Array[j][i]置为1;对于有向图,如果顶点i到顶点j之间,有一条从i指向j的边,我们就将Array[i][j]置为1;对于带权图,和有向图类似,不过值不再是1,而是权重。
        因此,对于实际问题,我们需要先将用户存储到数组中,保存每个用户的下标。
邻接矩阵虽然简单,但比较浪费空间。

  1. 对于无向图,实际上我们只需要将Array[i][j]置为1即可,不需要置为Array[j][i]。实际我们只需矩阵的右上半部即可。
  2. 如果我们存储的是稀疏图(边的个数少),其实很也是很浪费资源的。

        但是对于用户量不是很大的情况下,我们使用邻接矩阵还是比较好的。
邻接表其实和散列表相似,依赖于指针数组存储。有N个用户,就创建Array[N]的数组大小。数组里存储的是顶点指向的顶点。
如图:

当然随着数据的变多,链表长度肯定会变长,我们可以使用哈希,或搜索二叉树来加快搜索。

广度优先算法

        数据结构是服务于算法的,广度优先算法就非常适合使用图来实现。广度优先算法(Breadth-First-Search),我们平常简称为BFS。
        BFS一般是用于解决两样东西之间最短距离的问题;这个最短距离可以是导航中,最快到达目的地的路径顺序;也可以是象棋中,至少需要多少步取得胜利的方式;根据人脉关系,如何找到关系最近的医生。

        之前我学习算法图解的时候,我写过一个广度优先算法的程序,只不过当时并没有说明图的概念,完全用数组来实现广度优先算法-CSDN博客。现在结合图来理解,其实用到了邻接矩阵实现。并且现在对BFS又有了更加深入的理解。

思路:

  1. 建立邻接表,保存每个用户之间的关系。
  2. 从起始点,开始遍历,遍历完所有的相邻节点之后,再从相邻节点遍历。直至达到目标节点。

其实,这就是一个按层搜索的暴力解决方式。代码如下(没有运行验证,但思路应该如此,我会尽量说明详细);

/*
有图可知,该图是一个无向图
*/
/
*用于描述顶点信息*
/
typedef struct node
{
    int data;
    Node* next;
}Node;
/
*邻接表*
/
#deifne MAX 16
Node * peoples[MAX] = {0};
/
*表示建立a顶点和b顶点之间的关系*
/
int create_adjtable(int a , int b)
{
    Node * temp = &peoples[a];
    Node * peoples = NULL;
    while(temp!= NULL && temp->next != NULL)
    {
        temp = temp->next;
    }
    Node * people = (Node*) malloc(sizeof(Node));
    people->data = b;
    people->next = NULL;
    if(temp == NULL)
        temp = people;
    else
        temp->next = people;
    /
*一条边是双向的*
/  
    temp = &peoples[b];
    while(temp!= NULL && temp->next != NULL)
    {
        temp = temp->next;
    }
    Node * people = (Node*) malloc(sizeof(Node));
    people->data = a;
    people->next = NULL;
    if(temp == NULL)
        temp = people;
    else
        temp->next = people;
}
/
*表示求start顶点到end顶点的最短距离*
/
int BFS(int start , int end)
{
    if(start == end) return 0;
/
*用于记录已经访问的节点*
/
    int visit[MAX] ={0};
    /
*用于记录到达该顶点的上一个顶底,比如2->3,我们就记录prev[3]=2,表示是从2顶点到达3顶点*
/
    int prev[MAX] = {0};
    visit[start] = 1;
/
*queue表示已经访问顶点的相邻顶点,至于queue的长度为什么为MAX就可以了?*
/
    int queue[MAX] = {0};
    int index = 0;
    queue[index]=start;
    int i = 0;
    /**/
    while(queue[i] != 0)
    {
        Node * temp = peoples[queue[i]];
        while(temp->next != NULL)
        {
            /
*表示该顶点没有访问*
/
            if(visit[temp->data] == 0)
            {
                /
*temp->data顶点是由queue[i]顶点过来的*
/
                preve[temp->data] = queue[i];
                if(temp->data == end)
                {
                    print(prev,start,end);
                    return 0;
                }
                queue[++index] = temp->data;
                visit[temp->data]=1;
            }
            /
*对于访问过的顶点,我们不再处理,因为广度优先算法是按层搜索的,即使遇到访问过的顶点,那一定不是最短路径,故可以忽略*
/
            temp = temp->next;
        }
i++;
    }
retunr -1;
}
/*递归打印*/
int print(int prev,int start,int end)
{
    if(end != start)
        print(prev,start,prev[end])
    printf("%d->",end);
}

该代码还没有调试,但思路大致如此,如有问题还请见谅。

时间复杂度和空间复杂度

        假设图的顶点个数是V,边的个数是E。从代码的实现可知,空间复杂度不会大于O(V),毕竟start到end并不一定会遍历所有顶点。
        空间上的消耗主要是queue,prev,visit三个数组,但它们的长度不会多于V,故空间复杂度是O(V)

深度优先算法

        深度优先算法(Depth-First-Search),简称DFS。常用于走迷宫。还记得刚毕业时,我就遇到这样的面试题,结果是惨败。
        DFS和BFS相似,但实现逻辑不太一样。BFS是按层搜索找到最短路径。而DFS是按照深度搜索,他找到的路径可能不是最短路径。
如图:

不同的算法结果是不同的:

BFS算法结果:A,B,C,D,E,F,G,H

DFS算法结果:A,B,D,E,C,F,G,H
代码实现:

int dfs(int start,int end)
{
    int visit[MAX] = {0};
    int prev[MAX] = {0};
    recurdfs(visit,prev,start,end);
    print(rev,start,end);
    return 0;
}
void recurdfs(int visit[],int prev[],int start,int end)
{
    visit[start] = 1;
    if(start == end)
        return;
    Node * temp = peoples[start];
    while(temp->next != NULL)
    {
        if(visit[temp->data] == 0)
        {
            prev[temp->data] = start;
            recurdfs(visit,prev,temp->data,end);
        }
        temp = temp->next;
    }
}

总结

        本章内容较多,我们介绍了图这种数据结构,并且介绍了图的两种算法:BFS和DFS。这两种算法的思想和逻辑需要我们反序去理解和推敲

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

主机dbeaver访问gitlab容器中的pg

映射5432端口- 5431:5432或者从docker客户端查看 version: 3.6 services:web:image: gitlab/gitlab-ce:latestrestart: alwayshostname: localhostenvironment:GITLAB_OMNIBUS_CONFIG: |external_url http://localhost:8929gitlab_rails[gitlab_shell_ssh_port] 2224ports:- …

Leetcode:622. 设计循环队列 题解【具详细】

目录 一、题目: 二、思路详解: 1.循环队列的存储定义 2.循环队列的创建 3.循环队列的判空与判断情况 (1) 循环队列的判空: (2) 循环队列的判满 4.循环队列元素的插入 5.循环队列元素的删除 6.获取队头元素 7.获取队尾元素 8.循环队列释放 三…

【Java】用Java库中自带的阻塞队列以及用阻塞队列实现生产者-消费者模型

1、阻塞队列(BlockingDeque) 首先我们来认识一下什么是堵塞队列 阻塞队列即实现了线程安全和阻塞的队列。在队列为空时,获取元素的线程会等待队列存放元素变为非空;在队列满时,存放元素的线程会等待队列取出元素变为不满。 阻塞队列常应用于生…

Elasticsearch:FMA 风格的向量相似度计算

作者:Chris Hegarty 在 Lucene 9.7.0 中,我们添加了利用 SIMD 指令执行向量相似性计算的数据并行化的支持。 现在,我们通过使用融合乘加 (Fused Mulitply-Add - FMA) 进一步推动这一点。 什么是 FMA 乘法和加法是一种常见的运算,…

ansible的基本安装

目录 一、简介 1.ansible自动化运维人工运维时代 2.自动化运维时代 3.ansible介绍 4.ansible特点 二、ansible实践 1.环境 2.ansible管理安装 3.ansible被管理安装 4.管理方式 5.添加被管理机器的ip 6.ssh密码认证方式管理 三、配置免密登录 1.ansible自带的密码…

网站被攻击了怎么办,有什么办法防御攻击?

近年来,随着互联网发展,出现了各种各样的网站,web应用,网络极大方便了人们的生活,改变了人们生活方式。而随着网络的发展普及,网络安全问题也困扰着用户。 许多人都曾有过这样经历,网站上线后&…

Pytorch中的tensor维度理解

Pytorch中的tensor维度理解 文章目录 Pytorch中的tensor维度理解摘要打消心理恐惧,从三维学起三维tensor参考文献 摘要 面对pytorch编程中的tensor时,我不时会感到恐惧。对里面数据是怎么排布的,一直没有一个直观的理解。今天我想把这个事情…

微信小程序开发者工具] ? Enable IDE Service (y/N) ESC[27DESC[27C

在HBuilder运行微信小程序开发者工具报错 如何解决 打开微信小程序开发者工具打开设置--->安全设置--->服务器端口选择打开就可以啦

3.11-容器的资源限制

这一小节我们来看一下如何限制容器的资源,比如CPU和内存。 我们先来看一下对内存的限制。 --memory和--memory-swap这两个参数,如果我们只限定了--memory,没有限定--memory-swap,那么--memory-swap的大小就会和--memory大小一样。…

程序员兼职月入30k如何做,快来收藏这21个宝藏平台!

最近经济不太景气,大家都各种方法发展副业收入开源,在某职场论坛上,看到这样一个帖子:某程序员找了3份兼职,晒出自己月入3万的收入! 很多网友对此表示羡慕,评论区很多人在问,这个程…

我在Vscode学OpenCV 几何变换(缩放、翻转、仿射变换、透视、重映射)

几何变换指的是将一幅图像映射到另一幅图像内的操作。 cv2.warpAffine:使用仿射变换矩阵对图像进行变换,可以实现平移、缩放和旋转等操作。cv2.warpPerspective:使用透视变换矩阵对图像进行透视变换,可以实现镜头校正、图像纠偏等…

元素的点击操作

元素的点击操作 .click 语法 // 单击某个元素 .click()// 带参数的单击 .click(options)// 在某个位置点击 .click(position)// 在某个位置点击,且带参数 .click(position, options)// 根据页面坐标点击 .click(x, y)// 根据页面坐标点击,且带参数 .c…