首先,我检查了这种问题是否适合Stackoverflow,并基于一个类似的问题(javascript)和以下问题:https://meta.stackexchange.com/questions/129598/which-computer-science-programming-stack-exchange-sites-do-i-post-on –确实如此。
就这样。我认为挑战非常简单:
给出五个正整数,求出最小值和最大值 可以通过对五个整数中的四个精确求和来计算。然后 将相应的最小值和最大值打印为一行 两个以空格分隔的长整数。
例如,。我们的最小和为最大。我们会 打印
16 24
输入约束:
1 <= arr[i] <= (10^9)
我的解决方案非常简单。这是我最擅长的:
func miniMaxSum(arr: [Int]) -> Void {
let sorted = arr.sorted()
let reversed = Array(sorted.reversed())
var minSum = 0
var maxSum = 0
_ = sorted
.filter({ $0 != sorted.last!})
.map { minSum += $0 }
_ = reversed
.filter({ $0 != reversed.last!})
.map { maxSum += $0 }
print("\(minSum) \(maxSum)")
}
如您所见,我有两个排序数组。一种是递增,另一种是递减。我要删除两个新排序的数组的最后一个元素。我删除最后一个元素的方法是使用filter
,这可能会造成问题。但是从那里开始,我认为我可以轻松获得这四个元素的最小和最大总和。
我有13/14个测试用例通过了。我的问题是,该解决方案可能失败的测试案例是什么?
问题链接:https://www.hackerrank.com/challenges/mini-max-sum/problem
答案 0 :(得分:3)
这里
_ = sorted
.filter({ $0 != sorted.last!})
.map { minSum += $0 }
您的期望是添加了除最大元素以外的所有元素。但这是正确的,前提是最大的元素是 unique。(并且最大和也类似)。
选择一个具有所有相同错误的数组会使问题更加明显:
miniMaxSum(arr: [1, 1, 1, 1, 1])
// 0 0
一个简单的解决方案是一次计算所有元素的总和,然后通过减去最大的,最小的数组元素来获得结果。我将实现留给您:)
答案 1 :(得分:3)
这是O(n)解决方案:
func miniMaxSum(arr: [Int]) {
var smallest = Int.max
var greatest = Int.min
var sum = 0
for x in arr {
sum += x
smallest = min(smallest, x)
greatest = max(greatest, x)
}
print(sum - greatest, sum - smallest, separator: " ")
}
答案 2 :(得分:1)
尝试此已被接受的
func miniMaxSum(arr: [Int]) -> Void {
let sorted = arr.sorted()
let minSum = sorted[0...3].reduce(0, +)
let maxSum = sorted[1...4].reduce(0, +)
print("\(minSum) \(maxSum)"
}
答案 3 :(得分:1)
我知道这不是codereview.stackexchange.com,但是我认为需要进行一些清理,因此我将从此开始。
let reversed = Array(sorted.reversed())
ReversedCollection
返回的Array.reversed()
的全部意义在于,它不会导致元素的复制,并且不会占用任何额外的内存或时间。它只是一个集合的包装,它拦截索引操作并将其更改为模仿已反转的缓冲区。是否要求.first
?它会给您.last
打包的集合。是否要求.last
?它将返回.first
,等等。
通过从Array
初始化一个新的sorted.reversed()
,您将导致不必要的复制,并破坏了ReversedCollection
的意义。在某些情况下,这可能是有必要的(例如,您希望将指针传递给C API的反向元素缓冲区),但这不是其中之一。
所以我们可以将其更改为let reversed = sorted.reversed()
-> Void
不做任何事情,请忽略。
sorted.filter({ $0 != sorted.last!})
效率低下。
...但不仅如此,这是错误的根源。这有一个错误。如果您有一个像[1, 1, 2, 3, 3]
这样的数组,那么当minSum
应该是4
({ {1}})。同样,[1, 1, 2]
将是7
([1, 1, 2, 3]
的总和),而不是maxSum
(8
的总和)。
您正在扫描整个数组,进行[2, 3, 3]
相等性检查,只是丢弃具有已知位置的元素(最后一个元素)。而是使用9
,它返回包装输入的集合,但其操作掩盖了最后一个元素的存在。
[1, 2, 3, 3]
sorted.count
...是反模式。 dropLast()
和_ = sorted
.dropLast()
.map { minSum += $0 }
_ = reversed
.dropLast()
.map { maxSum += $0 }
之间的区别在于,它生成一个结果数组,该数组存储每个输入元素评估的闭包的返回值。如果您不打算使用结果,请使用_ = someCollection.map(f)
map
但是,还有更好的方法。与其通过使变量变异并手动添加变量来求和,不如使用forEach
来进行求和。之所以理想,是因为它允许您删除forEach
和sorted.dropLast().forEach { minSum += $0 }
reversed.dropLast().forEach { maxSum += $0 }
的可变性。
reduce
您根本不需要minSum
变量。您可以通过在maxSum
上进行操作并使用let minSum = sorted.dropLast().reduce(0, +)
let maxSum = reversed.dropLast().reduce(0, +)
而不是reversed
来实现相同的目的:
sorted
您的代码假定输入大小始终为5。最好在代码中进行记录:
dropFirst()
解决方案的一般化会使用大量额外的内存,而您可能无法使用这些内存。
此问题解决了求和数(总是4)和输入数字(总是5)的问题。可以将此问题概括为从任何大小的dropLast()
中选择func miniMaxSum(arr: [Int]) {
let sorted = arr.sorted()
let minSum = sorted.dropLast().reduce(0, +)
let maxSum = sorted.dropFirst().reduce(0, +)
print("\(minSum) \(maxSum)")
}
个数字。在这种情况下,两次排序和求和效率很低:
func miniMaxSum(arr: [Int]) {
assert(arr.count == 5)
let sorted = arr.sorted()
let minSum = sorted.dropLast().reduce(0, +)
let maxSum = sorted.dropFirst().reduce(0, +)
print("\(minSum) \(maxSum)")
}
summedElementCount
,则可以减少为`O(1)。您的解决方案的时间复杂度为arr
推导:首先排序(花费O(arr.count)
),然后对第一个和最后一个arr
(每个O((arr.count * log_2(arr.count)) + summedElementCount)
)求和
O(arr.count * log_2(arr.count))
可以使用有界优先级队列来解决此问题,例如Google的Gauva Java库中的summedElementCount
。它只是min-max heap的包装器,它维护固定数量的元素,将其添加到后,将导致最大的元素(根据提供的比较器)被逐出。如果您在Swift中可以使用类似的功能,则可以执行以下操作:
O(summedElementCount)
此解决方案的空间复杂度只有 O(arr.count * log_2(arr.count)) + (2 * O(summedElementCount))
= O(arr.count * log_2(arr.count)) + O(summedElementCount) // Annihilation of multiplication by a constant factor
= O((arr.count * log_2(arr.count)) + summedElementCount) // Addition law for big O
个额外空间,才能容纳两个队列,每个队列的最大大小为MinMaxPriorityQueue
。
func miniMaxSum(arr: [Int], summedElementCount: Int) {
let minQueue = MinMaxPriorityQueue<Int>(size: summedElementCount, comparator: <)
let maxQueue = MinMaxPriorityQueue<Int>(size: summedElementCount, comparator: >)
for i in arr {
minQueue.offer(i)
maxQueue.offer(i)
}
let (minSum, maxSum) = (minQueue.reduce(0, +), maxQueue.reduce(0, +))
print("\(minSum) \(maxSum)")
}
此解决方案的时间复杂度为O(summedElementCount)
派生:for循环执行summedElementCount
个迭代,每个迭代都由两个队列上的summedElementCount <= arr.count
操作组成。
O(arr.count * log_2(summedElementCount))
我不清楚这是比arr.count
好还是坏。如果您知道,请在下面的评论中让我知道!
答案 4 :(得分:0)
试试这个-
func miniMaxSum(arr: [Int]) -> Void {
var minSum = 0
var maxSum = 0
var minChecked = false
var maxChecked = false
let numMax = arr.reduce(Int.min, { max($0, $1) })
print("Max number in array: \(numMax)")
let numMin = arr.reduce(Int.max, { min($0, $1) })
print("Min number in array: \(numMin)")
for item in arr {
if !minChecked && numMin == item {
minChecked = true
} else {
maxSum = maxSum + item
}
if !maxChecked && numMax == item {
maxChecked = true
} else {
minSum = minSum + item
}
}
print("\(minSum) \(maxSum)")
}