【问题标题】:maximum number of consecutive in swiftswift中的最大连续数
【发布时间】:2018-12-21 16:35:28
【问题描述】:

我正在尝试通过解决 LeetCode 上的简单面试问题来学习 Swift。

问题如下:

给定一个二进制数组,求这个数组中连续1的最大个数。

示例 1:

输入: [1,1,0,1,1,1]

输出: 3

解释:前两位或后三位是连续的1。 连续 1 的最大数量为 3。

注意:

输入数组将只包含 0 和 1。 输入数组长度为正整数,不超过10000

我已经解决了以下问题,没问题,它运行并覆盖了测试用例,但它显示我的提交只比所有提交快 73%。我想知道是否有更好的方法来解决这个问题。我的解决方案时间复杂度是 O(n)。

class Solution {
    func findMaxConsecutiveOnes(_ nums: [Int]) -> Int {

        var globalMax = 0
        var localMax = 0

        for num in nums
        {
           if num == 1
           {
              localMax += 1
              globalMax = max(globalMax, localMax)
           }
           else
           {
                localMax = 0
           }  
        }
        return globalMax
    }
}

【问题讨论】:

  • codereview.stackexchange.com 可能是这个问题的更好归宿
  • 这里不是问题,但总的来说,使用isEmpty 比测试count == 0 更好。它使意图更加明确,如果您的类型不符合RandomAccessCollection,它实际上更有效。但正如其他人指出的那样,这里根本不需要检查。更实质性的观察是你不应该每次都打电话给maxnum == 1...
  • 说实话,这是一个很好的解决方案,复杂性是最好的。可能会实现一些优化,但总体改进可能是特定于机器的,以毫秒甚至更低的顺序。

标签: swift algorithm data-structures


【解决方案1】:

您的问题更适合Code Review site。 LeetCode 中的排名不可靠,可能会因提交而异(使用相同的代码)。

这里有一个避免if .. else的解决方案(java原代码here):

func findMaxConsecutiveOnes4(_ nums: [Int]) -> Int {
    var globalMax = 0
    var localMax = 0

    for num in nums {
        localMax *= num
        localMax += num
        globalMax = max(globalMax, localMax)
    }
    return globalMax
}

对于基准测试,我使用了以下代码:

let array = (0...10_000).map { _ in Int.random(in: 0..<2) }

let start1 = mach_absolute_time()
let x1 = findMaxConsecutiveOnes1(array)
let end1 = mach_absolute_time()
print("1st", x1, Double(end1 - start1)/Double(1e3), "us")

let start2 = mach_absolute_time()
let x2 = findMaxConsecutiveOnes2(array)
let end2 = mach_absolute_time()
print("2nd", x2, Double(end2 - start2)/Double(1e3), "us")

let start3 = mach_absolute_time()
let x3 = findMaxConsecutiveOnes3(array)
let end3 = mach_absolute_time()
print("3rd", x3, Double(end3 - start3)/Double(1e3), "us")

findMaxConsecutiveOnes 是您的解决方案,findMaxConsecutiveOnes2Joakim'sfindMaxConsecutiveOnes3 是此答案中提出的解决方案。

在终端中使用优化 (-O) 编译,执行时间如下:

  • findMaxConsecutiveOnes1:49.079 us
  • findMaxConsecutiveOnes2:54.016 us
  • findMaxConsecutiveOnes3:14.883 us

超过 100.00% 的 Swift 在线提交(目前)

以下实现被 LeetCode 认为是当前最快的:

func findMaxConsecutiveOnes(_ nums: [Int]) -> Int {

    guard !nums.isEmpty else {
        return 0
    }

    var globalMax = 0
    var localMax = -1

    for i in 0..<nums.count {
        if nums[i] != 1 { //It is faster than: if nums[i] == 0
            localMax = i
        }
        globalMax = max(globalMax, i - localMax)
    }
    return globalMax
}

Here 是原始的 C++ 实现。

在我的基准测试中,它没有达到预期的效果:33.615 us 在 10,000 个元素的数组上。

但是,在较小的阵列上,它被证明是最快的解决方案


更快的解决方案

这是最快的:

func findMaxConsecutiveOnes(_ nums: [Int]) -> Int {

    guard !nums.isEmpty else {
        return 0
    }

    var globalMax = 0
    var lastZeroIdx = -1

    for i in 0..<nums.count {
        if nums[i] == 0 {
            globalMax = max(lastZeroIdx == -1 ? i : i - lastZeroIdx - 1, globalMax)
            lastZeroIdx = i
        }
    }
    globalMax = max(lastZeroIdx == -1 ? nums.count : nums.count - lastZeroIdx - 1, globalMax)
    return globalMax
}

它是从this java 实现移植过来的。

在我的基准测试中,我没有注意到小(10 元素)和大(10,000 元素 -> 42.363 us)数组的执行时间有任何改进。

【讨论】:

  • 有意思,等于0不等于1的运算!为什么它会提高性能?
  • @hotspring 不等式运算符!= 在某些架构上比== 快。
【解决方案2】:

两个小调整,检查数组大小并不是真正需要的,只需要在找到 0 时调用 max 并结束

func findMaxConsecutiveOnes(_ nums: [Int]) -> Int {
    var globalMax = 0
    var localMax = 0

    for num in nums {
       if num == 1 {
          localMax += 1              
       } else {
            globalMax = max(globalMax, localMax)
            localMax = 0
       }  
    }
    return max(globalMax, localMax)
}

我做了一个拆分和减少的实验,这个组合似乎比上面的组合稍快,但与 cmets 中链接的组合大致相同

func findMaxConsecutiveOnes3(_ nums: [Int]) -> Int {
    return nums.split(separator: 0).reduce(ArraySlice<Int>(), { ($1.count > $0.count) ?  $1 : $0 }).count
}

【讨论】:

  • 我不喜欢reduce 解决方案,因为lazy.map { $0.count }.max ?? 0 可读性更强。
  • 显然,max(),不仅仅是max
  • @Rob 无论哪种方式,在我的终端优化测试 (-O) 中,功能方法的速度是迭代方法的 5 倍。我的基准测试中使用的数组如下let array = (0...10_000).map { _ in Int.random(in: 0..&lt;2) }.shuffled()
  • @Carpsen90 - 有趣,因为在我的电脑上它只慢了 25%。但我并不是要争辩说函数式比其他方法性能更高,而只是建议对 Joakim 和 Sulthan 提出的函数式模式进行可能的改进(就像我可能想知道为什么你会费心将一系列随机价值观,哈哈)。就个人而言,除非性能是可观察的和实质性的,否则这不是我选择使用函数式模式的决定因素。
  • @Rob 抱歉在这里回复有点晚了,使用omittingEmptySubsequences 的想法很棒但是现在我有时间阅读文档我看到参数默认为@987654330 @ 所以我不知道我已经利用了这个功能。
【解决方案3】:

也许他们期待一些 Swift 特有的技巧。

如果不是 - 我可以假设检查系统不喜欢每个 1 项的计算。

您只能在 1-0 转换(或数组末尾)的情况下计算全局最大值。在这种情况下,您还需要记住索引 1 以防 0-1 变化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-19
    • 2021-01-21
    • 2020-01-22
    • 1970-01-01
    • 2021-01-15
    相关资源
    最近更新 更多