一、原题
给你一个整数数组 nums,每次 操作 会从中选择一个元素并 将该元素的值减少 1。如果符合下列情况之一,则数组 A 就是锯齿数组:
每个偶数索引对应的元素都大于相邻的元素,即 A[0] > A[1] < A[2] > A[3] < A[4] > ...
或者,每个奇数索引对应的元素都大于相邻的元素,即 A[0] < A[1] > A[2] < A[3] > A[4] < ...
返回将数组 nums 转换为锯齿数组所需的最小操作次数。
来源:力扣(LeetCode)
二、思路
一开始没怎么理解题目,感觉好难。后来仔细一想,只需要分奇数、偶数索引两种情况遍历数组就可以用。假定偶数索引对应的元素都大于相邻的元素,则我们只需要遍历所有的偶数索引处的元素,判断它的元素值:如果它的值大于相邻元素,对它进行递减操作;否则不进行任何操作。
以[9, 6, 1, 6, 2]为例,
情况一:偶数索引处值大于相邻元素,固定9、1、2是不动的,遍历奇数处元素,i=1时,nums[1]=6,使得6小于两侧的元素9和1,因此将6递减至0,需要进行6次操作;i=3时,将对应的元素值6递减至min(1, 2)-1,即0,也需要进行6次操作;因此情况一需进行12次递减操作
情况二:奇数索引处元素大于相邻元素,固定6、6不动,遍历偶数处元素,递减它们的值至满足要求。i=0时,将9递减至比6小,即5,需进行4次操作;i=2时,对应元素值1小于相邻元素;i=4时对应元素值2小于相邻元素;因此情况二需进行4次操作。4 < 12,因此函数返回4.。
代码如下:
class Solution:
def movesToMakeZigzag(self, nums: List[int]) -> int:
length = len(nums)
cnt_even = 0 # 用于记录情况一时需要进行的最小操作次数
cnt_odd = 0 # 用于记录情况二时需要进行的最小操作次数
# 情况一:偶数位置元素大的情况, 遍历、修改奇数位置的元素
for i in range(1, length, 2):
# 根据i处是否是最后一个元素进行分情况讨论
if i + 1 < length:
# 判断相邻的两个元素谁更小,我们需要将nums[i]递减到比较小值还要小1
if nums[i - 1] < nums[i + 1]:
if nums[i] >= nums[i-1]:
cnt_even += (nums[i] - nums[i - 1] + 1)
else:
if nums[i] >= nums[i+1]:
cnt_even += (nums[i] - nums[i + 1] + 1)
# i处是最后一个元素,因此只有左侧一个相邻元素
else:
if nums[i] > nums[i - 1]:
cnt_even += (nums[i] - nums[i - 1] + 1)
# 情况二:奇数位置元素大时,遍历、修改偶数位置元素
for i in range(0, length, 2):
if i + 1 < length:
if i == 0:
if nums[i] >= nums[i + 1]:
cnt_odd += (nums[i] - nums[i+1] + 1)
else:
if nums[i - 1] < nums[i + 1]:
if nums[i] >= nums[i-1]:
cnt_odd += (nums[i] - nums[i - 1] + 1)
else:
if nums[i] >= nums[i+1]:
cnt_odd += (nums[i] - nums[i + 1] + 1)
else:
if nums[i] >= nums[i - 1]:
cnt_odd += (nums[i] - nums[i - 1] + 1)
# 返回二者的最小值
return min(cnt_even, cnt_odd)
乍一看的话,上面的代码确实比较繁琐,是否有更简洁的写法,在题解中看到了刘岳大佬的解法。其实基本思路是完全一样的,参考代码如下:
def movesToMakeZigzag(nums):
length = len(nums)
cnt_even = 0
cnt_odd = 0
for i in range(0, length):
# 固定奇数索引处的值,修改偶数处元素
if i%2==0:
d1 = nums[i] - nums[i-1] + 1 if i>0 and nums[i]>=nums[i-1] else 0 # 考虑i=0时的情况,此时d1直接为0
d2 = nums[i] + nums[i+1] + 1 if i<length-1 and nums[i]>=nums[i+1] else 0
cnt_even += max(d1, d2)
else:
d1 = nums[i] - nums[i-1] + 1 if nums[i] >= nums[i-1] else 0 # 此时,i为奇数,前面一定有其他元素
d2 = nums[i] - nums[i+1] + 1 if i<length-1 and nums[i]>=nums[i+1] else 0
cnt_odd += max(d1, d2)
return min(cnt_even, cnt_odd)