C# 如何获取二进制搜索的第一个索引&x27;结果如何?

C# 如何获取二进制搜索的第一个索引&x27;结果如何?,c#,linq,list,C#,Linq,List,我有一些问题。我有两个清单,例如: List<int> firstList = new List<int> { 1, 2, 2, 3, 5}; List<int> secondList = new List<int> { 2, 3, 1 }; List firstList=新列表{1,2,2,3,5}; List secondList=新列表{2,3,1}; ⇒ 真正的结果是:{1,3,0} 我想得到firstList中存在的secondLi

我有一些问题。我有两个清单,例如:

List<int> firstList = new List<int> { 1, 2, 2, 3, 5};

List<int> secondList = new List<int> { 2, 3, 1 };
List firstList=新列表{1,2,2,3,5};
List secondList=新列表{2,3,1};
⇒ 真正的结果是:
{1,3,0}

我想得到firstList中存在的secondList中的第一个数字索引。我使用了
list.BinarySearch()
但是结果是
{2,3,0}

list firstList=newlist{1,2,2,3,5};
  List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
  List<int> secondList = new List<int> { 2, 3, 1 };

  var output = secondList.Select(item => firstList.IndexOf(item)); // [1 , 3 , 0]
List secondList=新列表{2,3,1}; var output=secondList.Select(item=>firstList.IndexOf(item));//[1 , 3 , 0]
您可以用BinarySearch逻辑替换
IndexOf
,但是BinarySearch返回第一个匹配的元素索引,因此您不会得到最低的数字,IndexOf会返回最低的匹配索引。

List firstList=新列表{1,2,2,3,5};
List secondList=新列表{2,3,1};
var output=secondList.Select(item=>firstList.IndexOf(item));//[1 , 3 , 0]

您可以用BinarySearch逻辑替换
IndexOf
,但是BinarySearch返回第一个匹配的元素索引,因此您不会得到最低的数字,IndexOf会返回最低的匹配索引。

迭代第二个数组并获取第一个数组中元素的索引:

foreach (int item in secondList)
{
    Console.WriteLine(firstList.IndexOf(item));
}

迭代第二个数组并获取第一个数组中元素的索引:

foreach (int item in secondList)
{
    Console.WriteLine(firstList.IndexOf(item));
}

问题在于,当列表包含与您的情况相同的重复值时,
BinarySearch
方法将返回任何匹配值的索引(非确定性)

要获得所需的结果,可以创建并使用如下自定义扩展方法:

public static class ListExtensions
{
    public static int BinarySearchFirst<T>(this List<T> source, T item, IComparer<T> comparer = null)
    {
        if (comparer == null) comparer = Comparer<T>.Default;
        int index = source.BinarySearch(item, comparer);
        while (index > 0 && comparer.Compare(source[index], source[index - 1]) == 0)
            index--;
        return index;
    }
}

问题在于,当列表包含与您的情况相同的重复值时,
BinarySearch
方法将返回任何匹配值的索引(非确定性)

要获得所需的结果,可以创建并使用如下自定义扩展方法:

public static class ListExtensions
{
    public static int BinarySearchFirst<T>(this List<T> source, T item, IComparer<T> comparer = null)
    {
        if (comparer == null) comparer = Comparer<T>.Default;
        int index = source.BinarySearch(item, comparer);
        while (index > 0 && comparer.Compare(source[index], source[index - 1]) == 0)
            index--;
        return index;
    }
}

如果您有一个较大的
firstList
,因此必须使用
BinarySearch
尝试修改它:通过
BinarySearch
查找项目(不保证是最左边的项目),然后在读取相同项目的同时向左移动:

  List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };

  List<int> secondList = new List<int> { 2, 3, 1, 123 };

  var result = secondList
    .Select(item => firstList.BinarySearch(item))
    .Select(index => index < 0 ? -1 : Enumerable
       .Range(0, index + 1)     // we have to scan [0..index] at the worst case
       .Select(i => index - i)  // scan in reverse
       .TakeWhile(i => firstList[index] == firstList[i]) // take while items are the same
       .Last()); // finally, we want the last item

如果您有一个较大的
firstList
,因此必须使用
BinarySearch
尝试修改它:通过
BinarySearch
查找项目(不保证是最左边的项目),然后在读取相同项目的同时向左移动:

  List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };

  List<int> secondList = new List<int> { 2, 3, 1, 123 };

  var result = secondList
    .Select(item => firstList.BinarySearch(item))
    .Select(index => index < 0 ? -1 : Enumerable
       .Range(0, index + 1)     // we have to scan [0..index] at the worst case
       .Select(i => index - i)  // scan in reverse
       .TakeWhile(i => firstList[index] == firstList[i]) // take while items are the same
       .Last()); // finally, we want the last item

