Swift Mini-Max总和一个测试用例失败-HackerRank

时间:2019-06-20 13:17:11

标签: swift algorithm

首先,我检查了这种问题是否适合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

5 个答案:

答案 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,但是我认为需要进行一些清理,因此我将从此开始。

  1. let reversed = Array(sorted.reversed())

    ReversedCollection返回的Array.reversed()的全部意义在于,它不会导致元素的复制,并且不会占用任何额外的内存或时间。它只是一个集合的包装,它拦截索引操作并将其更改为模仿已反转的缓冲区。是否要求.first?它会给您.last打包的集合。是否要求.last?它将返回.first,等等。

    通过从Array初始化一个新的sorted.reversed(),您将导致不必要的复制,并破坏了ReversedCollection的意义。在某些情况下,这可能是有必要的(例如,您希望将指针传递给C API的反向元素缓冲区),但这不是其中之一。

    所以我们可以将其更改为let reversed = sorted.reversed()

  2. -> Void不做任何事情,请忽略。

  3. sorted.filter({ $0 != sorted.last!})效率低下。

    ...但不仅如此,这是错误的根源。这有一个错误。如果您有一个像[1, 1, 2, 3, 3]这样的数组,那么当minSum应该是4({ {1}})。同样,[1, 1, 2]将是7[1, 1, 2, 3]的总和),而不是maxSum8的总和)。

    您正在扫描整个数组,进行[2, 3, 3]相等性检查,只是丢弃具有已知位置的元素(最后一个元素)。而是使用9,它返回包装输入的集合,但其操作掩盖了最后一个元素的存在。

    [1, 2, 3, 3]
  4. sorted.count

    ...是反模式。 dropLast()_ = sorted .dropLast() .map { minSum += $0 } _ = reversed .dropLast() .map { maxSum += $0 } 之间的区别在于,它生成一个结果数组,该数组存储每个输入元素评估的闭包的返回值。如果您不打算使用结果,请使用_ = someCollection.map(f)

    map

    但是,还有更好的方法。与其通过使变量变异并手动添加变量来求和,不如使用forEach来进行求和。之所以理想,是因为它允许您删除forEachsorted.dropLast().forEach { minSum += $0 } reversed.dropLast().forEach { maxSum += $0 } 的可变性。

    reduce
  5. 您根本不需要minSum变量。您可以通过在maxSum上进行操作并使用let minSum = sorted.dropLast().reduce(0, +) let maxSum = reversed.dropLast().reduce(0, +) 而不是reversed来实现相同的目的:

    sorted
  6. 您的代码假定输入大小始终为5。最好在代码中进行记录:

    dropFirst()
  7. 解决方案的一般化会使用大量额外的内存,而您可能无法使用这些内存。

    此问题解决了求和数(总是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)")
}