广度优先搜索算法①-滚动数组及BFS

目录

滚动数组含义

为什么叫滚动数组

滚动数组应用场景

目录

滚动数组含义

为什么叫滚动数组

滚动数组应用场景

滚动数组实现方法

方法一:使用list实现滚动数组

方法二:使用队列(queue)实现滚动数组

使用滚动数组实现的算法举例

example-one:

题目:

第一步:先把无向图的数据结构定义出来

无向图的概念

定义无向图

第二步:使用列表构造滚动数组实现广度优先搜索算法

第三步:调用

完整代码及详细注释


滚动数组实现方法

方法一:使用list实现滚动数组

方法二:使用队列(queue)实现滚动数组

使用滚动数组实现的算法举例

example-one:

题目:

第一步:先把无向图的数据结构定义出来

无向图的概念:

定义无向图

第二步:使用列表构造滚动数组实现广度优先搜索算法

第三步:调用

完整代码及详细注释


滚动数组含义

        在Python中,滚动数组(Rolling Array)是一种常用的技巧,用于实现数据流的实时处理或跟踪最新的N个值。滚动数组可以理解为一个固定大小的数据结构,当新的元素添加进来时,最旧的元素会被移除。

为什么叫滚动数组

        从设计原理通俗易懂的理解为:在上限max_size的限制下,使得result永远保持固定的max_size个元素,这就使得最早添加进来的数据被移除(下面的案例中是判断数组长度与max_size相等以后就执行pop()移除一个),然后再将新的值使用append()函数在末端添加。

以这样的方式实现了先进先出,且使得数组保持高效,使算法能够解决时间复杂度和空间复杂度,形象的理解为有一个长度为N的数组,我们只要其中的max_size这么长的一截,好比一根电线杆很高,我们使用手电筒从下往上扫描,光出现在电线杆上的长度是固定的。

滚动数组应用场景

  • 实时统计最近一段时间内的平均值、最大值、最小值等统计数据。
  • 在限制内存的情况下,处理大量的数据流。
  • 实现广度优先搜索算法、滑动窗口算法,如移动平均线、时间序列分析等。

滚动数组实现方法

        滚动数组的实现通常有两种方法:一种是使用列表(list),另一种是使用队列(queue)。

方法一:使用list实现滚动数组

def rolling_array(data, max_size):
    '''
    代码解释:
    1.定义一个名为rolling_array的函数,它接受两个参数:data和max_size。
    其中,data是一个列表,表示输入数据;max_size是一个整数,表示上限大小。
    2.创建一个空列表result,用于存储滚动数组的结果。
    3.使用一个for循环遍历输入数据data中的每个元素。在每次迭代中,我们可以通过索引i访问到当前的元素。
    4.检查result列表的长度是否等于max_size。
        如果是,那么我们需要从result的开头移除一个元素,以保持上限大小不变,这可以通过调用pop(0)方法实现。
    5.将当前的元素data[i]添加到result列表的末尾。这可以通过调用append()方法实现。
    6.循环结束后,返回result列表作为结果。
    :param data:测试数据集
    :param max_size:上限
    :return:在上限要求下的数组
    '''
    result = []
    for i in range(len(data)):
        if len(result) == max_size:
            result.pop(0)
        result.append(data[i])
    return result


# 调用和测试
data = [1, 2, 3, 4, 5]
max_size = 3
result = rolling_array(data, max_size)
print(result)  # 输出:[3, 4, 5]

方法二:使用队列(queue)实现滚动数组

import collections


def rolling_array(data, max_size):
    '''
    1.首先,导入Python标准库中的collections模块。
        这个模块提供了一些有用的容器数据类型,包括双端队列deque。
    2.定义一个名为rolling_array的函数,它接受两个参数:data和max_size。
        其中,data是一个列表,表示输入数据;max_size是一个整数,表示上限。
    3.创建一个collections.deque对象,并设置其maxlen参数为max_size。
        这样,当向队列中添加元素时,如果队列的长度超过了指定的上限,那么队列的头部元素将会自动被移除。
    4.使用一个for循环遍历输入数据data中的每个元素。在每次迭代中,我们可以通过变量i访问到当前的元素。
    5.将当前的元素i添加到队列result的末尾。这可以通过调用append()方法实现。
    6.循环结束后,将队列result转换为列表并返回。这可以通过调用list()函数实现。
    :param data:测试集
    :param max_size:上限
    :return:动态数组
    '''
    result = collections.deque(maxlen=max_size)
    for i in data:
        result.append(i)
    return list(result)


# 调用和测试
data = [1, 2, 3, 4, 5]
max_size = 3
result = rolling_array(data, max_size)
print(result)  # 输出:[3, 4, 5]

使用滚动数组实现的算法举例

example-one:

题目:

遍历一个给定的无向图,假设每个节点都有一个唯一的标签(比如ABC等),返回从给定点出发到所有可达顶点的最短路径。

第一步:先把无向图的数据结构定义出来

无向图的概念

无向图(Undirected Graph) 是一种数据结构,用于表示一组对象(称为顶点或节点)以及它们之间的关系(称为边)。在无向图中,每条边都没有方向,这意味着从一个顶点到另一个顶点的关系是双向的。

数学上,一个无向图 ( G ) 可以用 ( (V, E) ) 来表示,其中:

  • ( V ) 是一个非空集合,包含图中的所有顶点。
  • ( E ) 是一个由 ( V ) 中元素构成的二元组(即有序对)的集合,表示图中的边。对于一条边 ( (u, v) ),它表示顶点 ( u ) 和顶点 ( v ) 之间存在一条连接。

需要注意的是,在无向图中,( (u, v) ) 和 ( (v, u) ) 表示的是同一条边,因为边是没有方向的。因此,通常会使用集合来表示边集,而不是列表或其他顺序性数据结构。

