我尝试在函数式编程和无变量中解决您的问题。
给定 2 个数组
let nums0 = [1, 7, 17, 25, 38]
let nums1 = [2, 5, 17, 29, 31]
我们将第一个与第二个的反转版本连接起来
let all = nums0 + nums1.reversed()
结果会是这种金字塔。
[1, 7, 17, 25, 38, 31, 29, 17, 5, 2]
理论
现在,如果我们一个接一个地选择边缘(左或右)的最小元素,我们保证会按升序选择所有元素。
[1, 7, 17, 25, 38, 31, 29, 17, 5, 2] -> we pick 1 (left edge)
[7, 17, 25, 38, 31, 29, 17, 5, 2] -> we pick 2 (right edge)
[7, 17, 25, 38, 31, 29, 17, 5] -> we pick 5 (right edge)
[7, 17, 25, 38, 31, 29, 17] -> we pick 7 (left edge)
[17, 25, 38, 31, 29, 17] -> we pick 17 (right edge)
[17, 25, 38, 31, 29] -> we pick 17 (left edge)
[25, 38, 31, 29] -> we pick 25 (left edge)
[38, 31, 29] -> we pick 29 (right edge)
[38, 31] -> we pick 31 (right edge)
[38] -> we pick 38 (both edges)
现在让我们看看我们构建的数组,挑选所有这些元素。
We selected 1: [1]
We selected 2: [1, 2]
We selected 5: [1, 2, 5]
We selected 7: [1, 2, 5, 7]
We selected 17: [1, 2, 5, 7, 17]
We selected 17: [1, 2, 5, 7, 17, 17]
We selected 25: [1, 2, 5, 7, 17, 17, 25]
We selected 29: [1, 2, 5, 7, 17, 17, 25, 29]
We selected 31: [1, 2, 5, 7, 17, 17, 25, 29, 31]
We selected 38: [1, 2, 5, 7, 17, 17, 25, 29, 31, 38]
这看起来是我们想要达到的结果吧?
现在是时候编写一些 Swifty 代码了。
代码!
好的,我们如何在函数式编程中做到这一点?
这是代码
let merged = all.reduce((all, [Int]())) { (result, elm) -> ([Int], [Int]) in
let input = result.0
let output = result.1
let first = input.first!
let last = input.last!
// I know these ☝️ force unwraps are scary but input will never be empty
if first < last {
return (Array(input.dropFirst()), output + [first])
} else {
return (Array(input.dropLast()), output + [last])
}
}.1
它是如何工作的?
1.
我们将一个包含all 数组和一个空数组的元组传递给reduce。
all.reduce((all, [Int]()))
我们将调用第一个数组input 和第二个数组output。
逐步减少将删除input 边缘的最小元素,并将其附加到output。
2. 然后,在闭包内部,我们为 out 元组的 2 个元素命名
let input = result.0
let output = result.1
3.我们选择输入的第一个和最后一个元素
let first = input.first!
let last = input.last!
是的,我也不喜欢强制展开,但由于input 永远不会为空,因此这些强制展开永远不会产生致命错误。
4. 现在如果first < last 我们需要:
- 返回输入减去第一个元素
- 返回输出 + 输入的第一个元素
否则我们会做相反的事情。
if first < last {
return (Array(input.dropFirst()), output + [first])
} else {
return (Array(input.dropLast()), output + [last])
}
5.最后我们选择reduce返回的元组的第二个元素,因为它是我们存储结果的地方。
}.1
时间复杂度
计算时间为 O(n + m),其中 n 是 nums0.count,m 是 nums1.count,因为:
nums1.reversed()
这个☝️是O(1)
all.reduce(...) { ... }
这个☝️是O(n + m),因为闭包是针对all的每个元素执行的
时间复杂度为 O(n) ^ 2。请参阅下面来自 @dfri 的有价值的 cmets。
版本 2
这个版本应该真的有 O(n) 时间复杂度。
let merged = all.reduce(into: (all, [Int]())) { (result, elm) in
let first = result.0.first!
let last = result.0.last!
if first < last {
result.0.removeFirst()
result.1.append(first)
} else {
result.0.removeLast()
result.1.append(last)
}
}.1