前言:
算法训练系列是做《代码随想录》一刷,个人的学习笔记和详细的解题思路,总共会有60篇博客来记录,计划用60天的时间刷完。
内容包括了面试常见的10类题目,分别是:数组,链表,哈希表,字符串,栈与队列,二叉树,回溯算法,贪心算法,动态规划,单调栈。
博客记录结构上分为 思路,代码实现,复杂度分析,思考和收获,四个方面。
如果这个系列的博客可以帮助到读者,就是我最大的开心啦,一起LeetCode一起进步呀;)
目录
LeetCode1005.K次取反后最大化的数组和
链接:1005. K 次取反后最大化的数组和 - 力扣(LeetCode)
1. 思路
本题思路其实比较好想了,如何可以让数组和最大呢?
贪心的思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。
局部最优可以推出全局最优。
那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让 数组和 达到最大。
那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。
虽然这道题目大家做的时候,可能都不会去想什么贪心算法,一鼓作气,就AC了。
我这里其实是为了给大家展现出来 经常被大家忽略的贪心思路,这么一道简单题,就用了两次贪心!
那么本题的解题步骤为:
- 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
- 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
- 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
- 第四步:求和
2. 代码实现
# time:O(NlogN);space:O(N)
class Solution(object):
def largestSumAfterKNegations(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
# 将数组nums按照绝对值大小,从大到小排序
sortedNums = sorted(nums,key=abs,reverse=True)
index = 0
# 从大的开始,把负数变成正数
while k>0 and index<len(nums):
if sortedNums[index]<0:
sortedNums[index] *= -1
k -= 1
index += 1
# 如果所有数都变成非负数之后,K仍为正数
# 就针对最后一个绝对值最小的数,把K都用完
if k>0:
sortedNums[-1] *= (-1)**k
return sum(sortedNums)
3. 复杂度分析
-
时间复杂度:O(logN)
其中N为数组nums的长度,首先需要对数组nums进行排序的时间复杂度为O(NlogN);然后需要遍历一遍数组,从大到小把尽可能多的负数变成正数,O(N),还有sum操作,复杂度O(N),总体时间复杂度O(NlogN);
-
空间复杂度:O(N)
其中N为nums的长度,sorted排序新建了一个数组,O(N);
4. 思考与收获
-
空间复杂度上还可以继续优化,不用sorted,而用sort,就会在原数组上进行操作,不会新建一个数组,复杂度可以降低为O(1),代码如下:
# time:O(NlogN);space:O(1) class Solution(object): def largestSumAfterKNegations(self, nums, k): """ :type nums: List[int] :type k: int :rtype: int """ # 将数组nums按照绝对值大小,从大到小排序 nums.sort(key=abs,reverse=True) index = 0 # 从大的开始,把负数变成正数 while k>0 and index<len(nums): if nums[index]<0: nums[index]