C++对此有一个标准库函数,称为

这是一个C#实现。如果要搜索大型集合,此选项非常有用:

public static int LowerBound<T>(IList<T> values, T target, int first, int last) 
    where T : IComparable<T>
{
    int left = first;
    int right = last;

    while (left < right)
    {
        int mid = left + (right - left) / 2;
        var middle = values[mid];

        if (middle.CompareTo(target) < 0)
            left = mid + 1;
        else
            right = mid;
    }

    return left;
}
public static int LowerBound(IList值、T目标、int first、int last)
其中T:i可比较
{
int左=第一;
int right=last;
while(左<右)
{
int mid=左+(右-左)/2;
var middle=数值[mid];
if(中间比较(目标)<0)
左=中+1;
其他的
右=中;
}
左转;
}
对于找不到的元素,它不会返回-1,因此要解决这个问题,我们可以这样包装它:

public static int LowerBoundOrMinusOne<T>(IList<T> values, T target, int first, int last)
    where T : IComparable<T>
{
    int result = LowerBound(values, target, first, last);

    if (result >= last || result < first || values[result].CompareTo(target) != 0)
        return -1;

    return result;
}
public static int LowerBoundOrMinusOne(IList值、T目标、int first、int last)
其中T:i可比较
{
int result=LowerBound(值、目标、第一个、最后一个);
如果(结果>=最后一个| |结果<第一个| |值[结果]。比较(目标)!=0)
返回-1;
返回结果;
}
以下是您如何使用它:

List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1 };

List<int> result = secondList
    .Select(value => LowerBoundOrMinusOne(firstList, value, 0, firstList.Count))
    .ToList();

Console.WriteLine(string.Join(", ", result));
List firstList=新列表{1,2,2,3,5};
List secondList=新列表{2,3,1};
列表结果=第二个列表
.Select(value=>lowerbounderminusone(firstList,value,0,firstList.Count))
.ToList();
Console.WriteLine(string.Join(“,”,result));

当然,这主要有利于大型列表,因为它的复杂性是O(Log2(N))而不是O(N)。

C++有一个标准的库函数来实现这一点

这是一个C#实现。如果要搜索大型集合,此选项非常有用:

public static int LowerBound<T>(IList<T> values, T target, int first, int last) 
    where T : IComparable<T>
{
    int left = first;
    int right = last;

    while (left < right)
    {
        int mid = left + (right - left) / 2;
        var middle = values[mid];

        if (middle.CompareTo(target) < 0)
            left = mid + 1;
        else
            right = mid;
    }

    return left;
}
public static int LowerBound(IList值、T目标、int first、int last)
其中T:i可比较
{
int左=第一;
int right=last;
while(左<右)
{
int mid=左+(右-左)/2;
var middle=数值[mid];
if(中间比较(目标)<0)
左=中+1;
其他的
右=中;
}
左转;
}
对于找不到的元素,它不会返回-1,因此要解决这个问题,我们可以这样包装它:

public static int LowerBoundOrMinusOne<T>(IList<T> values, T target, int first, int last)
    where T : IComparable<T>
{
    int result = LowerBound(values, target, first, last);

    if (result >= last || result < first || values[result].CompareTo(target) != 0)
        return -1;

    return result;
}
public static int LowerBoundOrMinusOne(IList值、T目标、int first、int last)
其中T:i可比较
{
int result=LowerBound(值、目标、第一个、最后一个);
如果(结果>=最后一个| |结果<第一个| |值[结果]。比较(目标)!=0)
返回-1;
返回结果;
}
以下是您如何使用它:

List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1 };

List<int> result = secondList
    .Select(value => LowerBoundOrMinusOne(firstList, value, 0, firstList.Count))
    .ToList();

Console.WriteLine(string.Join(", ", result));
List firstList=新列表{1,2,2,3,5};
List secondList=新列表{2,3,1};
列表结果=第二个列表
.Select(value=>lowerbounderminusone(firstList,value,0,firstList.Count))
.ToList();
Console.WriteLine(string.Join(“,”,result));

当然,这主要对大型列表有利,因为它的复杂性是O(Log2(N))而不是O(N)。

BinarySearch不保证第一个匹配,它用于搜索和插入排序的集合。是的,我得到了它。谢谢BinarySearch不保证第一个匹配,它用于搜索和插入已排序的集合。是的,我找到了。谢谢,如果第二个列表中的数字在第一个列表中不存在,我必须返回-1。我应该使用SingleorDefault?IndexOf将返回-1,继续并尝试现在如果第二个列表中的数字在第一个列表中不存在,我必须返回-1。我应该使用SingleorDefault?IndexOf将返回-1,如果没有元素,请继续尝试他想要的
-1
found@Ofiris:谢谢你,我明白了;只需对三值运算符进行一点修改,他需要
-1