软件开发常见面试题目总结(含答案示例,更新中)

284 阅读21分钟

排序

冒泡排序: 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。每条日志都是以空格分隔的字串,其第一个字为字母与数字混合的标识符。

有两种不同类型的日志:

  • 字母日志:除标识符之外,所有字均由小写字母组成
  • 数字日志:除标识符之外,所有字均由数字组成

请按下述规则将日志重新排序:

  • 所有 字母日志 都排在 数字日志 之前。
  • 字母日志 在内容不同时,忽略标识符后,按内容字母顺序排序;在内容相同时,按标识符排序。
  • 数字日志 应该保留原来的相对顺序。

返回日志的最终顺序。

示例 11: 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)

SQL