目录
目录
滚动数组含义
在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}