如何二进制搜索Panel列表

时间:2016-05-02 22:34:32

标签: c# winforms arraylist binary-search

我有一个面板列表,按其y值排序。您可以通过这种方式查看my question from earlier有关其结构的具体细节。长话短说,这个List在0号位置有一个最高的面板,在位置1位于它下面的那个,等到最后一个位置的最后一个。我使用从我的链接问题改编的代码行访问每个面板的y坐标:

Panel p = panelList[someIndex];
int panelHeight = p.Top + p.Parent.Top - p.Parent.Margin.Top;
//The above line guarantees that the first panel (index 0) has y-coordinate 0 when scrolled all the way up,
//and becomes negative as the user scrolls down.
//the second panel starts with a positive y-coordinate, but grows negative after the user scrolls past the top of that page
//and so on...

我需要找到面板最接近到高度0的索引,因此我知道哪些面板当前处于打开状态,或者非常靠近页面。因此,我试图使用List.BinarySearch()方法,这是我被困的地方。我希望利用BinarySearch的属性来返回索引,如果它确实存在于列表中,那么该值将是。这样我就可以在高度0(我不希望找到)中搜索面板,但是找到离它最近的元素(例如在y = 24或y = -5的情况下),并且知道那是面板正在被渲染。

二进制搜索允许您指定IComparer来定义<和>操作,所以我写了这个课:

class PanelLocationComparer : IComparer<Panel>
{
    public int Compare(Panel x, Panel y)
    {
        //start by checking all the cases for invalid input
        if      (x == null && y == null) { return  0; }
        else if (x == null && y != null) { return -1; }
        else if (x != null && y == null) { return  1; }
        else//both values are defined, compare their y values
        {
            int xHeight = x.Top + x.Parent.Top - x.Parent.Margin.Top;
            int yHeight = y.Top + y.Parent.Top - y.Parent.Margin.Top;
            if (xHeight > yHeight)
            {
                return 1;
            }
            else if (xHeight < yHeight)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }
    }
}

这不起作用,我现在意识到这是因为比较两个面板大于或小于实际上并不关心我正在搜索的值,在这种情况下y值= 0。 有没有办法在IComparer中实现这一点,还是有办法使用内置的BinarySearch进行这种类型的搜索?

我考虑每次创建一个与Panel列表长度相同的新List,将y值复制到其中,然后在这个int列表中搜索0,但每次创建,搜索和销毁该列表他们滚动的时间会对性能造成太大影响,以至于它会破坏二分搜索的重点。

我的问题是also related to this one,但我无法弄清楚如何调整它,因为它们最终使用内置的比较方法,在这种情况下我无权访问。

1 个答案:

答案 0 :(得分:1)

不幸的是,内置的BinarySearch方法无法处理这种情况。他们所能做的就是搜索列表或可从列表中提取的内容。某些时候它们可以与假物品和适当的比较器一起使用,但这不适用于此。

另一方面,二进制搜索是一种非常简单的算法,因此您可以轻松地为您的特定情况创建一个,或者更好,创建一个自定义扩展方法,以便下次您不需要重复这样的事情:< / p>

public static class Algorithms
{
    public static int BinarySearch<TSource, TValue>(this IReadOnlyList<TSource> source, TValue value, Func<TSource, TValue> valueSelector, IComparer<TValue> valueComparer = null)
    {
        return source.BinarySearch(0, source.Count, value, valueSelector, valueComparer);
    }
    public static int BinarySearch<TSource, TValue>(this IReadOnlyList<TSource> source, int start, int count, TValue value, Func<TSource, TValue> valueSelector, IComparer<TValue> valueComparer = null)
    {
        if (valueComparer == null) valueComparer = Comparer<TValue>.Default;
        int lo = start, hi = lo + count - 1;
        while (lo <= hi)
        {
            int mid = lo + (hi - lo) / 2;
            int compare = valueComparer.Compare(value, valueSelector(source[mid]));
            if (compare < 0) hi = mid - 1;
            else if (compare > 0) lo = mid + 1;
            else return mid;
        }
        return ~lo; // Same behavior as the built-in methods
    }
}

然后简单地使用:

int index = panelList.BinarySearch(0,  p => p.Top + p.Parent.Top - p.Parent.Margin.Top);