删除集合数组中的子集

时间:2015-05-29 09:11:26

标签: arrays swift set

是否有一种有效的方法从集合数组中删除子集

E.g。数组数组

[[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]]

输出数组

[[2, 3, 4, 7, 8, 9, 10], [1, 5, 6]]

5 个答案:

答案 0 :(得分:2)

关键是保证源集按大小的降序排序。这样,所有超集都在其子集之前。

这是一个通用的功能。您可以调整它以采用任何类型的hashable序列,并将它们转换为一组数组:

func removeSubsets<T: Hashable>(source: [Set<T>]) -> [Set<T>] {    
    let sets = source.sorted { $0.count > $1.count }
    var supersets: [Set<T>] = []
    for set in sets {
        if !contains(supersets, { set.isSubsetOf($0) }) {
            supersets.append(set)
        }
    }

    return supersets
}


removeSubsets([[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]])
// returns [{10, 2, 9, 4, 7, 3, 8}, {5, 6, 1}]

由于contains是线性的,所以它仍然是立方的,isSubsetOf也是如此。

编辑:这是完全通用的版本:

func removeSubsets
  <S0: SequenceType, S1: SequenceType 
   where S0.Generator.Element == S1, 
         S1.Generator.Element: Hashable>
  (source: S0) -> [Set<S1.Generator.Element>] 
{    
    let sets = map(source) { Set($0) }.sorted { $0.count > $1.count }
    var supersets: [Set<S1.Generator.Element>] = []
    for set in sets {
        if !contains(supersets, { set.isSubsetOf($0) }) {
            supersets.append(set)
        }
    }

    return supersets
}

let a: [[Int]] = [
    [2, 3, 4, 7, 8, 9, 10],
    [1, 5, 6], [3, 7, 10],
    [4, 8, 9], [5, 6],
    [7, 10], [8, 9],
    [6], [9]]

removeSubsets(a) // returns [{10, 2, 9, 4, 7, 3, 8}, {5, 6, 1}]

EDIT2:如果你希望结果是原始数组的数组(因为将它们转换为集合会丢失它们的顺序),你可以进行以下更改,这会占用更多空间,但也会稍微提高效率,因为它只会转换设置的超集,而不是子集:

func removeSubsets<T: Hashable>(source: [[T]]) -> [[T]] {
    // note, this is quite efficient since arrays are copy-on-write,
    // so it is only really creating a new array of pointers
    let sets = source.sorted { $0.count > $1.count }
    var supersets: [Set<T>] = []
    var result: [[T]] = []

    for set in sets {
        if !contains(supersets, { $0.isSupersetOf(set) }) {
            supersets.append(Set(set))
            result.append(set)
        }
    }

    return result
}


removeSubsets([[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]])
// returns [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6]]

EDIT3:如果你想保留集合的原始顺序(只删除子集),你可以在排序之前用途中的数字标记它们,然后使用它重新排序它们并将其剥离结果结束:

func removeSubsets<T: Hashable>(source: [[T]]) -> [[T]] {
    let sets = sorted(enumerate(source)) { $0.1.count > $1.1.count }
    var supersets: [Set<T>] = []
    var result: [(Int,[T])] = []

    for (n,set) in sets {
        if !contains(supersets, { $0.isSupersetOf(set) }) {
            supersets.append(Set(set))
            result.append(n,set)
        }
    }

    return result.sorted { $0.0 < $1.0 }.map { $1 }
}


// note, input not sorted in order of length
removeSubsets([[1, 5, 6], [2, 3, 4, 7, 8, 9, 10], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]])
// returns [[1, 5, 6], [2, 3, 4, 7, 8, 9, 10]]

答案 1 :(得分:0)

就像使用任何其他(非2D / set)数组一样,你可以像这样使用数组扩展......

extension Array
{
    func slice(indices:Int...) -> Array
    {
        var s = indices[0];
        var e = self.count - 1;
        if (indices.count > 1)
        {
            e = indices[1];
        }

        if (e < 0)
        {
            e += self.count;
        }

        if (s < 0)
        {
            s += self.count;
        }

        let count = (s < e ? e - s : s - e) + 1;
        let inc = s < e ? 1 : -1;
        var result = Array();

        var idx = s;
        for i in 0 ..< count
        {
            result.append(self[idx]);
            idx += inc;
        }

        return result;
    }
}

用法:

let a = [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]];
let b = a.slice(0, 1);
let c = a.slice(3);

答案 2 :(得分:0)

如果您的数组不包含重复的int值,您可以转换为Set以使用Swift中的某些功能:

(看一下执行集合操作) https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html

这是我的代码,以获取另一个不包含子集的数组。这种方法没有经过优化,但却有效。

//let arrayOfArray = [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]]

//use set instead
var setArray : [Set<Int>] = [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]]

setArray.sort({$0.count > $1.count}) //sort to have ordered array (biggest set at first)

var result = [Set<Int>]() //you will get your result in this variable.

for _aSet in setArray {
    var isSubSet = false
    for _exitSet in result {
        if _aSet.isSubsetOf(_exitSet) {
            isSubSet = true
            break;
        }
    }

    if (!isSubSet) {
        result.append(_aSet)
    }
}

答案 3 :(得分:0)

这是我能想到的最有效的方式:

let nArrays = [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]]

nArrays
  .reduce([Set<Int>]()) {
    accu, el in let setEl = Set(el)
    return contains(accu) {setEl.isSubsetOf($0)} ? accu : accu + [setEl]
  }


//[{10, 2, 9, 4, 7, 3, 8}, {5, 6, 1}]

不是检查每个数组是否是每个其他数组的子集,而是检查它们是否是已检查数组的子集。当然,它返回一个Sets数组,而不是一个Arrays数组,但你可以map()在它上面转换它:

let nArrays = [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]]

nArrays
  .reduce([Set<Int>]()) {
    accu, el in let setEl = Set(el)
    return contains(accu) {setEl.isSubsetOf($0)} ? accu : accu + [setEl]
  }
  .map{Array($0)}


//[[10, 2, 9, 4, 7, 3, 8], [5, 6, 1]]

答案 4 :(得分:-1)

你可以这样做:

let arrayOfArray = [[2, 3, 4, 7, 8, 9, 10], [1, 5, 6], [3, 7, 10], [4, 8, 9], [5, 6], [7, 10], [8, 9], [6], [9]]
let output = arrayOfArray[0...1]