二分搜索 O(log n) 算法在顺序列表中查找重复项?

Posted

技术标签:

【中文标题】二分搜索 O(log n) 算法在顺序列表中查找重复项?【英文标题】:Binary Search O(log n) algorithm to find duplicate in sequential list? 【发布时间】:2012-09-04 12:59:31 【问题描述】:

有谁知道在数字序列列表中查找重复项的比线性算法更快的算法吗?我现在正在使用 Java,但任何语言或伪代码都可以。

例如,给定这个 int[] 输入:

0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | 9

输出将是索引或值“7”。

我知道O(n) 线性时间的明显遍历,但我试图通过O(log n) 时间的二进制搜索来查看这是否可能。

【问题讨论】:

什么是明显的线性时间?您是否假设列表已排序? 你只有一个重复值? 听起来像是一个面试问题;) 如果列表是连续的,它是排序的。显而易见的方法是遍历列表并将列表 [n] 与列表 [n+1] 进行比较,这最多需要 O(n) 次比较。 不确定您所说的顺序是什么意思...如果您的意思是增加 1,请参阅 Peter 的回答(只需查找您看到的索引比您预期的更高的数字)。 【参考方案1】:

如果您假设数字必须从 0 开始并以 1 递增,您可以将中间值与索引进行比较。如果中间是一样的走高,如果中间不是,就走低。

这将为您提供二进制搜索时间 O(log2 N)。唯一的区别是您正在与索引进行比较,而不是与固定值进行比较。


public static void main(String... args) 
    int[] array = 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9;
    int duplicate = findDuplicate(array);
    System.out.println(duplicate);


private static int findDuplicate(int[] array) 
    int low = 0;
    int high = array.length - 1;

    while (low <= high) 
        int mid = (low + high) >>> 1;
        int midVal = array[mid];

        if (midVal == mid)
            low = mid + 1;
        else
            high = mid - 1;
    
    return high;

【讨论】:

如果“从 0 开始并不断增加”,您的意思是排序:P 这仍然失败,但有间隙:0 | 3 | 14 @sshannin 我的意思是写“增加1”:P 如果它是一个没有重复的顺序列表,中点应该始终与列表长度成正比,对吧?所以在第 n 次迭代中,中点应该等于 (length - (length / (n + 1)))?当第一次将我的列表示例减半时,在第一次迭代中,中点应该正好是 5,但它是 5.5 向下舍入到 5 - 这个只是因为后半部分有一个额外的重复,表明我们需要在后半部分查看。我只是不知道这在代码中会是什么样子,如果我想太多了? 我建议您尝试根据二进制搜索的代码编写一些代码,例如Arrays.binarySearch ;) 如果中间和索引一样是什么意思?索引是多少?【参考方案2】:

请注意,二分查找适用于排序列表。因此,如果您有一个包含重复项的排序列表,则只有在您的重复项相邻时,二分查找才有用。相邻的重要性在于,您可以在找到的密钥的上一个和下一个位置测试该密钥的存在。尝试对未排序列表使用二进制搜索的任何其他方式都会给出错误的结果。

这里有一些代码来说明我的意思。

import java.util.Arrays;
public class Main 
    public static void main(String[] args) 
        int[] list = 1, 2, 3, 4, 5, 6, 7, 7, 8, 9 ;
        int key = 7;
        int result = Arrays.binarySearch(list, key);
        System.out.println(result);
        if( list[result+1] == key  || list[result-1] == key )
                System.out.println("yes we have a duplicate.");
    

if 中的比较是 O(1) 我们仍然与二分查找的 O(logn) 相比。

【讨论】:

但是发帖人说他的列表是顺序/排序的……所以重复的会彼此相邻。 "(这里放宽一下列表的定义,因为数学列表不允许重复。)" 什么? @airza:太糟糕了!我在考虑列表的属性,但写的是集合的属性。实际上列表允许重复。已更正。感谢您指出这一点。【参考方案3】:
public class DuplicateNumber 

    public int findDuplicateNumber(List<Integer> numbers)

        int highestNumber = numbers.size() - 1;
        int total = getSum(numbers);
        int duplicate = total - (highestNumber*(highestNumber+1)/2);
        return duplicate;
    

    public int getSum(List<Integer> numbers)

        int sum = 0;
        for(int num:numbers)
            sum += num;
        
        return sum;
    

    public static void main(String a[])
        List<Integer> numbers = new ArrayList<Integer>();
        for(int i=1;i<30;i++)
            numbers.add(i);
        
        //add duplicate number into the list
        numbers.add(22);
        DuplicateNumber dn = new DuplicateNumber();
        System.out.println("Duplicate Number: "+dn.findDuplicateNumber(numbers));
    

【讨论】:

以上是关于二分搜索 O(log n) 算法在顺序列表中查找重复项?的主要内容,如果未能解决你的问题,请参考以下文章

七大查找算法

九大查找算法,值得收藏

算法----列表查找以及列表排序

算法——二分搜索树

算法图解1 - 二分查找和大O表示法

查找算法之顺序二分二叉搜索树红黑树 详细比较总结