当前位置: 首页 > news >正文

【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题:分割回文串
DFS+BFS

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))

相关文章:

  • V4L2杂谈
  • 【U-Boot】解决U-Boot的“Unknown command ‘help‘ - try ‘help‘”问题
  • 等保十问/等保相关基本问题解答
  • git日志规范
  • FastAPI + Vue3 对 SSE 通知机制的实现与进阶思考
  • Python----概率论与统计(贝叶斯,朴素贝叶斯 )
  • 青少年编程与数学 02-016 Python数据结构与算法 05课题、哈希表
  • 如何在Linux系统Docker部署Dashy并远程访问内网服务界面
  • ROS云课三分钟-差动移动机器人巡逻报告如何撰写-中等报告
  • 使用pip3安装PyTorch与PyG,实现NVIDIA CUDA GPU加速
  • LeetCode Hot100 刷题笔记(1)—— 哈希、双指针、滑动窗口
  • <component>用于挂载切换几个不同的组件
  • mobaxterm连接ssh提示sshpass: command not found
  • vmware、centos: 快照、redis集群克隆、虚拟机克隆后无法上网
  • 最简rnn_lstm模型python源码
  • 京存双活磁盘阵列助力国家交通指挥中心
  • Excel流式多线程带进度条功能的导入框架
  • 当开源邂逅AI,公益长出翅膀 | 回顾3.30 上海「开源×AI 赋能公益」Meetup
  • 如何更改wsl2中的ubuntu默认安装位置
  • 前端Js,检查一个字符串中是否出现高重复率
  • 滕州市建设局网站/微商软文范例
  • 桂林网站建设培训班/精准引流推广团队
  • 自助服务器网站建设/电商关键词排名优化怎么做?
  • 网站建设推广咨询平台/济南seo的排名优化
  • 乌海网站制作/cps推广平台
  • 网站loading动画/国内5大搜索引擎