如何获得二叉查找结果的第一个索引
本文关键字:第一个 索引 结果 查找 何获得 | 更新日期: 2023-09-27 18:15:50
我有一些问题。我有两个列表,如:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
⇒True result为:{1, 3, 0}
我想获得firstList中存在的secondList中的第一个数字索引。我使用list.BinarySearch()
,但结果是{2, 3, 0}
。
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]
您可以用BinarySearch逻辑替换IndexOf
,但是BinarySearch返回第一个匹配的元素索引,所以您不会得到最低的数字,IndexOf返回最低匹配的索引。
问题是,当列表包含重复的值时,如您的情况,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;
}
}
示例用法:
var result = secondList.Select(x => firstList.BinarySearchFirst(x)).ToList();
// { 1, 3, 0 }
c++有一个标准库函数,叫做lower_bound()
。
这是一个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;
}
对于没有找到的元素不会返回-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;
}
用法如下:
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));
当然,这主要是对大列表的好处,因为它具有O(Log2(N))而不是O(N)的复杂度。
遍历第二个数组并获取第一个数组元素的索引:
foreach (int item in secondList)
{
Console.WriteLine(firstList.IndexOf(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
测试 // 1, 3, 0, -1
Console.Write(String.Join(", ", result));