【Python算法】基础语法、算法技巧模板、二分、DFS与BFS、并查集
建议全部跟着手敲一遍!!!
import sys
from math import gcd
from bisect import bisect_left
from collections import defaultdict, deque
import heapq
from itertools import permutations, combinations
from functools import lru_cache
from typing import List
# ===================== 输入输出优化 =====================
# 重新定义输入函数,使用sys.stdin.readline().strip()实现快速输入
# 这是因为sys.stdin.readline()比普通的input()函数读取速度更快
input = lambda: sys.stdin.readline().strip()
# 示例:读取n和n个数字
def read_data():
# 读取一行输入,并按空格分割成字符串列表
data = input().split()
# 将列表的第一个元素转换为整数,作为数字的数量n
n = int(data[0])
# 将列表中从第二个元素开始的n个元素转换为整数,并存储在列表nums中
nums = list(map(int, data[1:n+1]))
return n, nums
# ===================== 格式化输出 =====================
# 定义圆周率π的值
pi = 3.1415926
# 使用f-string格式化输出,保留两位小数
print(f"{pi:.2f}") # 保留两位小数 → 3.14
# 使用f-string格式化输出,将整数5补零到3位
print(f"{5:03d}") # 补零到3位 → 005
# ===================== 数据结构技巧 =====================
# 列表初始化
# 创建一个长度为100的一维数组,所有元素初始化为0
arr = [0] * 100
# 正确创建二维数组(3行5列)
# 使用列表推导式创建一个3行5列的二维数组,所有元素初始化为0
matrix = [[0] * 5 for _ in range(3)]
# 字典默认值处理
# 创建一个默认值为0的字典
# 当访问字典中不存在的键时,会自动创建该键并将其值初始化为0
d = defaultdict(int)
# 对字典中键为"key"的值加1,无需判断key是否存在
d["key"] += 1
# 字典排序(按值降序)
# 对字典d的键值对进行排序,按照值的降序排列
# 使用lambda函数指定排序的键为值的相反数,实现降序排序
sorted_dict = dict(sorted(d.items(), key=lambda x: -x[1]))
# 双端队列(BFS常用)
# 创建一个双端队列对象
q = deque()
# 在队尾插入元素1
q.append(1)
# 从队首移除并返回元素
q.popleft()
# ===================== 排序与查找算法 =====================
# 快速排序(分治思想)
def quick_sort(arr):
# 如果数组长度小于等于1,直接返回该数组
if len(arr) <= 1:
return arr
# 选择中间元素作为基准
pivot = arr[len(arr)//2]
# 小于基准的元素组成的列表
left = [x for x in arr if x < pivot]
# 等于基准的元素组成的列表
middle = [x for x in arr if x == pivot]
# 大于基准的元素组成的列表
right = [x for x in arr if x > pivot]
# 递归地对左右子数组进行排序,并合并结果
return quick_sort(left) + middle + quick_sort(right)
# 二分查找左边界(寻找第一个≥target的位置)
def bisect_left_custom(arr, target):
# 左指针初始化为0
l, r = 0, len(arr)
# 当左指针小于右指针时,继续循环
while l < r:
# 计算中间位置
mid = (l + r) // 2
# 如果中间元素小于目标值,将左指针移动到mid+1
if arr[mid] < target:
l = mid + 1
# 否则,将右指针移动到mid
else:
r = mid
# 返回插入位置
return l
# ===================== 数学与数论 =====================
# 埃拉托斯特尼筛法(素数筛选)
def sieve(n):
# 创建一个长度为n+1的布尔数组,初始值都为True,表示所有数都是素数
is_prime = [True] * (n+1)
# 0和1不是素数,将其标记为False
is_prime[0] = is_prime[1] = False
# 从2开始遍历到根号n
for i in range(2, int(n**0.5)+1):
# 如果i是素数
if is_prime[i]:
# 标记i的倍数为非素数
for j in range(i*i, n+1, i):
is_prime[j] = False
# 返回所有标记为True的数,即素数
return [i for i, prime in enumerate(is_prime) if prime]
# 最大公约数与最小公倍数
# 计算12和18的最大公约数
gcd_val = gcd(12, 18) # 6
# 根据公式计算最小公倍数
lcm_val = 12 * 18 // gcd_val # 36
# 组合数计算(模运算优化)
# 定义模数
MOD = 10**9 + 7
def comb(n, k):
# 初始化结果为1
res = 1
# 从1到k进行循环
for i in range(1, k+1):
# 根据递推公式计算组合数
res = res * (n - k + i) // i # 递推公式避免浮点
# 返回结果对MOD取模的值
return res % MOD
# ===================== 图论算法 =====================
# 邻接表建图(无向图)
# 创建一个默认值为列表的字典,用于存储图的邻接表
graph = defaultdict(list)
# 定义图的边
edges = [[1,2], [2,3], [1,3]]
# 遍历每条边
for u, v in edges:
# 将v添加到u的邻接表中
graph[u].append(v)
# 将u添加到v的邻接表中,因为是无向图
graph[v].append(u)
# Dijkstra最短路径(堆优化)
def dijkstra(graph, start):
# 创建一个最小堆,初始元素为(0, start),表示从起点到起点的距离为0
heap = [(0, start)]
# 初始化距离字典,起点到自身的距离为0
dist = {start: 0}
# 当堆不为空时,继续循环
while heap:
# 从堆中取出距离最小的节点及其距离
d, u = heapq.heappop(heap)
# 如果取出的距离大于当前记录的最短距离,跳过该节点
if d > dist.get(u, float('inf')):
continue
# 遍历该节点的所有邻居节点
for v, w in graph[u]:
# 如果通过当前节点到达邻居节点的距离更短
if dist.get(v, float('inf')) > d + w:
# 更新邻居节点的最短距离
dist[v] = d + w
# 将邻居节点及其最短距离加入堆中
heapq.heappush(heap, (dist[v], v))
# 返回距离字典
return dist
# ===================== 动态规划 =====================
# 01背包问题(空间优化版)
def knapsack(weights, values, capacity):
# 创建一个长度为capacity+1的数组,用于存储最大价值
dp = [0] * (capacity + 1)
# 遍历每个物品
for w, v in zip(weights, values):
# 逆序更新dp数组,避免重复选取物品
for j in range(capacity, w-1, -1):
# 选择放入或不放入当前物品的最大价值
dp[j] = max(dp[j], dp[j - w] + v)
# 返回背包容量为capacity时的最大价值
return dp[capacity]
# 最长上升子序列(贪心+二分)
def lengthOfLIS(nums):
# 创建一个空列表,用于存储上升子序列的末尾元素
tails = []
# 遍历数组中的每个元素
for num in nums:
# 找到元素num在tails列表中应该插入的位置
idx = bisect_left(tails, num)
# 如果插入位置等于tails列表的长度,说明num比tails中的所有元素都大
if idx == len(tails):
# 将num添加到tails列表的末尾
tails.append(num)
else:
# 否则,将tails列表中该位置的元素替换为num
tails[idx] = num
# 返回上升子序列的长度
return len(tails)
# ===================== 回溯算法 =====================
# 全排列生成
def permute(nums):
def backtrack(path):
# 如果当前路径的长度等于数组的长度,说明已经生成了一个全排列
if len(path) == len(nums):
# 将当前路径的副本添加到结果列表中
res.append(path.copy())
return
# 遍历数组中的每个元素
for num in nums:
# 如果元素不在当前路径中
if num not in path:
# 将元素添加到当前路径中
path.append(num)
# 递归调用backtrack函数,继续生成排列
backtrack(path)
# 回溯,移除最后一个元素
path.pop()
# 初始化结果列表
res = []
# 调用backtrack函数,从空路径开始生成排列
backtrack([])
# 返回结果列表
return res
# ===================== 前缀和与差分 =====================
# 前缀和数组
# 初始化前缀和数组,第一个元素为0
prefix = [0]
# 遍历数组[1, 2, 3, 4]
for num in [1, 2, 3, 4]:
# 计算前缀和,将前一个前缀和加上当前元素的值
prefix.append(prefix[-1] + num) # prefix = [0,1,3,6,10]
# 差分数组(区间更新)
# 创建一个长度为5的差分数组,所有元素初始化为0
diff = [0] * 5
# 差分数组的第一个元素设为1
diff[0] = 1
# 示例差分,计算差分数组的其他元素
for i in range(1, 5):
diff[i] = (i+1) - i # 示例差分
# ===================== 其他实用技巧 =====================
# 排列组合生成
# 生成[1,2,3]中取2个元素的所有排列,并打印结果
print(list(permutations([1,2,3], 2))) # 排列
# 生成[1,2,3]中取2个元素的所有组合,并打印结果
print(list(combinations([1,2,3], 2))) # 组合
# 堆操作(默认小顶堆)
# 创建一个空堆
heap = []
# 向堆中插入元素2
heapq.heappush(heap, 2)
# 向堆中插入元素1
heapq.heappush(heap, 1)
# 从堆中取出最小的元素并打印
print(heapq.heappop(heap)) # 1
# 大顶堆技巧(存入负数)
# 向堆中插入元素-3
heapq.heappush(heap, -3)
# 从堆中取出最大的元素(因为存入的是负数,所以取反)
max_val = -heapq.heappop(heap)
# 位运算技巧
# 定义一个二进制数
n = 0b1010
# 清除最低位的1,并打印结果
print(n & (n-1)) # 清除最低位的1 → 0b1000
# 计算2的3次方,并打印结果
print(1 << 3) # 计算2^3 → 8
# 判断5的奇偶性,奇数返回0,偶数返回1
print(~5 & 1) # 判断奇偶(5是奇→0,偶→1)
# 记忆化搜索(斐波那契数列)
# 使用lru_cache装饰器实现记忆化搜索
@lru_cache(maxsize=None)
def fib(n):
# 如果n小于2,返回n
if n < 2:
return n
# 递归计算斐波那契数列的值
return fib(n-1) + fib(n-2)
# ===================== 高频考题模板 =====================
# 和为K的子数组个数(前缀和+哈希)
def subarraySum(nums: List[int], k: int) -> int:
# 创建一个默认值为0的字典,用于存储前缀和及其出现的次数
prefix_sum = defaultdict(int)
# 前缀和为0的情况出现1次
prefix_sum[0] = 1
# 初始化当前前缀和为0
current_sum = count = 0
# 遍历数组中的每个元素
for num in nums:
# 更新当前前缀和
current_sum += num
# 计算需要的前缀和
count += prefix_sum.get(current_sum - k, 0)
# 更新当前前缀和的出现次数
prefix_sum[current_sum] += 1
# 返回和为k的子数组的个数
return count
# 滑动窗口模板
def sliding_window(nums, k):
# 左指针初始化为0
left = 0
# 右指针从0开始遍历数组
for right in range(len(nums)):
# 当窗口大小超过k时,收缩窗口
while right - left + 1 > k:
left += 1
# 处理当前窗口
print(nums[left:right+1])
BFS
下面的解答都是非力扣式,而是自行写输入输出
在一个二维网格中找到由 1 组成的最大连通区域的面积。
求岛屿的最大面积问题:
from collections import deque
grid = []
m,n = map(int,input().split())
for _ in range(m):
row = list(map(int, input().split()))
grid.append(row) # 输入m行n列
m = len(grid)
n = len(grid[0])
direc = [(0,1),(0,-1),(-1,0),(1,0)]
def bfs(i,j):
ans=1
q = deque([(i,j)]) # 入队
grid[i][j]=0 # 标记为已经访问过了
while q:
x,y =q.popleft()
for dx,dy in direc:
nx = x+dx
ny = y+dy
if 0<=nx<m and 0<=ny<n and grid[nx][ny]:
q.append((nx,ny))
grid[nx][ny]=0 # 添加进队列后都要标记为已经访问过了
ans+=1
return ans
res=0
for i,row in enumerate(grid):
for j,x in enumerate(row):
if x==1:
res = max(res,bfs(i,j))
print(res)
力扣1926题
from collections import deque
grid = []
# 输入行数和列数
m, n = map(int, input().split())
# 逐行输入网格数据
for _ in range(m):
row = list(map(str, input().split()))
grid.append(row)
# 输入入口坐标
i, j = map(int, input().split())
direc = [(0, 1), (0, -1), (-1, 0), (1, 0)]
ans = -1 # 初始化结果为 -1,表示未找到出口
q = deque([(i, j, 0)])
grid[i][j] = '+' # 入队后标记为已经访问过了
while q:
x, y, steps = q.popleft()
# 检查是否到达边界且不是入口
if (x == 0 or x == m - 1 or y == 0 or y == n - 1) and (x, y) != (i, j):
ans = steps
break
# 遍历四个方向
for dx, dy in direc:
nx, ny = x + dx, y + dy
# 检查新坐标是否合法且未访问过
if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] == '.':
q.append((nx, ny, steps + 1))
grid[nx][ny] = '+'
print(ans)
力扣1091题:二进制矩阵中的最短路径
from collections import deque
def shortest_path_binary_matrix():
"""
从标准输入读取二进制矩阵并寻找最短路径
输入格式:
第一行:n(矩阵大小)
接下来n行:每行n个数字(0或1),空格分隔
"""
# 读取输入
n = int(input())
grid = []
for _ in range(n):
row = list(map(int, input().split()))
grid.append(row)
# 检查起点和终点
if grid[0][0] != 0 or grid[-1][-1] != 0:
print(-1)
return
# 8个移动方向
directions = [(-1,-1), (-1,0), (-1,1),
(0,-1), (0,1),
(1,-1), (1,0), (1,1)]
queue = deque([(0, 0, 1)])
grid[0][0] = 1 # 标记起点
while queue:
x, y, steps = queue.popleft()
if x == n-1 and y == n-1:
print(steps)
return
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] == 0:
queue.append((nx, ny, steps + 1))
grid[nx][ny] = 1
print(-1)
# 执行函数
shortest_path_binary_matrix()
关于多源BFS
常规的单源BFS需要对每个陆地单元格单独计算到最近水域的距离,时间复杂度为O(m²n²)。
而多源BFS可以同时从所有水域出发,一次性计算所有陆地的高度,时间复杂度优化为O(mn)。
力扣1162题:地图分析
将所有陆地单元格(1)作为起点同时开始BFS
,而不是对每个海洋单元格单独BFS,避免了重复计算,时间复杂度从O(n⁴)降低到O(n²)
from collections import deque
def max_distance():
"""
计算海洋单元格到最近陆地单元格的最大距离
输入格式:
第一行:n(矩阵大小)
接下来n行:每行n个数字(0或1),空格分隔
0表示海洋,1表示陆地
返回:
海洋到陆地的最大距离,如果全陆地或全海洋则返回-1
"""
# 读取输入
n = int(input())
grid = []
for _ in range(n):
row = list(map(int, input().split()))
grid.append(row)
q = deque()
# 将所有陆地(1)的位置加入队列,初始步数为0
for i in range(n):
for j in range(n):
if grid[i][j] == 1:
q.append((i, j, 0))
# 检查全陆地或全海洋的情况
if not q or len(q) == n * n:
print(-1)
return
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
max_distance = 0
while q:
x, y, steps = q.popleft()
max_distance = max(max_distance, steps)
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] == 0:
grid[nx][ny] = 1 # 标记为已访问
q.append((nx, ny, steps + 1))
print(max_distance if max_distance != 0 else -1)
# 调用函数
max_distance()
力扣1765题:地图中的最高点
陆地单元格:高度等于到最近水域的距离
from collections import deque
def calculate_water_heights():
"""
计算水域周围单元格的高度
输入格式:
第一行:m n(矩阵行数和列数)
接下来m行:每行n个数字(0或1),空格分隔
1表示水域(高度为0),0表示陆地(需要计算高度)
返回:
高度矩阵,水域周围单元格的高度为1,依此类推
"""
# 读取输入
m, n = map(int, input().split())
isWater = []
for _ in range(m):
row = list(map(int, input().split()))
isWater.append(row)
# 初始化结果矩阵
heights = [[0] * n for _ in range(m)]
q = deque()
# 将所有水域位置加入队列,初始高度为0
for i in range(m):
for j in range(n):
if isWater[i][j] == 1:
q.append((i, j, 0))
# 定义四个移动方向
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
# 多源BFS计算高度
while q:
x, y, h = q.popleft()
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 检查边界和是否为未处理的陆地
if 0 <= nx < m and 0 <= ny < n and isWater[nx][ny] == 0:
heights[nx][ny] = h + 1
q.append((nx, ny, h + 1))
isWater[nx][ny] = 1 # 标记为已处理
# 输出结果
for row in heights:
print(' '.join(map(str, row)))
# 调用函数
calculate_water_heights()
力扣994题
from collections import deque
import sys
# 输入行数和列数
m, n = map(int, input().split())
grid = []
fresh = 0
q = deque()
# 读取网格并初始化
for i in range(m):
row = list(map(int, input().split()))
grid.append(row)
for j in range(n):
if grid[i][j] == 1:
fresh += 1
elif grid[i][j] == 2:
q.append((i, j, 0))
# 特殊情况处理:如果没有新鲜橘子,直接返回0
if fresh == 0:
print(0)
sys.exit()
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
minutes = 0
# BFS处理腐烂过程
while q:
x, y, time = q.popleft()
minutes = time
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] == 1:
grid[nx][ny] = 2
fresh -= 1
q.append((nx, ny, time + 1))
# 输出结果
print(minutes if fresh == 0 else -1)
力扣934题:最短的桥
DFS+BFS:
解决最短桥问题的完整方案
算法步骤:
1. 使用DFS找到并标记第一座岛屿(标记为2)
2. 使用多源BFS从第一座岛屿扩展,寻找第二座岛屿
3. 当BFS遇到第二座岛屿时,返回当前扩展的步数
from collections import deque
# 读取网格大小
n = int(input())
# 读取网格数据,转换为二维整数列表
grid = [list(map(int, input().split())) for _ in range(n)]
# 定义四个移动方向:右、左、下、上
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
# 初始化BFS队列
q = deque()
# ========== 第一阶段:标记第一座岛屿 ==========
found = False # 标记是否已找到第一座岛屿
# 遍历整个网格寻找第一座岛屿的起点
for i in range(n):
if found:
break
for j in range(n):
# 当找到第一个陆地单元格(值为1)
if grid[i][j] == 1:
# 使用栈实现迭代式DFS(避免递归深度限制)
stack = [(i, j)]
# 将起始点标记为2(表示属于第一座岛屿)
grid[i][j] = 2
# 开始DFS遍历
while stack:
x, y = stack.pop()
# 将当前岛屿点加入BFS队列,初始步数为0
q.append((x, y, 0))
# 检查四个方向
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 确保新坐标在网格内且是未访问的陆地(值为1)
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] == 1:
# 标记为属于第一座岛屿
grid[nx][ny] = 2
# 加入栈中继续DFS
stack.append((nx, ny))
# 标记已找到第一座岛屿,跳出循环
found = True
break
# ========== 第二阶段:多源BFS寻找第二座岛屿 ==========
while q:
x, y, steps = q.popleft() # 取出队列中的点和当前步数
# 检查四个方向
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 确保新坐标在网格内
if 0 <= nx < n and 0 <= ny < n:
# 如果遇到值为1的单元格,说明找到第二座岛屿
if grid[nx][ny] == 1:
# 输出当前步数(即需要翻转的最少水域数)
print(steps)
# 直接退出程序
exit()
# 如果是水域(值为0)
if grid[nx][ny] == 0:
# 标记为已访问(-1表示已探索的水域)
grid[nx][ny] = -1
# 加入队列,步数+1
q.append((nx, ny, steps + 1))
print(-1)
DFS
力扣086题:分割回文串
s = input().strip() # 读取输入并去除首尾空格
res = []
path = []
def dfs(start):
"""使用深度优先搜索查找所有回文分割方案
Args:
start: 当前搜索的起始位置
"""
if start == len(s):
res.append(path.copy())
return
for end in range(start + 1, len(s) + 1):
substring = s[start:end]
if substring == substring[::-1]: # 判断是否为回文
path.append(substring)
dfs(end)
path.pop() # 回溯
dfs(0) # 从字符串起始位置开始搜索
print(res)
力扣395题
要求找到最长的子串,其中每个字符出现的次数都不少于k次。
遍历当前子串,查找第一个出现次数 <k 的字符
如果找到,将字符串分割为两部分递归处理:
左部分:dfs(start, i-1)
右部分:dfs(i+1, end)
如果当前子串所有字符出现次数都 ≥k,返回子串长度
如果 end 超出字符串范围,返回0
闭区间写法:(分治递归)
from collections import Counter
def longest_substring_with_k_repeats():
"""
查找包含至少k次重复字符的最长子串
输入格式:
第一行:字符串s
第二行:整数k
输出:
满足条件的最长子串长度
"""
s = input().strip()
k = int(input())
def dfs(start, end):
# 边界条件处理
if end >= len(s):
return 0
# 统计当前子串字符频率
freq = Counter(s[start:end+1])
# 检查是否有不满足条件的字符
for i in range(start, end+1):
if freq[s[i]] < k:
# 分割字符串并递归处理
return max(dfs(start, i-1), dfs(i+1, end))
# 整个子串都满足条件
return end - start + 1
if len(s) < k:
print(0)
else:
print(dfs(0, len(s)-1))
# 调用函数
longest_substring_with_k_repeats()
二分
力扣793题:阶乘后函数K个零
由于阶乘末尾零的数量随着数字的增加而单调递增,我们可以利用二分查找来高效地定位这些数字。
尾随的零 ≤k 的那个最大的数
使用下面的二分模板:
while left <= right:
mid = (left + right) // 2
if count_trailing_zeros(mid) <= k:
left = mid + 1 # 保持"left左侧都满足"
else:
right = mid - 1 # 保持"right右侧都不满足"
# 结束时 right == 最后一个满足条件的值,即最大满足条件的数
upper 定位到 k 个零的区间的右端点。
lower 定位到 k-1 个零的区间的右端点。
两者的差值 upper - lower 直接给出了恰好有 k 个零的数字数量。
完整代码:
def count_trailing_zeros(x):
"""计算x!末尾有多少个零"""
zeros = 0
while x > 0:
x = x // 5
zeros += x
return zeros
def find_max_num_with_k_zeros(k):
"""找到最大的n使得n!的尾随零<=k"""
left, right = 0, 5 * (k + 1) # 足够大的上界
while left <= right:
mid = (left + right) // 2 # 注意这里不需要+1
zeros = count_trailing_zeros(mid)
if zeros <= k:
left = mid + 1 # 尝试更大的数
else:
right = mid - 1 # 尝试更小的数
return right # 注意返回的是right而不是left
# 输入处理
k = int(input())
# 计算恰好有k个零的数字数量
if k == 0:
print(0)
else:
upper = find_max_num_with_k_zeros(k)
lower = find_max_num_with_k_zeros(k - 1)
print(upper - lower)
并查集
力扣1998题
如果 gcd(nums[i], nums[j]) > 1 ,交换 nums[i] 和 nums[j] 的位置。这意味着 nums[i] 和 nums[j] 至少共享一个质因数。如果两个数共享一个质因数,那么它们可以通过这个质因数“连接”起来。如果两个数没有共享质因数,则无法交换。
判断是否可以通过交换数组中任意两个元素的位置(但交换必须满足两个元素有公共质因子),使得数组最终能按非递减顺序排列。
质因数分解:对每个数分解质因数,并将所有质因数合并到同一个集合中。例如,数字12的质因数是2和3,那么2和3会被合并到同一个集合。
如果两个数有公共质因子,则它们可以交换位置。并查集可以高效管理这种多个数之间能交换这种间接关系。
将原数组排序后,检查每个位置上的数是否与原数组对应位置的数属于同一个并查集。如果是,则可以交换到正确位置;否则无法满足排序要求。
注:每个数都可以唯一分解为质因数的乘积。
from math import sqrt
import sys
nums = list(map(int,input().split()))
n = len(nums)
max_num = max(nums)
pa = list(range(max_num +1))
def find(x):
if pa[x]!=x:
pa[x] = find(pa[x])
return pa[x]
def union(u,v):
if find(u)!=find(v):
pa[find(u)]=find(v)
for num in nums:
#寻找质因数
x=num
for j in range(2,int(sqrt(x))+1):
if x%j==0:
union(j,num)
while x%j==0:
x//=j
if x>1:
union(x,num)
sorted_nums = sorted(nums)
for u,v in zip(nums,sorted_nums):
if u==v: # 没有发生交换
continue
if find(u)!=find(v):
print("False")
sys.exit()
print("True")
Dijkstra
问题描述
给定一个带权无向图(节点编号从1开始),求从起点 S 到终点 T 的最短路径。如果路径不存在,输出 -1。
输入格式:
第一行:两个整数 n(节点数)和 m(边数)。
接下来 m 行:每行三个整数 u, v, w,表示节点 u 和 v 之间有一条权值为 w 的边。
最后一行:两个整数 S 和 T,表示起点和终点。
输出格式:
一个整数,表示最短路径长度。
示例输入
4 4
1 2 2
1 3 1
2 4 3
3 4 1
1 4
示例输出
3
Dijkstra 算法的核心思想是贪心策略:每次从未确定最短路径的节点中,选择当前距离起点最近的节点,并基于它更新邻居节点的距离
。
import heapq
def dijkstra(n, edges, start, end):
# 构建邻接表
graph = {i: [] for i in range(1, n+1)}
for u, v, w in edges:
graph[u].append((v, w))
graph[v].append((u, w)) # 无向图需双向添加
# 初始化堆和距离字典
heap = [(0, start)]
dist = {i: float('inf') for i in range(1, n+1)} #初始化为无穷大
dist[start] = 0 #记录到起点的最短距离
while heap:
d, u = heapq.heappop(heap)
if u == end: # 提前终止:已找到终点
return d
if d > dist[u]: # 跳过堆中同一节点的旧记录,确保每个节点只被处理一次(以最小距离为准)
continue
for v, w in graph[u]: #松弛邻居节点
if dist[v] > d + w: #比原来短就更新
dist[v] = d + w
heapq.heappush(heap, (dist[v], v)) # 更新后的节点入堆
return -1 # 不可达
# 输入处理
n, m = map(int, input().split())
edges = [tuple(map(int, input().split())) for _ in range(m)]
S, T = map(int, input().split())
# 调用Dijkstra并输出结果
print(dijkstra(n, edges, S, T))