用于检测在长列表中出现的对的算法

时间:2012-06-01 17:03:47

标签: performance algorithm list search count

给定N个(不必要的不​​同)数字的长序列,比如说

{1, 50, 3, 99, 1, 2, 100, 99, 4, 100, 4, 100} (could be very long)

和一小组M个有序对,比如说

(1, 2)

(2, 1)

(1, 3)    

(99, 50)

(99, 100)

我想检测有序对是否在列表中出现任何地方(它们可以分开,但顺序很重要)。例如,上面的计数是:

(1, 2): 2 (each 1 pairs with the later 2)

(2, 1): 0 (no 1's come after the 2)

(1, 3): 1 (only one of the 1's come before the 3)

(99, 50): 0 (no 99's come before the 50)

(99, 100): 5 (3 times for the first 99 and 2 times for the second)

假设有序对中的每个数字都保证出现在列表中,是否存在一种算法来提取这些计数的速度比天真的O(N * M)时间更快(通过强力搜索每个有序对来实现) ?

作为一个侧面问题,如果我们仅限于布尔出现而不是计数,那么可能会有一个快速算法吗?那就是:

(1, 2): yes

(2, 1): no

(1, 3): yes

(99, 50): no

(99, 100): yes

任何帮助都将不胜感激。

4 个答案:

答案 0 :(得分:5)

保留两个哈希值,一个映射数字到它们出现的最小位置,一个映射数字到它们出现的最大位置。有序对(a,b)按顺序出现,如果至少[a]<最大[b](并且两个散列键都存在)。预处理时间是线性的,空间使用是线性的,查询时间是恒定的(在关于散列复杂性的标准假设下)。

至于计数版本,我能想到的最好的方法是保持一个哈希将每个数字映射到它按排序顺序出现的位置。要查询一对,“合并”位置列表,跟踪到目前为止的a元素的数量和对的出现次数。当选择b元素作为下一个时,将对数增加a元素的数量。当选择a元素为next时,增加a元素的数量。 (如果a == b,返回长度选择2。)

答案 1 :(得分:1)

这是一个O(n)解决方案......

unordered_map<int, unordered_set<int>> pairs = ...;

void process(int n)
{
    // keep list of pairs that have their first seen, indexed by second...
    static unordered_map<int, vector<pair<int,int>>> live;

    // if next item is in live list, we have found some pairs...
    for (auto found_pair : live[n])
        process(found_pair);

    // add pairs to live list that have a first of the current item
    for (auto pair : pairs[n])
        for (auto second : pair.second)
           live.insert(second, make_pair(pair.first, second));
}

答案 2 :(得分:1)

您可以保留活动对的列表,并遍历数字列表。每当您找到一对中的第一个数字时,就将该对复制到活动列表中。每当您在活动列表中找到对中的第二个数字时,就会增加该对的计数。

C#中的示例:

public class Pair {

  public int First { get; private set; }
  public int Second { get; private set; }
  public int Count { get; set; }

  public Pair(int first, int second) {
    First = first;
    Second = second;
    Count = 0;
  }

}

int[] values = {1, 50, 3, 99, 1, 2, 100, 99, 4, 100, 4, 100};

List<Pair> pairs = new List<Pair>();
pairs.Add(new Pair(1, 2));
pairs.Add(new Pair(2, 1));
pairs.Add(new Pair(1, 3));
pairs.Add(new Pair(99, 50));
pairs.Add(new Pair(99, 100));

List<Pair> active = new List<Pair>();

foreach (int value in values) {
  foreach (Pair p in active) {
    if (p.Second == value) {
      p.Count++;
    }
  }
  foreach (Pair p in pairs) {
    if (p.First == value) {
      active.Add(p);
    }
  }
}

foreach (Pair p in pairs) {
  Console.WriteLine("({0},{1}) : Count: {2}", p.First, p.Second, p.Count);
}

输出:

(1,2) : Count: 2
(2,1) : Count: 0
(1,3) : Count: 1
(99,50) : Count: 0
(99,100) : Count: 5

改善思路:

  • 您可以使用Dictionary<int, List<Pair>>作为配对列表。
  • 您可以在对中添加有效计数,因此不会在活动列表中添加其他引用,而是增加活动计数。

答案 3 :(得分:-2)

假设所有数字都不同,您不认为蛮力解决方案是唯一的解决方案。