Arrays 从数组中查找项的最小计数,其中项的总和为X

Arrays 从数组中查找项的最小计数,其中项的总和为X,arrays,swift,algorithm,Arrays,Swift,Algorithm,我最近遇到了一个面试问题,你必须从一个数组中找到最少需要的项目,这个数组可以加在一起生成X值 例如: [1,9,2,5,3,10]目标是:13,=>应该是[10,3] 不能在数组外使用任何数字。所以[10,9,9,2]和20->[10,10]是无效的,但是[9,9,2]非常好 时间复杂性并不重要(但更高效的解决方案将受到欢迎) 允许的任何数字,包括负数 我试着对物品进行分类,一个接一个地从头部取出一些其他东西,但没有成功。尽管如此,我已经用一个不太好的解决方案解决了这个问题,我将把它作为一个

我最近遇到了一个面试问题,你必须从一个数组中找到最少需要的项目,这个数组可以加在一起生成
X

例如:

[1,9,2,5,3,10]
目标是:
13
,=>应该是
[10,3]

  • 不能在数组外使用任何数字。所以
    [10,9,9,2]
    20
    ->
    [10,10]
    是无效的,但是
    [9,9,2]
    非常好
  • 时间复杂性并不重要(但更高效的解决方案将受到欢迎)
  • 允许的任何数字,包括负数
我试着对物品进行分类,一个接一个地从头部取出一些其他东西,但没有成功。尽管如此,我已经用一个不太好的解决方案解决了这个问题,我将把它作为一个答案发布。我正在寻找一个更有效的解决方案

我最后想到的是算法:

  • 找到目标和的所有子集

  • 找到最小的子集


  • 首先,我们必须找到数组的所有子集:

    extension Array {
        var allSubsets: [[Element]] {
            guard count > 0 else { return [[]] }
    
            let tail = Array(self[1..<endIndex]
            let head = self[0]
            let withoutHead = tail.allSubsets
    
            let withHead = withoutHead.map { $0 + [head] }
    
            return withHead + withoutHead
        }
    }
    
    最后,通过其计数找到最小子集:

    validSubsets.reduce(array) { $0.count < $1.count ? $0 : $1 }
    
    validSubsets.reduce(数组){$0.count<$1.count?$0:$1}
    
    将其包装在函数中,将是:

    func minimumElements(in array: [Int], goal: Int) -> [Int] {
        let subsets = array.allSubsets
    
        let validSubsets = subsets.filter { subset in
            subset.reduce(0) { $0 + $1 } == goal
        }
    
        return validSubsets.reduce(array) { $0.count < $1.count ? $0 : $1 }
    }
    
    func最小元素(数组:[Int],目标:Int)->[Int]{
    设subsets=array.allSubsets
    设validSubsets=subsets.filter{subset in
    subset.reduce(0){$0+$1}==goal
    }
    返回有效子集。减少(数组){$0.count<$1.count?$0:$1}
    }
    

    注意我认为这不是很有效,如果有人能计算时间复杂度就好了,但由于问题提到复杂度无关紧要,这是一个有效的解决方案。

    首先我们必须找到数组的所有子集:

    extension Array {
        var allSubsets: [[Element]] {
            guard count > 0 else { return [[]] }
    
            let tail = Array(self[1..<endIndex]
            let head = self[0]
            let withoutHead = tail.allSubsets
    
            let withHead = withoutHead.map { $0 + [head] }
    
            return withHead + withoutHead
        }
    }
    
    最后,通过其计数找到最小子集:

    validSubsets.reduce(array) { $0.count < $1.count ? $0 : $1 }
    
    validSubsets.reduce(数组){$0.count<$1.count?$0:$1}
    
    将其包装在函数中,将是:

    func minimumElements(in array: [Int], goal: Int) -> [Int] {
        let subsets = array.allSubsets
    
        let validSubsets = subsets.filter { subset in
            subset.reduce(0) { $0 + $1 } == goal
        }
    
        return validSubsets.reduce(array) { $0.count < $1.count ? $0 : $1 }
    }
    
    func最小元素(数组:[Int],目标:Int)->[Int]{
    设subsets=array.allSubsets
    设validSubsets=subsets.filter{subset in
    subset.reduce(0){$0+$1}==goal
    }
    返回有效子集。减少(数组){$0.count<$1.count?$0:$1}
    }
    
    注意我认为这不是很有效,如果有人能计算时间复杂度就好了,但由于问题提到复杂度无关紧要,这是一个有效的解决方案。

    回溯 我能想到的最好的解决方案(从时间复杂性的角度来看)是回溯算法

    这与暴力非常相似。在最坏的情况下,它具有与暴力相同的时间复杂性。但它稍微好一点,因为它只检查有意义的组合

    理论 我们使用递归的
    visit
    函数来探索组合树

    每个组合都由一条从根到一片叶子的路径表示

    在这之前跟暴力没什么区别,对吧

    然而,我们的函数将足够聪明,当它构建的部分解决方案等于或大于目标值(在您的例子中为13)时,将停止探索树的分支

    对于某些输入,这个小东西使回溯比蛮力更好

    在最坏的情况下,回程将是一个缓慢而残酷的过程

    但是有一个问题! 感谢@MartinR指出当前的想法不适用于负数

    例如,给定此数组
    [1,1,1,5,-1]
    4
    作为目标值,算法将返回
    [1,1,1]
    作为最佳解决方案,而不考虑
    [5,-1]
    确实更好

    管理负数 为了管理负数,我添加了以下逻辑

    如果目标值为0或正数,则输入数组按升序排序(负数将放在第一位)

    所以
    [1,1,1,5,-1]
    将变成
    [-1,1,1,1,5]

    否则,如果目标为负,则输入数组将按降序排序

    所以
    [1,1,1,1,5,-1]
    将变成
    [5,1,1,1,1,-1]

    编码回溯 我能想到的最好的解决方案(从时间复杂性的角度来看)是回溯算法

    这与暴力非常相似。在最坏的情况下,它具有与暴力相同的时间复杂性。但它稍微好一点,因为它只检查有意义的组合

    理论 我们使用递归的
    visit
    函数来探索组合树

    每个组合都由一条从根到一片叶子的路径表示

    在这之前跟暴力没什么区别,对吧

    然而,我们的函数将足够聪明,当它构建的部分解决方案等于或大于目标值(在您的例子中为13)时,将停止探索树的分支

    对于某些输入,这个小东西使回溯比蛮力更好

    在最坏的情况下,回程将是一个缓慢而残酷的过程

    但是有一个问题! 感谢@MartinR指出当前的想法不适用于负数

    例如,给定此数组
    [1,1,1,5,-1]
    4
    作为目标值,算法将返回
    [1,1,1]
    作为最佳解决方案,而不考虑
    [5,-1]
    确实更好

    管理负数 为了管理负数,我添加了以下逻辑

    如果目标值为0或正数,则输入数组按升序排序(负数将放在第一位)

    所以
    [1,1,1,1,5,