为什么binarySearch给我错误的索引?

时间:2014-09-27 22:05:36

标签: java binary-search

我按自然顺序对点数组(使用自定义Point类)进行排序,并保存对点的引用。然后我使用比较器再次对它进行排序,并使用binarySearch找到相同的点(确保指定当前顺序)。这是在循环的上下文中完成的,而binarySearch每隔一次迭代就给出一个不正确的索引。为什么呢?

import java.util.Arrays;

public class TestSearch {

    public static void main(String[] args) {

        Point[] pts = new Point[6];

        pts[0] = new Point(19000, 10000);
        pts[1] = new Point(18000, 10000);
        pts[2] = new Point(32000, 10000);
        pts[3] = new Point(21000, 10000);
        pts[4] = new Point(1234, 5678);
        pts[5] = new Point(14000, 10000);

        Arrays.sort(pts);
        for (int i = 0; i < pts.length-2; i++) {

            Point firstP = pts[i];    

            // Get a point from the array
            Point secondP = pts[i+1];
            System.out.println("The point I saved: " + secondP.toString());

            // Find that same point and return it
            // Why is this sometimes a different point?
            Arrays.sort(pts, firstP.SLOPE_ORDER);
            int secondI = Arrays.binarySearch(pts, secondP, firstP.SLOPE_ORDER);
            System.out.println("The found point: " + pts[secondI].toString());
            System.out.println("-----");

            Arrays.sort(pts);
        }
    }
}

这是前两次迭代的输出。为什么它有时会返回与我搜索的点不同的点?

The point I saved: (14000, 10000)
The found point: (14000, 10000)
-----
The point I saved: (18000, 10000)
The found point: (19000, 10000)
-----

如果有帮助的话,那就是自定义Point类。

import java.util.Comparator;

public class Point implements Comparable<Point> {

    // compare points by slope
    public final Comparator<Point> SLOPE_ORDER = new BySlope();

    private final int x;    // x coordinate
    private final int y;    // y coordinate

    // create the point (x, y)
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // slope between invoking point and end point
    public double slopeTo(Point end) {
        if (end == null)
            throw new java.lang.NullPointerException("Point object is null");
        if (this.equals(end)) 
            return Double.NEGATIVE_INFINITY;
        if (end.x - this.x == 0) 
            return Double.POSITIVE_INFINITY;
        return (double) (end.y - this.y) / (double) (end.x - this.x);
    }

    // is invoking point lexicographically smaller than second one?
    // comparing y-coordinates and breaking ties by x-coordinates
    public int compareTo(Point second) {
        if (this.y < second.y)
            return -1;
        if (this.y == second.y && this.x < second.x)
            return -1;
        if (this.y > second.y)
            return 1;
        if (this.y == second.y && this.x > second.x)
            return 1;
        return 0;
    }

    // return string representation of this point
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    private class BySlope implements Comparator<Point> {

        @Override
        public int compare(Point q1, Point q2) {
            if (q1 == null || q2 ==  null) 
                throw new java.lang.NullPointerException("Point object"
                        + "is null");
            if (Point.this.slopeTo(q1) < Point.this.slopeTo(q2)) 
                return -1;
            if (Point.this.slopeTo(q1) > Point.this.slopeTo(q2))
                return 1;
            return 0;
        }

    }
}

1 个答案:

答案 0 :(得分:0)

让我们把它剥掉一点。显然,第一个Arrays.sort调用与问题无关:binarySearch失败,只有在数组已经排序后才会调用。 “斜坡秩序”。

显然,问题在于你的ComparatorComparator本身没有任何问题,因为它看起来符合Comparator Javadoc中建立的合同。但是,它与equals不一致:可能有abcompare(a, b) == 0,但a.equals(b) == false。但是,由于二进制搜索只“看到”比较器建立的顺序,因此无法区分ab,因此它可能会返回“看起来像”其中任何一个的第一个元素。

您的问题是您通过比较器定义这些点ab等效,但仍然期望二进制搜索来区分它们并返回一个不仅等效的点,而且等于到您提供的点。尝试输出firstP.slopeTo(secondPt)firstP.slopeTo(pts[secondI]) - 这些值是否相等?

最后,一个解决方案是通过默认为Points的自然顺序来打破你的排序中的联系。也就是说,将return 0;中的BySlope.compare替换为return q1.compareTo(q2)