排序
冒泡排序: O(n^2)
def bubble_sort(arr):
n = len(arr)
# 外层循环控制总共需要进行多少轮冒泡
for i in range(n):
# 内层循环控制每一轮冒泡的比较和交换
for j in range(0, n - i - 1):
# 比较相邻的两个元素,如果前面的大于后面的,则交换它们的位置
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
# 测试
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = bubble_sort(arr)
print(sorted_arr)
插入排序: O(n^2)
打扑克
def insertion_sort(arr):
# 从第二个元素开始进行插入排序
for i in range(1, len(arr)):
key = arr[i] # 当前需要插入的元素
j = i - 1 # 已排序区域的最后一个元素的索引
# 将当前元素与已排序区域中的元素从右往左比较,找到合适的插入位置
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j] # 将大于当前元素的元素向右移动一个位置
j -= 1
arr[j + 1] = key # 将当前元素插入到合适的位置
return arr
# 测试
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = insertion_sort(arr)
print(sorted_arr)
快速排序:复杂度为 O(nlog n)--> 分治递归
- 挑选基准值:从数列中挑出一个元素,称为"基准"(pivot);
- 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;
- 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0] # 选择第一个元素作为基准
left = [x for x in arr[1:] if x <= pivot] # 所有小于等于基准的元素
right = [x for x in arr[1:] if x > pivot] # 所有大于基准的元素
return quick_sort(left) + [pivot] + quick_sort(right)
# 测试
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = quick_sort(arr)
print(sorted_arr)
归并排序:O(nlog n)--> 分治递归
- :归并排序的核心思想是分治;又分为自上而下的递归以及自下而上的迭代(分割+集成)
def merge_sort(arr):
if len(arr) <= 1:
return arr
# 将数组一分为二
mid = len(arr) // 2
left = arr[:mid]
right = arr[mid:]
# 递归地对左右两个子数组进行归并排序
left = merge_sort(left)
right = merge_sort(right)
# 合并左右两个已排序的子数组
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
# 合并两个子数组,直到其中一个子数组的元素全部添加到结果数组中
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# 将剩余的元素添加到结果数组中
result.extend(left[i:])
result.extend(right[j:])
return result
# 测试
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = merge_sort(arr)
print(sorted_arr)
数组合并:给定两个按升序排列的有序数组,将它们合并成一个新的有序数组。例如:a = [1,2,6,8], b = [2,4,7,10],输出为 arr = [1,2,2,4,6,7,8,10]
def merge_sorted_array(a,b):
c = []
i,j = 0,0
while True:
if i==len(a):
c.extend(b[j:])
return(c)
elif j==len(b):
c.extend(a[i:])
return(c)
else:
if a[i]<b[j]:
c.append(a[i])
i=i+1
else:
c.append(b[j])
j=j+1
print(merge_sorted_array([1,2,6,8],[2,4,7,10]))
排序优先级
给你一个日志数组 logs。每条日志都是以空格分隔的字串,其第一个字为字母与数字混合的标识符。
有两种不同类型的日志:
- 字母日志:除标识符之外,所有字均由小写字母组成
- 数字日志:除标识符之外,所有字均由数字组成
请按下述规则将日志重新排序:
- 所有 字母日志 都排在 数字日志 之前。
- 字母日志 在内容不同时,忽略标识符后,按内容字母顺序排序;在内容相同时,按标识符排序。
- 数字日志 应该保留原来的相对顺序。
返回日志的最终顺序。
示例 1:
1: logs = ["dig1 8 1 5 1", "let1 art can","dig2 3 6", "let2 own kit dig", "let3 art zero"]
FI: ["leti art can","let3 art zero","let2 own kit dig","dig1 8 1 5 1", "dig2 3 6"]
解释:
字母日志的内容都不同,所以顺序为“art can","art zero","own kit dig"。
数字日志保留原来的相对顺序, "dig1 8 1 5 1", "dig2 3 6" •
示例 2:
输入 = ["a1 9 2 3 1", "g1 act car","z04 4 7","abl off key dog", "a8 act zoo"]
输出: ["g1 act car", "a8 act zoo", "abl off key dog", "a1 9 2 3 1", "z04 4 7"]
def split(logs):
letter_logs = []
digit_logs = []
for log in logs:
if log.split()[1].isdigit():
digit_logs.append(log)
else:
letter_logs.append(log)
letter_logs = reorder_letter(letter_logs)
new_log = letter_logs + digit_logs
print(new_log)
def reorder_letter(letter_logs):
# 先按照word[1:]排序,对于相同的word[1:], 再按照word[0]进行排序
def let_sort(log):
words = log.split()
return (words[1:], words[0])
letter_logs.sort(key=let_sort)
return letter_logs
logs = ["dig1 8 1 5 1","let1 art can","dig2 3 6","let2 own kit dig","let3 art zero"]
logs2 = ["a1 9 2 3 1","g1 act car","zo4 4 7","ab1 off key dog","a8 act zoo"]
split(logs)
split(logs2)
几乎排序(G)
当一个数组只有最多一个元素被删除后,就能成为完美升序排序的时候,这个数组叫做“几乎排序”。例如,[2,1,7], [13], [9,2]都是几乎排序,因为他们有0或1个元素没有在正确的位置上。[4,2,1], [1,2,6,4,3]不是因为他们多于一个元素在正确的位置上。假设一个数组有n个特殊整数,求最少需要删除多少个数字就能够将数组变为“几乎排序”。
from bisect import bisect_left
def lis_length(arr):
lis = []
for num in arr:
pos = bisect_left(lis, num)
if pos ‹ len(lis):
lis[pos] = num
else:
lis. append (num)
return len(lis)
def minDeletions(arr):
if len(arr) == 1:
return 0
n = len (arr)
lis_len = lis_length(arr)
if lis_len == len(arr):
return 0
return n - lis_len - 1
循环
合并三个相同相邻数字
def merge_triplets(lst):
i = 0
while i < len(lst) - 2:
if lst[i] == lst[i + 1] == lst[i + 2]:
# 合并连续三个相同数字
lst.pop(i + 2)
lst.pop(i + 1)
lst[i] *= 3
# 回退一个位置,以检查新合并的数字是否能够再次合并
i = max(0, i - 1)
else:
i += 1
return lst
# 示例
input_list = [1, 6, 2, 2, 2, 6, 9, 3, 3, 3, 9, 5, 5, 5, 6]
result = merge_triplets(input_list)
print(result)
将excel的列名,转换成0开始的整数序号
例如A->0,B->1,...,Z->25, AA->26, AB->27,...AZ->51,BA->52,...ZZ->701,AAA->702,...ARQ->1160
def toNumber(str):
result = 0
for char in str:
result *= 26
result += (ord(char) - ord('A') + 1)
return result - 1
网格连接
现在有一个矩阵,其中如果坐标值为1,代表为节点,否则为0。 其中,每层节点都能与下层至少一个节点相连(最后一层除外);集群中的每个节点有一个与之关联的层。位于网格第i行的节点是第i级节点。第i层的每个节点都连接到第k层的所有节点,其中k > i,k是包含至少1个节点。当到达网格的最后一层时,就不可能再连接了。现在求这些连接层有多少个节点之间的连接。
例:[(1 1 1)(0,1,0)(0,0,0),[1 1 0]]总共有3 + 2 = 5个连接。
def count_connections(matrix):
if not matrix:
return 0
rows = len(matrix)
cols = len(matrix[0])
# 记录每层的节点数
node_counts = [0] * rows
# 计算每层的节点数
for i in range(rows):
node_counts[i] = sum(matrix[i])
node_counts = list(filter(lambda x: x != 0, node_counts))
print(node_counts)
# 从底向上遍历计算连接数
total_connections = 0
for i in range(0, len(node_counts)-1):
total_connections += node_counts[i] * node_counts[i + 1]
return total_connections
# 示例矩阵
matrix = [[1, 1, 1], [0, 1, 0], [0, 0, 0], [1, 1, 0]]
# 计算连接数
print(count_connections(matrix)) # 输出 5
比较不一样的元素数量
比较两个列表中不一样的元素数量(如果一个不同的元素出现多次同样计算) 例:
Input:
11
1 1 2 3 4 5 5 7 6 9 10
10
11 12 13 4 5 6 7 18 19 20
output:
12
def countOfElement(listInput1, listInput2):
set1 = set(listInput1)
set2 = set(listInput2)
diff = set1.symmetric_difference(set2)
count_dict = {}
for i in diff:
count_dict[i] = 0
for j in listInput1:
if j in count_dict:
count_dict[j] += 1
for p in listInput2:
if p in count_dict:
count_dict[p] -= 1
count = sum(abs(count) for count in count_dict.values())
return count
连续相同元素
一台数字机器生成二进制数据,该数据由一串0和1的字符组成。数据中的最大信号M指的是连续出现的1或0的最大数量(然而,M不能出现在字符串的开头或结尾)。 设计一种方法来找到最大信号的长度。
def maxSignal(strInput):
# Write your code here
clean_str = ""
chk = 0
i = 0
if '1' not in strInput:
return 0
if '0' not in strInput:
return 0
while chk != 1:
if strInput[i] == strInput[i+1]:
i += 1
else:
chk = 1
j = len(strInput)-1
while chk != 0:
if strInput[j] == strInput[j-1]:
j -= 1
else:
chk = 0
clean_str = strInput[i+1:j]
if len(clean_str) == 0:
return 0
max_count = 1
current_count = 1
for i in range(1, len(clean_str)):
if clean_str[i] == clean_str[i-1]:
current_count += 1
else:
max_count = max(current_count, max_count)
current_count = 1
return max(current_count, max_count)
稳定字段 (G)
如果一个列表arr[0] == arr[-1] == arr[1:-1], 那么这个列表就是稳定字段。下面需要计算给定列表的所有子列表中有多少稳定字段。
def countStableSegments(capacity):
from collections import defaultdict
n = len(capacity)
if n == 0:
return 0
result_count = 0
positions = defaultdict(list)
for i, value in enumerate(capacity):
positions[value].append(i)
for value, indices in positions.items():
num_indices = len(indices)
if num_indices < 2:
continue
start_ptr = 0
for end_ptr in range(1, num_indices):
while start_ptr < end_ptr and indices[end_ptr] - indices[start_ptr] > 1:
subarray_sum = sum(capacity[indices[start_ptr]: indices[end_ptr] + 1])
if subarray_sum - 2 * value == value:
result_count += 1
start_ptr += 1
return result_count
最长连续子数组(G)
给定一个值num,求arr中所有子数组中和为num里长度最长的。
def getMaxColors(prices, money):
print("money: ", money)
if money < min(prices):
return 0
max_length = 0
for start in range(len(prices)):
if sum(prices[start:]) < money:
continue
elif prices[start] == money:
max_length = 1
else:
end = start + 1
temp_sum = sum(prices[start:end])
while (temp_sum < money):
if end + 1 < len(prices):
end += 1
temp_sum += prices[end]
else:
break
max_length = max(end - start, max_length)
return max_length
最高频率元素
返回出现频率最高的第K个元素
from collections import Counter
def main(num, k):
count = Counter(num)
count = count.most_common()
print(count)
ans = count[k-1]
print(f"the {k}th high is {ans[0]}")
if __name__ == "__main__":
nums = [1, 2, 2, 3, 3, 3, 4, 4,4,4,4,4]
k = 3
main(nums, k)
递归
反转括号内字符串:给出一个字符串 s(仅含有小写英文字母和括号)。
请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。注意,您的结果中 不应包含任何括号。
def reverse(str):
return str[:: -1]
def checkposition(str):
if "(" not in str:
if ")" not in str:
print(str)
return None
last_left = 0
first_right = 0
for i in range(0, len(str)):
if str[i] == '(':
last_left = i
for j in range(0, len(str)):
if str[j] == ')':
first_right = j
break
str = str[0:last_left] + reverse(str[last_left+1:first_right]) + str[first_right+1:]
checkposition(str)
checkposition("(abcd)") #dcba
checkposition("(u(love)i)") #iloveu
checkposition("(ed(et(oc))el)") #leetcode
checkposition("a(bcdefghijkl(mno)p)q") #apmnolkjihgfedcbq
检查字符串左右括号是否匹配
def is_valid_parentheses(s):
stack = []
mapping = {")": "(", "}": "{", "]": "["}
for char in s:
if char in mapping:
top_element = stack.pop() if stack else '#'
if mapping[char] != top_element:
return False
else:
stack.append(char)
return not stack
# 测试
test_strings = ["(){}[]", "((()))", "({[]})", "((()", "())", "{{{}}}]", "{[}]"]
for test_str in test_strings:
if is_valid_parentheses(test_str):
print(f"{test_str}: 括号匹配")
else:
print(f"{test_str}: 括号不匹配")
当然,还有一种更简单的方法,这种方法比使用栈更简单,因为它不需要额外的数据结构来存储括号:
def is_valid_parentheses(s):
count = 0
for char in s:
if char == "(":
count += 1
elif char == ")":
count -= 1
if count < 0:
return False
return count == 0
# 测试
test_strings = ["(){}[]", "((()))", "({[]})", "((()", "())", "{{{}}}]", "{[}]"]
for test_str in test_strings:
if is_valid_parentheses(test_str):
print(f"{test_str}: 括号匹配")
else:
print(f"{test_str}: 括号不匹配")
二分查找
- 确定数组的起始位置 start 和结束位置 end,初始时 start 为数组起始位置,end 为数组结束位置。
- 在每一轮循环中,计算数组中间元素的索引 mid,并比较目标元素与中间元素的大小关系。
- 如果目标元素等于中间元素,则直接返回中间元素的索引。
- 如果目标元素小于中间元素,则更新结束位置 end 为 mid,在左半部分继续查找。
- 如果目标元素大于中间元素,则更新起始位置 start 为 mid,在右半部分继续查找。
- 继续重复步骤 2-5,直到 start 和 end 相邻,此时如果目标元素与相邻位置的元素相等,则返回其中一个位置,否则说明目标元素不存在于数组中,返回 -1。
def binary_search(arr, target):
start, end = 0, len(arr) - 1
while True:
if end - start <= 1:
if target == arr[start]:
return start
elif target == arr[end]:
return end
else:
return -1
mid = (start + end) // 2
if arr[mid] >= target:
end = mid
else:
start = mid
print(binary_search([1, 4, 7, 8, 9, 12], 9)) # Output: 4
print(binary_search([1, 4, 7, 8, 9, 12], 3)) # Output: -1
动态规划
爬楼梯:有一个楼梯,总共有10级台阶,每次只能走一级或者两级台阶,全部走完,有多少种走法?
爬楼梯问题可以抽象为:对于第 i 级台阶,可以从第 i-1 级台阶跨一步到达,也可以从第 i-2 级台阶跨两步到达。因此,到达第 i 级台阶的方法数等于到达第 i-1 级台阶的方法数与到达第 i-2 级台阶的方法数之和。
首先我们考虑特殊情况:
- 当只有 1 级台阶时,只有一种方法,即直接爬一步到达,记为
f(1) = 1。 - 当有 2 级台阶时,有两种方法,可以一次跨两步到达,或者分两次跨一步到达,记为
f(2) = 2。
然后,我们从第 3 级台阶开始考虑:
- 到达第 3 级台阶的方法数等于到达第 2 级台阶的方法数与到达第 1 级台阶的方法数之和,即
f(3) = f(2) + f(1)。 - 同理,到达第 4 级台阶的方法数等于到达第 3 级台阶的方法数与到达第 2 级台阶的方法数之和,即
f(4) = f(3) + f(2)。
以此类推,可以得到递推关系式:f(n) = f(n-1) + f(n-2)。
通过这个递推关系,我们可以用动态规划来求解爬楼梯问题。我们只需要初始化 f(1) 和 f(2) 的值,然后通过循环依次计算 f(3)、f(4),直到 f(n)。这样可以大大减少重复计算,提高了效率。
def climb_stairs(n):
if n==1:
return 1
if n==2:
return 2
a,b = 1,2
i = 3
while i<=n:
a,b = b,a+b
i +=1
return b
print(climb_stairs(10))
最大回撤: 有一个数组,求其中两个数x,y,满足x的索引小于y的索引,使得 x-y 最大。例如 arr = [3,7,2,6,4,1,9,8,5], 最大回撤是6,对应的x=7,y=1。
我们从数组的第三个数开始遍历,同时维护两个重要的变量:一个是目前为止遇到的最大值 xmax,另一个是目前为止找到的最大回撤 maxdiff。
在遍历过程中,我们每次都检查前一个数是否大于当前的最大值 xmax。如果是的话,我们就更新最大值 xmax 为前一个数。然后,我们计算当前数与 xmax 的差值,如果这个差值比目前记录的最大回撤还大,那么我们就更新最大回撤 maxdiff,并记录对应的两个数 x 和 y,分别为 xmax 和当前数。
通过这种方法,我们可以在一次遍历数组的过程中,找到满足条件的两个数,并且计算出它们的最大回撤。最终,我们返回最大回撤的值。
def max_drawdown(arr):
# assert len(arr)>2, "len(arr) should > 2!"
x,y = arr[0:2]
xmax = x
maxdiff = x-y
for i in range(2,len(arr)):
if arr[i-1] > xmax:
xmax = arr[i-1]
if xmax - arr[i] > maxdiff:
maxdiff = xmax - arr[i]
x,y = xmax,arr[i]
print("x=",x,",y=",y)
return(maxdiff)
print(max_drawdown([3,7,2,6,4,1,9,8,5]))
给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和
/ > 示例:
>輸入:[-2,1,-3,4,-1,2,1,-5,4]
≥输出:6
>解释:连续子数组[4,-1,2,11 的和最大,为6。
def max_subarray_sum(nums):
max_sum = float('-inf') # 初始化最大和为负无穷大
current_sum = 0 # 当前子数组的和
for num in nums:
current_sum = max(num, current_sum + num) # 更新当前子数组的和,取较大值
max_sum = max(max_sum, current_sum) # 更新最大和,取较大值
return max_sum
# 测试
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
max_sum = max_subarray_sum(nums)
print(max_sum)
哈希表
两数和:寻找列表中满足两数之和等于目标值的元素的下标。例如:arr = [2,7,4,9],target = 6 则返回 [0,2],若不存在,返回空列表[]。
- 最简单的方法,多层循环嵌套
- 通过空间换时间的方式,利用哈希表(字典)来降低时间复杂度至 O(n), 具体思路如下:
- 首先创建一个空的哈希表 dic,用于存储列表中的元素及其对应的索引。
- 遍历列表 arr,对于每个元素 x,我们计算出与之配对的另一个数 y,即 y = target - x。
- 在遍历过程中,我们先检查字典 dic 中是否存在键为 y 的项,如果存在,说明我们找到了满足条件的元素对,直接返回它们的下标。
- 如果不存在,将当前元素 x 及其索引存储在字典 dic 中,以备后续检查。
def sum_of_two(arr, target):
dic = {}
for i, x in enumerate(arr):
y = target - x
if y in dic:
return [dic[y], i]
dic[x] = i
return []
arr = [2, 7, 4, 9]
target = 6
print(sum_of_two(arr, target))
数组查找 (利用哈希表减少复杂度)
查找出一个列表中,(a, b)所组成的数对中满足a=k+b的情况。其中,(a,b)中a b两个数字可以相同,但是(a,b)数对不能重复,k为任意整数(包括0)
def count_distinct_pairs(numbers, k):
# Create a set from the list of numbers for fast lookups
num_set = set(numbers)
# Create a set to store unique pairs
seen_pairs = set()
for num in numbers:
# Check for (num, num + k) pair
if k == 0:
# Handle (a, a) case when k = 0
if num in num_set:
seen_pairs.add((num, num))
else:
if (num + k) in num_set:
# Create a tuple in a sorted manner to ensure uniqueness
pair = (num, num + k) if num < num + k else (num + k, num)
seen_pairs.add(pair)
# Return the number of unique pairs
return len(seen_pairs)
查找同字母字符串
def getGroupedAnagrams(words):
# Dictionary to hold groups of anagrams
anagram_groups = {}
# Process each word in the input list
for word in words:
# Sort the characters in the word to get the anagram key
sorted_word = ''.join(sorted(word))
# If the sorted word key is not in the dictionary, add it with an empty list
if sorted_word not in anagram_groups:
anagram_groups[sorted_word] = []
# Append the original word to the correct anagram group
anagram_groups[sorted_word].append(word)
# The number of keys in the dictionary represents the number of anagram groups
return len(anagram_groups)
# Example usage
words = ["cat", "listen", "silent", "kitten", "salient"]
print(getGroupedAnagrams(words))
数学推导
字符串叠加
给定一个由以下规则构造的序列,请确定该序列中第N个位置的数字(使用 0-based 索引)。
序列的第一个元素为 0。 对于每次迭代,执行以下操作:复制当前序列的整个内容,将 0 替换为 1,1 替换为 2,2 替换为 0,并将修改后的序列放置在当前序列的末尾。例如:
0 → 0
0 1 → 0 1 1 2
0 1 1 2 → 0 1 1 2 1 2 2 0
- 解法一:
def find_nth_element(N): result = 0 while N > 0: if N % 2 == 1: result = (result + 1) % 3 N //= 2 return result import sys for line in sys.stdin: N = int(line.strip()) print(find_nth_element(N)) - 解法二:
def find_nth_element(N): return N.bit_count() % 3
最大值计算 (G)
给定三个整数 x, y 和 z,需要通过对 x 进行以下操作将其转换为 y:
- 给 x 加 1,或者
- 从 x 中减去 1。
最多可以进行 z 次操作来达到 y,并且如果需要的话,所有操作可以相同。在应用这些操作的过程中,跟踪 x 所能达到的最大值。目标是在 z 次操作内到达目标值 y 的同时,使得 x 在这个过程中尽可能达到最高的值。
例如:
- x = 4
- y = 4
- z = 4
在这种情况下,x 所能达到的最大值是 6。因为最多可以进行 4 次操作,可以先加两次 1 然后减去两次 1,从而回到 4。进行 0 次或 2 次操作也可以达到目标值 4,但最大值分别为 4 和 5。
def findMaxNum(x, y, z):
distance = abs(y - x)
if distance > z:
return -1
remaining_operations = z - distance
max_value = x + remaining_operations
if remaining_operations % 2 != 0:
max_value -= 1
return max_value
树
求postorder
知道inorder和preorder, 求postorder
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build_tree(preorder, inorder):
if not preorder or not inorder:
return None
root_val = preorder[0]
root = TreeNode(root_val)
mid_idx = inorder.index(root_val)
root.left = build_tree(preorder[1:1 + mid_idx], inorder[:mid_idx])
root.right = build_tree(preorder[1 + mid_idx:], inorder[mid_idx + 1:])
return root
def postorder_traversal(root):
return postorder_traversal(root.left) + postorder_traversal(root.right) + [root.val] if root else []
# 输入
inorder = [9, 2, 4, 6, 8, 7, 3, 1, 5, 10]
preorder = [8, 2, 9, 6, 4, 1, 3, 7, 5, 10]
# 构建二叉树
root = build_tree(preorder, inorder)
# 计算后序遍历
postorder = postorder_traversal(root)
print("Postorder Traversal:", postorder)
最短路径: 广度优先搜索(BFS)
给一个长宽为m,n的矩阵,起点终点坐标,一个含有block坐标的列表,问不经过block列表内的坐标,最短走多少距离从起点到终点?不能斜着走,无法到达返回-1,注意,要返回从起点到终点的最短距离,无法走到返回-1
BFS:
- 使用队列来存储待访问的位置,初始时加入起点。
- 使用一个集合来记录已经访问过的位置,避免重复访问。
- 每次从队列中取出一个位置并检查是否为终点。
- 如果是终点,返回当前的距离。
- 否则,尝试向四个方向移动,如果新位置合法且未被访问,则将其加入队列。
from collections import deque
def shortest_path(m, n, start, end, blocks):
# 将 blocks 转换为集合以便于快速查找
block_set = set(blocks)
# 定义四个方向:上下左右
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
# BFS 初始化
queue = deque([(start[0], start[1], 0)]) # (x, y, distance)
visited = set()
visited.add(start)
while queue:
x, y, distance = queue.popleft()
# 如果到达终点,返回当前距离
if (x, y) == end:
return distance
# 遍历所有方向
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 检查新的坐标是否在矩阵内,并且没有被阻挡
if 0 <= nx < m and 0 <= ny < n and (nx, ny) not in block_set and (nx, ny) not in visited:
visited.add((nx, ny)) # 记录已访问坐标
queue.append((nx, ny, distance + 1)) # 添加新坐标到队列
# 如果队列为空且未找到路径,返回 -1
return -1
# 示例用法
m = 5
n = 5
start = (0, 0)
end = (4, 4)
blocks = [(1, 1), (1, 2), (1, 3)]
result = shortest_path(m, n, start, end, blocks)
print(result) # 输出结果
第一行输入每个任务完成的时间,后面每行都是任务的依赖(-1代表无依赖),求任务执行的最短时间。
from collections import defaultdict, deque
def find_shortest_time(task_times, dependencies):
"""
计算所有任务的最短完成时间
:param task_times: 每个任务的执行时间
:param dependencies: 每个任务的依赖关系
:return: 最短完成时间
"""
n = len(task_times) # 任务的总数
print("任务执行时间:", task_times)
# 构建图和每个任务的入度(入度代表有多少任务依赖当前任务)
graph = defaultdict(list) # 用于存储任务依赖关系的有向图
indegree = [0] * n # 用于存储每个任务的入度
# 构建图和入度信息
for i, deps in enumerate(dependencies):
for dep in deps:
if dep != -1: # 如果不是 -1,说明有依赖
# 修正依赖关系,dep 依赖于 i
graph[dep].append(i) # 添加边,dep -> i
indegree[i] += 1 # 任务 i 的入度加 1
print("依赖关系图:", dict(graph))
print("任务入度:", indegree)
# 使用拓扑排序的思路来处理任务调度
zero_indegree = deque([i for i in range(n) if indegree[i] == 0])
earliest_completion = [0] * n
# 拓扑排序并计算最早完成时间
while zero_indegree:
task = zero_indegree.popleft() # 取出一个没有依赖的任务
# 更新当前任务的完成时间
earliest_completion[task] += task_times[task]
# 打印当前任务及其完成时间
print(f"任务 {task} 被处理,完成时间: {earliest_completion[task]}")
# 遍历该任务的所有后续任务(依赖当前任务的任务)
for next_task in graph[task]:
# 更新下一个任务的最早完成时间
earliest_completion[next_task] = max(earliest_completion[next_task], earliest_completion[task])
print(f"更新任务 {next_task} 的最早完成时间为: {earliest_completion[next_task]} (依赖任务 {task})")
# 将当前任务移除后,更新后续任务的入度
indegree[next_task] -= 1
if indegree[next_task] == 0: # 如果入度为 0,加入队列
zero_indegree.append(next_task)
# 所有任务的最早完成时间加上任务的执行时间
for i in range(n):
earliest_completion[i] += task_times[i]
# 打印所有任务的最早完成时间
print("所有任务的最早完成时间:", earliest_completion)
# 最后返回所有任务最早完成的最大值,即为所有任务的最短完成时间
return max(earliest_completion)