我想知道为什么我对这个LeetCode“移动零点”问题的解决方案比大多数其他提交方案都慢。有没有更好的方法来解决这个问题以使其更快?
问题如下:
给出一个数组
onChange
,编写一个函数,将所有0移到其末尾,同时保持非零元素的相对顺序。您必须就地执行此操作,而不需要复制阵列。示例:
输入:[0,1,0,3,12]
输出:[1、3、12、0、0]
这是我的解决方案:
nums
LeetCode提供了这些统计信息:
运行时间:52毫秒,比“移动零点”的Swift在线提交的 40.50%快。
内存使用量:19.4 MB,不到“移动零点”的Swift在线提交的13.33%。
编辑1:
如果我按照以下方法处理问题,它不会在结尾处移动零,
编辑2:
答案 0 :(得分:2)
据我所见,可能其他提交者正在这样做
毫无疑问,这是一种合乎逻辑的方法,但是我想说,您只是选择挑战的基本需求并坚持下去。
答案 1 :(得分:1)
我个人会使用:
input = input.filter { $0 != 0 } + input.filter { $0 == 0 }
可以简化为一遍:
let nonZeros = input.filter { $0 != 0 }
input = nonZeros + Array(repeating: 0, count: input.count - nonZeros.count)
编辑:不创建新数组的最简单版本是气泡排序的某些原始版本,例如:
var numZeros = 0
// iterate array from start to end
for (offset, element) in input.enumerated() {
if element == 0 {
// count every zero
numZeros += 1
} else if numZeros > 0 {
// move every non-zero element left
input[offset - numZeros] = element
// replace with zero
input[offset] = 0
}
}
答案 2 :(得分:1)
使用removeAll
变异方法的Swift 4.2或更高版本:
更改输入:
class Solution {
func moveZeroes(_ nums: inout [Int]) {
var counter = 0
nums.removeAll {
if $0 == 0 {
counter += 1
return true
}
return false
}
nums += repeatElement(0, count: counter)
}
}
与Swift 4.1或更早版本相似的方法
func moveZeroes(_ nums: inout [Int]) {
var counter = 0
nums.indices.reversed().forEach {
if nums[$0] == 0 {
counter += 1
nums.remove(at: $0)
}
}
nums += repeatElement(0, count: counter)
}
var input = [0,1,0,3,12]
moveZeroes(&input)
input // [1, 3, 12, 0, 0]
非变异方法:
func moveZeroes(_ nums: [Int]) -> [Int] {
var counter = 0
return nums.filter {
if $0 == 0 { counter += 1 }
return $0 != 0
} + repeatElement(0, count: counter)
}
let input = [0,1,0,3,12]
let zerosMoved = moveZeroes(input)
zerosMoved // [1, 3, 12, 0, 0]
答案 3 :(得分:1)
另一种方法是半稳定分区算法。好处是可以交换项目,而不是将其删除并插入/添加。
半稳定表示保留分割点左侧的顺序。
extension Array {
mutating func halfStablePartition(indexes : IndexSet) { // code is O(n)
guard var i = indexes.first, i < count else { return }
var j = index(after: i)
var k = indexes.integerGreaterThan(i) ?? endIndex
while j != endIndex {
if k != j { swapAt(i, j); formIndex(after: &i) }
else { k = indexes.integerGreaterThan(k) ?? endIndex }
formIndex(after: &j)
}
}
}
var input = [0,1,0,3,12]
let indices = IndexSet(input.indices.filter{input[$0] == 0})
input.halfStablePartition(indexes: indices)
答案 4 :(得分:1)
这是适合您的36ms就地解决方案:
class Solution {
func moveZeroes(_ nums: inout [Int]) {
if nums.count < 2 {
return
}
var j = 0
while j < nums.count, nums[j] != 0 {
j += 1
}
if j < nums.count - 1 {
for i in j+1..<nums.count {
if nums[i] != 0 {
nums.swapAt(i, j)
j += 1
}
}
}
}
}
答案 5 :(得分:0)
一种快速的解决方案是将非零元素向左移动直到遇到的零数量:
func moveZeroes(_ nums: inout [Int]) {
var offset = 0
for i in 0..<nums.count {
if nums[i] == 0 { offset += 1 }
else { nums.swapAt(i, i-offset) }
}
}
此解决方案正好需要N步,并且在每一步我们都执行加法或交换,这两个步骤都非常快。
另一方面,您的解决方案需要两次迭代,导致2 * N步,这就是为什么它比其他解决方案要慢的原因。
答案 6 :(得分:0)
用于修改数组并将其保留:
O(n)表示时间复杂度
O(1)表示空间复杂度
我很想念这个。交换不为零的元素的最干净方法:
func moveZeroes(_ nums: inout [Int]) {
// amount of swaps, will be used a as reference for next swap index
var j = 0
for (i, e) in nums.enumerated() {
if e != 0 {
nums.swapAt(j, i)
j += 1
}
}
}