题目描述
小C有两个长度为 N 的数组 A 和 B。他可以进行以下两种操作,来将数组 A 转换为数组 B:
- 反转数组 A,即使数组 A 的元素顺序完全颠倒。
- 在 [1, N] 范围内选择一个整数 i,然后可以对 A[i] 添加或减去任意值。
你的任务是帮助小C找到使数组 A 等于数组 B 所需的最小操作次数。
示例
-
样例1:
- 输入:N = 3, A = [1, 2, 5], B = [4, 2, 1]
- 输出:2
- 解释:第一步反转数组 A,得到新数组 A = [5, 2, 1]。第二步从位置 1 减去 1,得到新数组 A = [4, 2, 1]。
-
样例2:
- 输入:N = 4, A = [7, 8, 6, 2], B = [6, 2, 8, 7]
- 输出:3
- 解释:第一步反转数组 A,得到新数组 A = [2, 6, 8, 7]。第二步从位置 0 加上 4,得到新数组 A = [6, 6, 8, 7]。第三步从位置 1 加上 2,得到新数组 A = [6, 2, 8, 7]。
-
样例3:
- 输入:N = 2, A = [3, 9], B = [9, 3]
- 输出:1
- 解释:直接反转数组 A 即可。
思路
- 直接比较:检查数组 A 是否已经等于数组 B。如果是,返回 0。
- 反转比较:检查将数组 A 反转后是否等于数组 B。如果是,返回 1。
- 逐个修改:计算 A 和 B 中对应位置不同的元素个数
diff_count。 - 反转后再逐个修改:计算 A 反转后与 B 中对应位置不同的元素个数
diff_count_reversed,并考虑最小操作次数。
代码详解
python
def solution(N: int, A: list, B: list) -> int:
# 直接比较
if A == B:
return 0
# 反转比较
A_reversed = A[::-1]
if A_reversed == B:
return 1
# 逐个修改
diff_count = sum(1 for i in range(N) if A[i] != B[i])
# 反转后再逐个修改
diff_count_reversed = sum(1 for i in range(N) if A_reversed[i] != B[i])
# 返回最小操作次数
return min(diff_count, 1 + diff_count_reversed)
if __name__ == '__main__':
print(solution(N=3, A=[1, 2, 5], B=[4, 2, 1]) == 2)
print(solution(N=4, A=[7, 8, 6, 2], B=[6, 2, 8, 7]) == 3)
print(solution(N=2, A=[3, 9], B=[9, 3]) == 1)
知识总结
新知识点
- 列表切片:
A[::-1]用于反转列表。 - 生成器表达式:
sum(1 for i in range(N) if A[i] != B[i])用于计算不同元素的个数。 - 最小操作次数:通过
min(diff_count, 1 + diff_count_reversed)计算最小操作次数。
关键知识点详解
1. 列表切片
列表切片是 Python 中一种非常强大的功能,用于从列表中提取子列表。切片的基本语法是 list[start:stop:step],其中:
start是切片的起始索引(包含)。stop是切片的结束索引(不包含)。step是步长,即每隔多少个元素取一个。
示例
A = [1, 2, 3, 4, 5]
print(A[1:4]) # 输出 [2, 3, 4]
print(A[:3]) # 输出 [1, 2, 3]
print(A[2:]) # 输出 [3, 4, 5]
print(A[::2]) # 输出 [1, 3, 5]
print(A[::-1]) # 输出 [5, 4, 3, 2, 1],反转列表
在本题中,我们使用 A[::-1] 来反转列表 A,这相当于创建了一个新的列表,其中元素顺序与原列表相反。
2. 生成器表达式
生成器表达式是一种简洁的方式来创建生成器对象。生成器对象可以在需要时生成值,而不是一次性生成所有值,因此在处理大数据时非常高效。
语法
生成器表达式的语法类似于列表推导式,但使用圆括号 () 而不是方括号 []。
示例
numbers = [1, 2, 3, 4, 5]
squares = (x**2 for x in numbers)
print(list(squares)) # 输出 [1, 4, 9, 16, 25]
# 生成器表达式可以用于 sum 函数
sum_of_squares = sum(x**2 for x in numbers)
print(sum_of_squares) # 输出 55
在本题中,我们使用生成器表达式 sum(1 for i in range(N) if A[i] != B[i]) 来计算 A 和 B 中不同元素的个数。这比使用列表推导式更高效,因为生成器表达式不会一次性生成所有值,而是按需生成。
3. 最小操作次数
在解决这道题目时,我们需要找到将数组 A 转换为数组 B 的最小操作次数。我们考虑了以下几种操作:
- 直接比较:如果
A已经等于B,则不需要任何操作。 - 反转比较:如果将
A反转后等于B,则只需要一次操作。 - 逐个修改:计算
A和B中不同元素的个数diff_count,这需要diff_count次操作。 - 反转后再逐个修改:计算
A反转后与B中不同元素的个数diff_count_reversed,这需要1 + diff_count_reversed次操作。
方法选择
- 直接比较:这是最简单的一步,检查数组是否已经相等。
- 反转比较:反转数组后检查是否相等,这是一种常见的优化手段。
- 逐个修改:计算不同元素的个数,这是最基本的解决方案。
- 反转后再逐个修改:结合反转和逐个修改,找到最小操作次数。
问题及解决方法
-
问题:在某些情况下,代码可能没有正确处理所有可能的操作组合。
- 解决方法:仔细检查每种操作的可能性,特别是反转后再逐个修改的情况。
-
问题:代码效率低下。
- 解决方法:使用生成器表达式和列表切片等高效操作,减少不必要的计算。
-
问题:测试用例覆盖不全。
- 解决方法:编写更多的测试用例,涵盖各种边界情况和特殊情况。
总结
通过这道题目,我学到了如何处理数组转换问题,掌握了列表切片和生成器表达式的使用方法。在解决问题时,逐步思考和优化代码是非常重要的。希望这些经验和建议对其他入门同学有所帮助。