二进制搜索未检测到重复项?

时间:2019-12-09 15:01:31

标签: java binary-search

我有一系列物品,卡片,都带有字符串名称,所以

Card c1= new card("TheCard")

Card c2= new card("TheOtherCard")

然后我使用快速排序对列表进行排序,然后尝试进行二进制搜索以在添加更多卡片之前查看卡片是否已经存在

所以

if(cards.contains(c3)==true)

//do nothing

else

cards.add(c3)

我的cards.contains方法是

Comparator<Card> c = new Comparator<Card>() {    
    @Override
    public int compare(Card u1, Card u2) { 
        return u1.getName().compareTo(u2.getName()); 
    } 
};
int index;
index = Collections.binarySearch(cards, it, c);
if (index == -1) {
    return false;
} else {
    return true;
}

但是问题是它正在搜索纸牌数组,找到不在列表中的纸牌并说它们在,并说列表中的纸牌不在

我正在尝试添加10,000张卡,其中8,000张是唯一的,但是contains方法将返回2,000张唯一的卡,当我检查列表时,它们甚至都不是唯一的https://i.imgur.com/N9kQtms.png

我尝试运行未排序的代码,仅返回大约4,000个结果,并且存在重复卡片的相同问题,当我蛮力使用仅使用基本.contains时,该方法有效,但速度非常慢

(也很抱歉,如果我弄乱了帖子中的内容,这是我第一次在此处发布)

2 个答案:

答案 0 :(得分:2)

javadoc声明以下内容:

  

使用二进制搜索算法在指定列表中搜索指定对象。 在进行此调用之前,必须根据指定的比较器(如sort(List,Comparator)方法)将列表按升序排序。如果未排序,则结果是不确定的。如果列表包含等于指定对象的多个元素,则不能保证将找到哪个元素。

它还声明它返回:

  

搜索关键字的索引(如果包含在列表中);否则为(-(插入点)-1)。 插入点定义为将键插入列表的点:第一个元素的索引大于键,或者如果列表中的所有元素都小于指定的键,则为list.size()。请注意,这保证了当且仅当找到键时,返回值才> = 0。

因此,您的列表应事先排序,否则不会返回任何有意义的内容。然后,您确实返回了元素的索引或插入点。当心这种技术性。执行后,您应该检查索引处的元素实际上是正确的元素,而不仅是要插入元素it的索引。

您可以进行此测试以查看是否是您的卡:

// Test if the card at the index found has got the same name than the card you are actually looking for.
return !index == cards.length && cards[index].getName().equals(it.getName()));

您还可以覆盖equals使其更接近:

return !index == cards.length && cards[index].equals(it);

在两种情况下,如果插入点位于列表的末尾,我们都将确保不会出现ArrayOutOfBoundException

答案 1 :(得分:0)

binarySearch在找到项目时会给出非负索引。

它提供插入位置的补码:〜index == -index-1(找不到时)。

  • 在b d e中搜索d得到2。
  • 从头开始搜索d可获得〜2 == -3,插入位置为2。

所以检查是:

int index = Collections.binarySearch(cards, it, c);
return index >= 0;

此外,卡应具有正确的相等性:

public class Card implements Comparable<Card> {

    ...

    @Override
    public int compareTo(Card other) {
        return name.compareTo(other.name);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceOf Card)) {
            return false;
        }
        Card other = (Card) obj;
        return name.equals(other.name);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

在这种情况下,您可以实现Comparable<Card>而不是比较器,因为该名称是卡的读取标识。比较器更适用于按姓氏+名字或名字+姓氏或城市对人进行排序。

hashCode允许使用HashMap<Card, ...>