C#-比较2个列表之间的项目

时间:2018-09-20 12:45:13

标签: c# algorithm

我有两个具有以下相同字段(但内容不同)的列表:

  • TriangleID
  • 周长(以像素为单位)

我的任务是提取周长差异小于固定阈值的三角形的

我想和 Linq 一起做。

3 个答案:

答案 0 :(得分:2)

这不是 Linq ,但重要的是收藏集的 size N)。在最差的情况下(所有相等的三角形),您必须返回所有可能的三角形对作为解决方案。我们有

 N * N 

对。如果您有N ~ 1e6个三角形,您将获得多达多达 1e12)对作为答案。对于现代个人计算机而言,这太多了(但是,对于超级计算机,您可以尝试解决问题)。

让我们假设您没有最坏的情况,并且您希望最多获得~ N对。您可以这样做(C#伪代码):

// Sort triangles by their perimeters
firstList.Sort((t1, t2) => t1.Perimeter.CompareTo(t2.Perimeter));

foreach (left in secondList) {
  //TODO: you have to implement BinarySearchIndex
  int from = firstList.BinarySearchIndex(left.Period - threshould);
  int to = firstList.BinarySearchIndex(left.Period + threshould);

  // Scan all triangles within borders
  for (int i = leftBorder; i <= rightBorder; ++i) {
    triangle right = firstList[i];

    // return pair if right and left are different triangles
    if (right.Id != left.Id)
      yield return Pair(left, right);
  } 
}    

时间复杂度是

O(N * log(N))  +  /* sorting */ + 
O(N * log(N))     /* foreach (N) * 
                     Binary search (log N) * 
                     for (1 - not the worst case) */ =
O(N * log(N)) 

答案 1 :(得分:0)

我假设可以返回一个Triangle并关联附近其他Triangle的集合,而不是返回一个对列表。

这个想法是对两个列表进行排序,然后遍历它们。第一个列表将遍历每个项目,并将周长与第二个列表中的项目进行比较。但并非第二个列表中的每个项目都需要检查。由于两个列表都已排序,因此您可以遍历第二个列表,直到不再在边界内找到边界为止。另一种节省时间的方法是根据与第一列表的周长之差来提高第二列表的起始索引。这留给读者练习。

public class Triangle
{
    public int TriangleId {get; set;}
    public int Perimeter {get; set;}
}

// Returns a dictionary, that each triangle has an associated list of other triangles
// with a perimeter within a specified distance. This list may be empty.
public Dictionary<Triangle, List<Triangle>> NearbyPerimeter(List<Triangle> primary, List<Triangle> compareList, int maxDistance)
{
    // sort ~ O(n log n)
    // The sort is required to make an orderly advance through both lists, otherwise
    // every element needs to be compared to every other element.
    var sorteda = primary.OrderBy(x => x.Perimeter);

    // Call ToList to allow indexing with []
    var sortedb = compareList.OrderBy(x => x.Perimeter).ToList();

    var results = new Dictionary<Triangle, List<Triangle>>();

    int minCompareIndex = 0;
    int compareCount = compareList.Count;

    // ~ O(n)
    foreach (var tprime in sorteda)
    {
        var neighbors = new List<Triangle>();

        // Add logic to advance minCompareIndex based on 
        // which is larger, tprime.Perimeter or sortedb[minCompareIndex].Perimeter

        int i =  minCompareIndex;
        var foundMatch = false;

        // Until the missing logic above is added, this is O(n) x O(n) so ~ O(n^2)
        while (i < compareCount)
        {
            var second = sortedb[i];
            if (Math.Abs(tprime.Perimeter - second.Perimeter) < maxDistance)
            {
                neighbors.Add(second);

                foundMatch = true;
            }
            else if (foundMatch)
            {
                break;
            }
            i++;
        }

        results.Add(tprime, neighbors);
    }

    return results;
}

答案 2 :(得分:0)

您可以使用Linq来做到这一点,但只需稍作改动-基本上,您需要的是两个集合的笛卡尔积,并对差异进行过滤。可以使用Join和始终为true的条件来获得笛卡尔积。

下面的代码应该可以解决问题(我假设列表包含名为Triangle的类;如果不是这种情况,请根据您的需要调整代码):

var results = list1.Join(list2,
        _ => true,
        _ => true,
        (t1, t2) => new { Triangle1 = t1, Triangle2 = t2})
    .Where(pair = > Math.Abs(pair.Triangle1.Perimeter - pair.Triangle2.Perimeter) < threshold)
    .Select(pair => new{/*…*/});