我正在尝试在给定数组中查找非零元素,并将零移到后面。应用过滤器方法有效。应用分区方法使我的数组不是可变问题。
我想知道使用过滤器和分区在时间复杂度以及空间复杂度方面的优势。通常最好使用哪个?
var inputArray = [1,4,0,0,5,1,0]
过滤方法
func NonZeroArrayWithFilter(array:[Int]) -> [Int] {
return array.filter({$0 > 0}) + array.filter({$0 == 0})
}
分区方法
func NonZeroArrayWithPartition(array:[Int]) -> [Int] {
return array.partition(by: { $0 > 0 }) + array.partition(by: { $0 == 0 })
}
答案 0 :(得分:1)
filter
返回符合您条件的元素数组; partition
对数组进行重新排序,以使所有符合条件的元素位于不符合条件的元素之前。例如:
var array = [1, 5, 2, 6]
array.filter { $0 < 4 } // returns [1, 2]
// reorders `array` to [1, 2, 5, 6], and returns 2:
// all elements before array[2] are smaller than 4
array.partition { $0 < 4 }
换句话说,partition
不返回新数组。
答案 1 :(得分:1)
我认为,最好的方法是创建自己的排序描述符。然后,您无需两次遍历数组。
let array = [1,2,0,6,0,2,5]
func nonZeroSortDescriptor(lhs: Int, rhs: Int -> Bool {
return rhs == 0
}
array.sorted(by: nonZeroSortDescriptor)
//1,2,6,2,5,0,0
UPD
@Alexander是正确的,这种复杂性将是O(2N + 1)
,所以这里是优化的排序方式:
func NonZeroArrayWithFilterFast(array:[Int]) -> [Int] {
var zerosCount = 0
return array.filter({
if $0 == 0 {
zerosCount += 1
}
return $0 > 0
}) + [Int](repeating: 0, count: zerosCount)
}
NonZeroArrayWithFilterFast(array: array)
此算法的复杂度为O(N)
答案 2 :(得分:1)
这是一个简短的解决方案:
array.sorted { $1 == 0 * $0 }
它是 O(n log n)。它很短,但不及您的过滤方法的 O(2 n)。
更有效的解决方案是:
func zeroTail(_ array:[Int]) -> [Int] {
guard !array.isEmpty else { return array }
var tempo = Array(repeating: 0, count: array.count)
var index = 0
array.forEach { element in
if element != 0 {
tempo[index] = element
index += 1
}
}
return tempo
}
zeroTail([0,1,2,0,6,0,2,5,0]) //[1, 2, 6, 2, 5, 0, 0, 0, 0]
它仅遍历一次数组: O(n)。
partition
是有效的,但是在这里不合适,因为它会交换belong in the second partition
的元素和不交换的元素。这就与保持非零元素的原始顺序的要求相矛盾。
您通常还可以在Array上定义扩展名:
extension Array where Element: Comparable {
func withTail(_ value: Element) -> [Element] {
guard !self.isEmpty else { return self }
var tempo: [Element] = Array(repeating: value, count: self.count)
var index: Int = 0
self.forEach { element in
if element != value {
tempo[index] = element
index += 1
}
}
return tempo
}
}
以下是一些用例:
[0, 1, 2, 0, 6, 0, 2, 5, 0].withTail(0) //[1, 2, 6, 2, 5, 0, 0, 0, 0]
["c", "a", "c", "b", "c", "d", "c"].withTail("c") //["a", "b", "d", "c", "c", "c", "c"]
[1.2, 2.3, 4.5, 1.2].withTail(6.0) //[1.2, 2.3, 4.5, 1.2]
[Character]().withTail("a") //[]
答案 3 :(得分:1)
请注意,partition
仅返回索引,而不是像filter
这样的数组。
partition
根据条件重新索引数组,它将在满足条件的元素之前排列所有不满足条件的元素。
因此,partition
将返回满足条件的元素的第一个索引。
您为partition
使用变异成员而遇到的错误将通过以下方式解决。
func NonZeroArrayWithPartition(array: [Int]) -> [Int] {
var array = array
// return array.partition(by: { $0 > 0 }) + array.partition(by: { $0 == 0 }) // your statement
_ = array.partition(by: {$0 == 0})
return array
}
您的语句被注释,因为它将通过Array.Index
(即数组的索引),而如果int
,则该语句应返回数组。使用此方法时,您可以这样声明,否则会得到警告。
let inputArray = [1,4,0,0,5,1,0]
这是同一件事的另一个解决方案。
var inputArray = [1,4,0,0,5,1,0]
...
NonZeroArrayWithPartition(array: &inputArray)
...
func NonZeroArrayWithPartition(array:inout [Int]) -> [Int] {
_ = array.partition(by: {$0 == 0})
return array
}
它将更改源数组(即通过引用调用)
以下是partition
的行为供您更好地理解。
var inputArray = [1,4,0,0,5,1,0]
print(inputArray)
print(NonZeroArrayWithFilter(array: inputArray))
print(inputArray)
print(NonZeroArrayWithPartition(array: &inputArray))
print(inputArray)
输出将如下所示
[1、4、0、0、5、1、0]
[1、4、5、1、0、0、0]
[1,4,0,0,5,1,0]
[1、4、1、5、0、0、0]
[1、4、1、5、0、0、0]
这里是partition
的{{3}}。
就个人而言,我建议对这种方法使用partition
函数。
因为filter函数将创建2个数组,您需要将它们连接起来,而分区只会重新索引该数组。
答案 4 :(得分:0)
filter
创建一个新数组,并保留原始数组。 partition(by:)
适用于就地数组。您需要使数组可变,或者需要使副本可变。例如:
extension MutableCollection where Self.Element: ExpressibleByIntegerLiteral & Equatable {
mutating func moveZerosToEnd() -> Self {
var copy = self
_ = copy.partition(by: { $0 == 0 })
return copy
}
}
var inputArray = [1,4,0,0,5,1,0]
print(inputArray.moveZerosToEnd())