【问题标题】:How to use Binary Search to find duplicates in sorted array?如何使用二分搜索在排序数组中查找重复项?
【发布时间】:2015-10-09 02:19:16
【问题描述】:

我试图通过重置高变量来扩展函数以通过二进制搜索查找整数匹配的数量,但它陷入了循环。我猜一种解决方法是复制这个函数来获取最后一个索引来确定匹配的数量,但我认为这不是一个优雅的解决方案。

从这里:

public static Matches findMatches(int[] values, int query) {
    int firstMatchIndex = -1;
    int lastMatchIndex = -1;
    int numberOfMatches = 0;

    int low = 0;
    int mid = 0;
    int high = values[values.length - 1];
    boolean searchFirst = false;

    while (low <= high){
        mid = (low + high)/2;

        if (values[mid] == query && firstMatchIndex == -1){
            firstMatchIndex = mid;

            if (searchFirst){
                high = mid - 1;
                searchFirst = false;
            } else { 
                low = mid + 1;
            }

        } else if (query < values[mid]){
            high = mid - 1;
        } else {
            low = mid + 1;
        }           
    }

    if (firstMatchIndex != -1) { // First match index is set
        return new Matches(firstMatchIndex, numberOfMatches);
    }
    else { // First match index is not set
        return new Matches(-1, 0); 
    }
}

到这样的事情:

public static Matches findMatches(int[] values, int query) {
    int firstMatchIndex = -1;
    int lastMatchIndex = -1;
    int numberOfMatches = 0;

    int low = 0;
    int mid = 0;
    int high = values[values.length - 1];
    boolean searchFirst = false;

    while (low <= high){
        mid = (low + high)/2;

        if (values[mid] == query && firstMatchIndex == -1){
            firstMatchIndex = mid;

            if (searchFirst){
                high = values[values.length - 1]; // This is stuck in a loop
                searchFirst = false;
            } 
        } else if (values[mid] == query && lastMatchIndex == -1){
            lastMatchIndex = mid;

            if (!searchFirst){
                high = mid - 1;
            } else { 
                low = mid + 1;
            }
        } else if (query < values[mid]){
            high = mid - 1;
        } else {
            low = mid + 1;
        }

    }

    if (firstMatchIndex != -1) { // First match index is set
        return new Matches(firstMatchIndex, numberOfMatches);
    }
    else { // First match index is not set
        return new Matches(-1, 0); 
    }
}

【问题讨论】:

  • 如何使用二分查找来查找给定数字的索引?假设如果找不到值则返回-1,您可以使用该索引来查找重复项的数量吗?例如。二进制搜索在搜索数字“9”时返回索引 5,所以我会在索引 5 的左右搜索重复项,一旦没有重复项就停止。匹配数将为rightIndex - leftIndex + 1,因为值数组已排序。

标签: java binary-search sorted


【解决方案1】:

除了先验排序之外,对数据一无所知是很困难的。 看到这个: Binary Search O(log n) algorithm to find duplicate in sequential list?

这将在已排序数组中找到 k 重复项的第一个索引。 当然,这与首先知道重复的值有关,但在知道时非常有用。

    public static int searchFirstIndexOfK(int[] A, int k) {

     int left = 0, right = A.length - 1, result = -1;
     // [left : right] is the candidate set.
     while (left <= right) {
       int mid = left + ((right - left) >>> 1); // left + right >>> 1;
       if (A[mid] > k) {
         right = mid - 1;
       } else if (A[mid] == k) {
         result = mid;
         right = mid - 1; // Nothing to the right of mid can be
                                               // solution.
      } else { // A[mid] < k
      left = mid + 1;
      }
     }
     return result;
    }

这将在 log(n) 时间内找到一个重复项,但很脆弱,因为数据必须排序以及增加 1 并在 1..n 范围内。

static int findeDupe(int[] array) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
    int mid = (low + high) >>> 1;
    if (array[mid] == mid) {
    low = mid + 1;

    } else {
    high = mid - 1;

    }

}
System.out.println("returning" + high);
return high;

}

