这是我之前question的后续内容。
假设我想在给定的排序数组中找到一对整数,它们总和给定数字x
。众所周知的“一次通过”解决方案看起来像这样:
def pair(a: Array[Int], target: Int): Option[(Int, Int)] = {
var l = 0
var r = a.length - 1
var result: Option[(Int, Int)] = None
while (l < r && result.isEmpty) {
(a(l), a(r)) match {
case (x, y) if x + y == target => result = Some(x, y)
case (x, y) if x + y < target => l = l + 1
case (x, y) if x + y > target => r = r - 1
}
}
result
}
你如何建议在没有任何可变状态的情况下进行功能性写作?
我想我可以用Stream
编写一个递归版本(Scala中的惰性列表)
你能推荐一个非递归版本吗?
答案 0 :(得分:4)
这是一个相当简单的版本。它创建Stream
Vectors
,删除每次迭代的第一个或最后一个元素。然后我们限制其他无限Stream
的大小(-1这样你就不能自己添加一个数字),然后map
将它输入输出格式并检查目标条件。 / p>
def findSum(a: Vector[Int], target: Int): Option[(Int, Int)] = {
def stream = Stream.iterate(a){
xs => if (xs.head + xs.last > target) xs.init else xs.tail
}
stream.take (a.size - 1)
.map {xs => (xs.head, xs.last)}
.find {case (x,y) => x + y == target}
}
Scala集合的伴随对象中隐藏了很多宝石,例如Stream.iterate
。我强烈建议您查看它们。了解它们可以大大简化这样的问题。
答案 1 :(得分:2)
这是一个没有使用索引的版本(除非有重要的计算值,否则我会尽量避免使用索引):
label.text = [rawText stringByReplacingOccurrencesOfString@"\\n" withString:@"\n"];
它是递归的,但不需要Stream。 def findPair2(x: Int, a: Array[Int]): Option[(Int, Int)] = {
def findPairLoop(x: Int, l: Array[Int], r: Array[Int]): Option[(Int, Int)] = {
if (l.head >= r.last) None
else if (l.head + r.last == x) Some((l.head, r.last))
else if (l.head + r.last > x) findPairLoop(x, l, r.init)
else findPairLoop(x, l.tail, r)
}
findPairLoop(x, a, a)
}
和tail
对于数组是O(N)但如果我们使用列表并反转init
集合以避免r
和init
O(N)版本可以完成
last
如果我们不关心一次通过(或效率一般:))它是一个单线
def findPairInOrderN(x: Int, a: Array[Int]): Option[(Int, Int)] = {
def findPairLoop(x: Int, l: List[Int], r: List[Int]): Option[(Int, Int)] = {
if (l.head >= r.head) None
else if (l.head + r.head == x) Some((l.head, r.head))
else if (l.head + r.head > x) findPairLoop(x, l, r.tail)
else findPairLoop(x, l.tail, r)
}
val l = a.toList
findPairLoop(x, l, l.reverse)
}
将它展开到flatmap / map然后使用collectFirst给我们这个,这是相当整洁和更优化的(但仍然不是O(n)) - 它停在第一个正确的对,但做了更多的工作而不是到达那里
(for (m <-a ; n <- a if m + n == x) yield (m,n)).headOption
答案 2 :(得分:1)
如果没有递归并且没有可变状态,它可能会非常难看。这是我的尝试:
def f(l: List[Int], x: Int): Option[(Int, Int)] = {
l.foldLeft(l.reverse) {
(list, first) =>
list.headOption.map {
last =>
first + last match {
case `x` => return Some(first, last)
case sum if sum < x => list
case sum if sum > x =>
val innerList = list.dropWhile(_ + first > x)
innerList.headOption.collect {
case r if r + first == x => return Some(first, r)
}.getOrElse {
innerList
}
}
}.getOrElse {
return None
}
}
None
}
示例:
scala> f(List(1, 2, 3, 4, 5), 3)
res33: Option[(Int, Int)] = Some((1,2))
scala> f(List(1, 2, 3, 4, 5), 9)
res34: Option[(Int, Int)] = Some((4,5))
scala> f(List(1, 2, 3, 4, 5), 12)
res36: Option[(Int, Int)] = None
开头的.reverse
加上foldLeft
并在找到结果后返回O(2n)
。
答案 3 :(得分:1)
这是一个单线
[ (x, y) | x <- array, y <- array, x+y == n ]
它甚至适用于未排序的列表。
但是如果你想利用排序,只需对数组中的每个(n-x)
进行x
的二进制搜索,而不是通过数组。