无向图可以用来表示各种各样的问题,例如社交网络中的朋友关系、交通路线图等。

定义无向图
class Graph:
    '''
    这个Graph类有两个方法:
        __init__()
        add_edge()
    '''

    def __init__(self):
        '''
        __init__()初始化了一个空字典graph,用于存储图的边。
        '''
        self.graph = {}

    def add_edge(self, u, v):
        '''
        add_edge()方法接受两个参数:u和v,表示一条从顶点u到顶点v的边。
        当调用这个方法时,它会在图中添加这条边,并同时在u和v之间创建双向连接。
        :param u:
        :param v:
        :return:
        '''
        if u not in self.graph:
            self.graph[u] = []
        if v not in self.graph:
            self.graph[v] = []
        self.graph[u].append(v)
        self.graph[v].append(u)

第二步:使用列表构造滚动数组实现广度优先搜索算法

def bfs(graph, start):
    '''
    bfs()函数接受两个参数:
        graph,graph是一个Graph对象,表示要搜索的图;
        start,start是一个整数,表示搜索的起点。
        1.首先,初始化一个队列queue,并将起点start放入队列中。
            还创建了一个名为visited的集合,用于记录已经访问过的节点。
            此外,还创建了一个名为distance的字典,用于存储每个节点到起点的距离。
        2.接下来,进入一个while循环,只要队列不为空,就继续搜索。
            在每次迭代中,将队列中的第一个元素取出作为当前节点,并将其标记为已访问。
        3.然后,遍历当前节点的所有邻居。对于每一个未被访问的邻居节点,将它加入队列,并更新它的距离信息。
        4.当队列变为空时,说明已经搜索完了所有可达的节点。此时,可以返回distance字典,它包含了每个可达节点到起点的最短距离。
    :param graph:graph是一个Graph对象,表示要搜索的图
    :param start:tart是一个整数,表示搜索的起点
    :return:最短路径
    '''
    # 初始化队列、访问标志和距离映射
    queue = [start]
    visited = set()
    distance = {start: 0}

    while queue:
        # 取出队列的第一个元素作为当前节点
        current = queue.pop(0)

        # 记录当前节点已被访问
        visited.add(current)

        # 遍历当前节点的所有邻居
        for neighbor in graph[current]:
            if neighbor not in visited:
                # 将邻居节点加入队列,并更新其距离信息
                queue.append(neighbor)
                distance[neighbor] = distance[current] + 1

    return distance

第三步:调用

# 调用过程
g = Graph()
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(2, 3)
g.add_edge(3, 3)

start = 2
result = bfs(g.graph, start)
print(result)  # 输出:{0: 1, 1: 2, 2: 0, 3: 3}

完整代码及详细注释

from collections import deque

'''
在这个示例中,首先定义了一个Graph类,它有两个方法:__init__()和add_edge()
__init__()初始化了一个空字典graph,用于存储图的边。add_edge()方法接受两个参数:u和v,表示一条从顶点u到顶点v的边。当调用这个方法时,它会在图中添加这条边,并同时在u和v之间创建双向连接。

接下来,使用列表滚动数组来实现广度优先搜索算法。这个bfs()函数接受两个参数:graph和start。graph是一个Graph对象,表示要搜索的图;start是一个整数,表示搜索的起点。
    1.首先,初始化一个队列queue,并将起点start放入队列中。还创建了一个名为visited的集合,用于记录已经访问过的节点。此外,还创建了一个名为distance的字典,用于存储每个节点到起点的距离。
    2.接下来,进入一个while循环,只要队列不为空,就继续搜索。在每次迭代中,将队列中的第一个元素取出作为当前节点,并将其标记为已访问。
    3.然后,遍历当前节点的所有邻居。对于每一个未被访问的邻居节点,将它加入队列,并更新它的距离信息。
    4.当队列变为空时,说明已经搜索完了所有可达的节点。此时,可以返回distance字典,它包含了每个可达节点到起点的最短距离。
'''


class Graph:
    def __init__(self):
        self.graph = {}  # 初始化一个空字典来存储图的边

    def add_edge(self, u, v):  # 添加一条从顶点u到顶点v的边,并在u和v之间创建双向连接
        if u not in self.graph:  # 如果u不在图中,则创建一个新的列表来表示它的邻居节点
            self.graph[u] = []
        if v not in self.graph:  # 如果v不在图中,则创建一个新的列表来表示它的邻居节点
            self.graph[v] = []
        self.graph[u].append(v)  # 将v添加到u的邻居节点列表中
        self.graph[v].append(u)  # 将u添加到v的邻居节点列表中


def bfs(graph, start):
    queue = deque([start])  # 使用deque(双端队列)作为滚动数组来实现队列功能
    visited = set()  # 创建一个集合来记录已经访问过的节点
    distance = {start: 0}  # 创建一个字典来存储每个节点到起点的距离

    while queue:  # 只要队列不为空,就继续搜索
        current = queue.popleft()  # 从队列的第一个位置取出当前节点

        visited.add(current)  # 将当前节点标记为已访问

        for neighbor in graph[current]:  # 遍历当前节点的所有邻居
            if neighbor not in visited:  # 如果邻居节点未被访问过
                queue.append(neighbor)  # 将邻居节点加入队列
                distance[neighbor] = distance[current] + 1  # 更新邻居节点到起点的距离信息

    return distance  # 返回包含所有可达节点到起点距离的字典


# 示例:
g = Graph()
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(2, 3)
g.add_edge(3, 3)

start = 2
result = bfs(g.graph, start)
print(result)  # 输出:{0: 1, 1: 2, 2: 0, 3: 3}

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰灰老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值