【讨论】:

    【解决方案2】:

    在更正导致无限循环的重置高变量的错误后,我找到了解决方案。

    public static Matches findMatches(int[] values, int query) {
        int firstMatchIndex = -1;
        int lastMatchIndex = -1;
        int numberOfMatches = 0;
    
        int low = 0;
        int mid = 0;
        int high = values.length - 1;
    
        while (low <= high){
            mid = (low + high)/2;
    
            if (values[mid] == query && firstMatchIndex == -1){
    
                firstMatchIndex = mid;
                numberOfMatches++;
                high = values.length - 1;
                low = mid;
    
            } else if (values[mid] == query && (lastMatchIndex == -1 || lastMatchIndex != -1)){
    
                lastMatchIndex = mid;
                numberOfMatches++;
    
                if (query < values[mid]){
                    high = mid - 1;
                } else { 
                    low = mid + 1;
                }
    
            } else if (query < values[mid]){
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
    
        if (firstMatchIndex != -1) { // First match index is set
            return new Matches(firstMatchIndex, numberOfMatches);
        }
        else { // First match index is not set
            return new Matches(-1, 0); 
        }
    }
    

    【讨论】:

    • 这个程序的输出没有显示正确的结果。 (lastMatchIndex == -1 || lastMatchIndex != -1) 条件总是被评估为真。 @jruser2120512
    • @jruser2120512 你的代码没有显示正确的输出我的朋友。
    • @Dante 在该行,lastMatchIndex 将在找到 firstMatchIndex 后循环检查。我正在传递这些参数: int[] values = {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 9}; // 一个预先排序的整数数组。整数查询 = 8; // 要搜索的整数。我期望索引 9 和 3 匹配,这是我从我的代码中获得的。
    • @jruser2120512 用查询 5 试试这个 {1,2,3,3,3,4,5,5,5,5,6,7,7},它仍然给出错误的结果我朋友。
    • @Dante 我现在看到了。我使用这种持续的二分搜索的方法是行不通的。您的线性搜索解决方案似乎是最好的!
    【解决方案3】:

    我已将您的问题分为两部分 - 使用二进制搜索查找数字并计算匹配数。第一部分由 search 函数解析,第二部分由 findMatches 函数解析:

    public static Matches findMatches(int[] values, int query) {
    
        int leftIndex = -1;
        int rightIndex = -1;
        int high = values.length - 1;
    
        int matchedIndex = search(values, 0, high, query);
    
        //if at least one match
        if (matchedIndex != -1) {
    
            //decrement upper bound of left array
            int leftHigh = matchedIndex - 1;
            //increment lower bound of right array
            int rightLow = matchedIndex + 1;
    
            //loop until no more duplicates in left array
            while (true) {
    
                int leftMatchedIndex = search(values, 0, leftHigh, query);
    
                //if duplicate found
                if (leftMatchedIndex != -1) {
                    leftIndex = leftMatchedIndex;
                    //decrement upper bound of left array
                    leftHigh = leftMatchedIndex - 1;
                } else {
                    break;
                }
            }
    
            //loop until no more duplicates in right array
            while(true){
                int rightMatchedIndex = search(values, rightLow, high, query);
    
                //if duplicate found
                if(rightMatchedIndex != -1){
                    rightIndex = rightMatchedIndex;
                    //increment lower bound of right array
                    rightLow = rightMatchedIndex + 1;
                } else{
                    break;
                }
    
            }
    
            return new Matches(matchedIndex, rightIndex - leftIndex + 1);
    
        }
    
        return new Matches(-1, 0);
    
    }
    
    private static int search(int[] values, int low, int high, int query) {
    
        while (low <= high) {
            int mid = (low + high) / 2;
    
            if (values[mid] == query) {
                return mid;
            } else if (query < values[mid]) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
    
        return -1;
    
    }
    

    【讨论】:

    • @Gosu,我是否正确实现了您的算法?它看起来很复杂,可能会有所改进。
    【解决方案4】:

    你的代码有问题:

    high = values[values.length - 1];
    

    应该是

    high = values.length - 1;
    

    另外你不需要像 numberOfMatches 和 searchFirst 这样的变量,我们可以有相当简单的解决方案。

    现在问题来了,我明白你想要什么我认为二进制搜索适合这样的查询。

    完成所需的最佳方法是一旦找到匹配项,您只需从该索引向前和向后移动直到发生不匹配,这在计算 firstMatchIndex 和 numberOfMatches 时既优雅又高效。

    所以你的功能应该是:

    public static Matches findMatches(int[] values, int query) 
    {
     int firstMatchIndex = -1,lastMatchIndex=-1;
     int low = 0,mid = 0,high = values.length - 1;
     while (low <= high)
     {
          mid = (low + high)/2;
    
          if(values[mid]==query)
          {
              lastMatchIndex=mid;
              firstMatchIndex=mid;
              while(lastMatchIndex+1<values.length&&values[lastMatchIndex+1]==query)
               lastMatchIndex++;
              while(firstMatchIndex-1>=0&&values[firstMatchIndex-1]==query)
               firstMatchIndex--; 
              return new Matches(firstMatchIndex,lastMatchIndex-firstMatchIndex+1); 
          }
          else if(values[mid]>query)
           high=mid-1;
          else low=mid+1;
     }
     return new Matches(-1,0);
    }          
    

    【讨论】:

    • 感谢 Dante 的捕获!我看到你的线性方法。它可以在一些重复和小数组中很好地工作。我继续我的方法,并在下面找到了解决方案。
    【解决方案5】:

    难道你不能只使用像集合这样的东西来查找重复项吗?

    类似这样的:

    package example;
    
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    
    public class DuplicatesExample {
    
        public static void main(String[] args) {
            String[] strings = { "one", "two", "two", "three", "four", "five", "six", "six" };
            List<String> dups = getDups(strings);
            System.out.println("DUPLICATES:");
            for(String str : dups) {
                System.out.println("\t" + str);
            }
        }
    
        private static List<String> getDups(String[] strings) {
            ArrayList<String> rtn = new ArrayList<String>();
            HashSet<String> set = new HashSet<>();
            for (String str : strings) {
                boolean added = set.add(str);
                if (added == false ) {
                    rtn.add(str);
                }
            }
            return rtn;
        }
    
    }
    

    输出:

    DUPLICATES:
        two
        six
    

    【讨论】:

    • (您也可以从 getDups 方法返回一个集合以获取不同的重复项)
    猜你喜欢
    • 2020-11-01
    • 2012-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-08
    • 2015-09-28
    相关资源
    最近更新 更多