问题描述
小C和他的领导小F计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:飞机只能飞往当前机场的相邻机场或相同航空公司管理的机场。为了减少起飞次数,小C需要制定最优的飞行路线。机场由一个数组airports标识,其中:
- 数组每个元素代表一个独特的机场,元素的值代表不同的航空公司。
airports[0]为起点,airports[airports.length - 1]为终点。- 假设小C当前在机场
i,那么i - 1和i + 1(如果存在)代表邻近机场,飞机可以直接前往。 - 如果在机场
i,且存在airports[i] == airports[j],则机场i和机场j同属一家航空公司,可直接飞往。
求最小起飞次数。
问题理解
需要找到从起点机场到终点机场的最小起飞次数。飞机可以飞往相邻的机场(即索引为i-1和i+1的机场),或者飞往同一家航空公司的其他机场。
数据结构选择
- 队列(Queue) :用于BFS(广度优先搜索),确保按层级遍历机场。
- 集合(Set) :用于记录已经访问过的机场,避免重复访问。
- 字典(Dictionary) :用于存储每个航空公司的机场索引列表,加快查找同一家航空公司的机场。
解法一:使用集合
python代码
from collections import deque
def solution(airports):
# 初始化队列和访问集合
queue = deque([(0, 0)]) # (当前机场索引, 起飞次数)
visited = set([0]) # 记录已经访问过的机场
while queue:
current_index, steps = queue.popleft()
# 如果当前机场是终点,返回起飞次数
if current_index == len(airports) - 1:
return steps
# 获取当前机场的航空公司
current_airline = airports[current_index]
# 检查相邻机场
for neighbor in [current_index - 1, current_index + 1]:
if 0 <= neighbor < len(airports) and neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, steps + 1))
# 检查同一家航空公司的机场
for i in range(len(airports)):
if airports[i] == current_airline and i not in visited:
visited.add(i)
queue.append((i, steps + 1))
return -1 # 如果没有找到路径,返回-1
if __name__ == "__main__":
# You can add more test cases here
print(solution([10,12,13,12,14]) == 3 )
print(solution([10,11,12,13,14]) == 4 )
可以进行哪些优化?
-
优化访问集合的使用:
- 当前代码中,每次检查相邻机场和同一家航空公司的机场时,都会将机场索引添加到
visited集合中。这可能会导致重复添加,影响性能。 - 可以考虑在将机场索引添加到队列时,再将其添加到
visited集合中,以避免重复添加。
- 当前代码中,每次检查相邻机场和同一家航空公司的机场时,都会将机场索引添加到
-
减少不必要的循环:
- 当前代码中,每次都会遍历整个
airports数组来查找同一家航空公司的机场。这会导致不必要的循环。 - 可以考虑使用字典来存储每个航空公司的机场索引列表,这样在查找同一家航空公司的机场时,可以直接从字典中获取,减少循环次数。
- 当前代码中,每次都会遍历整个
-
边界条件处理:
- 当前代码中,已经处理了边界条件(如
neighbor的索引范围),但可以进一步优化,确保在处理边界条件时不会出现意外的错误。
- 当前代码中,已经处理了边界条件(如
解法二:BFS遍历
算法步骤
-
初始化:
- 创建一个队列,并将起点机场(索引为0)和起飞次数(初始为0)放入队列。
- 创建一个集合,用于记录已经访问过的机场,并将起点机场加入集合。
- 创建一个字典,用于存储每个航空公司的机场索引列表。
-
BFS遍历:
- 从队列中取出当前机场索引和当前起飞次数。
- 如果当前机场是终点机场,返回当前起飞次数。
- 检查当前机场的相邻机场(
i-1和i+1),如果相邻机场未被访问过,将其加入队列和访问集合。 - 检查同一家航空公司的其他机场,如果这些机场未被访问过,将其加入队列和访问集合。
-
返回结果:
- 如果队列为空且未找到终点机场,返回-1表示无法到达终点。
python代码实现
from collections import deque, defaultdict
def solution(airports):
# 初始化队列和访问集合
queue = deque([(0, 0)]) # (当前机场索引, 起飞次数)
visited = set([0]) # 记录已经访问过的机场
# 使用字典存储每个航空公司的机场索引列表
airline_dict = defaultdict(list)
for i, airline in enumerate(airports):
airline_dict[airline].append(i)
while queue:
current_index, steps = queue.popleft()
# 如果当前机场是终点,返回起飞次数
if current_index == len(airports) - 1:
return steps
# 获取当前机场的航空公司
current_airline = airports[current_index]
# 检查相邻机场
for neighbor in [current_index - 1, current_index + 1]:
if 0 <= neighbor < len(airports) and neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, steps + 1))
# 检查同一家航空公司的机场
for i in airline_dict[current_airline]:
if i not in visited:
visited.add(i)
queue.append((i, steps + 1))
return -1 # 如果没有找到路径,返回-1
代码解释
- 字典的使用:通过使用
defaultdict来存储每个航空公司的机场索引列表,可以减少在查找同一家航空公司的机场时的循环次数。 - 访问集合的优化:在将机场索引添加到队列时,再将其添加到
visited集合中,避免重复添加。 - 减少不必要的条件检查:在检查相邻机场时,提前判断是否已经到达终点,从而减少不必要的条件检查。
复杂度分析
时间复杂度
-
初始化部分:
- 初始化队列和访问集合:O(1)
- 构建航空公司字典:O(n),其中n是机场的数量。
-
BFS遍历部分:
- 每个机场最多被访问一次,因此BFS遍历的时间复杂度是O(n)。
- 对于每个机场,我们需要检查其相邻机场和同一家航空公司的机场。相邻机场最多有2个,同一家航空公司的机场最多有n个(极端情况下所有机场都属于同一家航空公司)。
- 因此,每次检查的时间复杂度是O(n)。
综合来看,BFS遍历的总时间复杂度是O(n^2)。
空间复杂度
-
队列:
- 在最坏情况下,队列中最多存储n个机场索引,因此空间复杂度是O(n)。
-
访问集合:
- 访问集合中最多存储n个机场索引,因此空间复杂度是O(n)。
-
航空公司字典:
- 航空公司字典中最多存储n个机场索引,因此空间复杂度是O(n)。
综合来看,总的空间复杂度是O(n)。
总结
通过使用BFS算法,结合字典和集合来优化查找和避免重复访问,可以有效地找到从起点机场到终点机场的最小起飞次数。