简体   繁体   English

高效算法在O(log(N))时间内找到排序数组中在一定范围内的整数数量?

[英]Efficient algo to find number of integers in a sorted array that are within a certain range in O(log(N)) time?

I came across a interview question that has to be done in O(logn) 我遇到了一个必须在O(logn)中完成的面试问题

Given a sorted integer array and a number, find the start and end indexes of the number in the array. 给定排序的整数数组和数字,找到数组中数字的开始和结束索引。

Ex1: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 3 --> Output = {3,6} 

Ex2: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 5 --> Output = {-1,-1} 

I am trying to find an efficient algo for this but so fat have not been successful. 我试图找到一个有效的算法,但这么胖也没有成功。

You can use the concept of binary search to find the starting and ending index: 您可以使用二进制搜索的概念来查找起始和结束索引:

  • To find the starting index, you halve the array, if the value is equal to or greater than the input number , repeat with the lower half of the array, otherwise repeat with the higher half. 要查找起始索引,请将数组减半,如果该值等于或大于输入数 ,则使用数组的下半部分重复,否则重复上半部分。 stop when you reached an array of size 1. 到达大小为1的数组时停止。
  • To find the starting index, you halve the array, if the value is greater than the input number , repeat with the lower half of the array, otherwise repeat with the higher half. 要查找起始索引,请将数组减半,如果值大于输入数 ,则使用数组的下半部分重复,否则重复上半部分。 stop when you reached an array of size 1. 到达大小为1的数组时停止。

Note that when we reached an array of size 1, we may be one cell next to the input number, so we check if it equals the input number, if not, we fix the index by adding/decreasing 1 from the index we found. 请注意,当我们到达大小为1的数组时,我们可能是输入数字旁边的一个单元格,因此我们检查它是否等于输入数字,如果不是,我们通过从我们找到的索引中添加/减少1来修复索引。

findStartIndex(int[] A, int num)
{
    int start = 0; end = A.length-1;

    while (end != start) 
    {
        mid = (end - start)/2;

        if (A[mid] >= num)
            end = mid;
        else
            start = mid;
    }

    if(A[start] == num) 
        return start;
    else 
        return start+1;
}

findEndIndex(int[] A, int num)
{
    int start = 0; end = A.length-1;

    while (end != start) 
    {
        mid = (end - start)/2;

        if (A[mid] > num)
            end = mid;
        else
            start = mid;
    }

    if(A[start] == num) 
        return start;
    else 
        return start-1;
}

And the whole procedure: 整个过程:

int start = findStartIndex(A, num);

if (A[start]!=num) 
{ 
     print("-1,-1"); 
}
else
{
     int end = findEndIndex(A, num);
     print(start, end);
}

Sounds like a binary search -- log graphs iirc represent the effect of "halving" with each increment, which basically is binary search. 听起来像二进制搜索 - 日志图表iirc表示每个增量“减半”的效果,基本上是二进制搜索。

Pseudocode: 伪代码:

Set number to search for
Get length of array, check if number is at the half point
if the half is > the #, check the half of the bottom half. is <, do the inverse
repeat
    if the half point is the #, mark the first time this happens as a variable storing its index
    then repeat binary searches above , and then binary searches below (separately), such that you check for how far to the left/right it can repeat.
    note*: and you sort binary left/right instead of just incrementally, in case your code is tested in a dataset with like 1,000,000 3's in a row or something

Is this clear enough to go from there? 这是否足够清楚?

The solution is to binary search the array concurrently (does't actually have to be concurrent :P ) at the start. 解决方案是在开始时同时二进制搜索数组(实际上不必是并发:P)。 The key is that the left and right searches are slightly different. 关键是左右搜索略有不同。 For the right side if you encounter a dupe you have to search to the right, and for the left side if you encounter a dupe you search to the left. 对于右侧,如果遇到欺骗,则必须向右搜索,对于左侧,如果遇到欺骗,则向左搜索。 what you are searching for is the boundary so on the right side you check for. 您要搜索的是边界,所以在右侧检查。

 yournum, not_yournum  

This is the boundary and on the left side you just search for the boundary in the opposite direction. 这是边界,在左侧,您只需搜索相反方向的边界。 At the end return the indices of the boundaries. 最后返回边界的索引。

Double binary search. 双二进制搜索。 You start with lower index = 0, upper index = length - 1. Then you check the point halfway and adjust your indexes accordingly. 您从较低的索引= 0开始,较高的索引=长度 - 1.然后您检查点中途并相应地调整您的索引。

The trick is that once you've found target, the pivot splits in two pivots. 诀窍在于,一旦找到目标,枢轴就会分成两个枢轴。

Since no one has posted working code yet, I'll post some (Java): 由于还没有人发布工作代码,我将发布一些(Java):

public class DuplicateNumberRangeFinder {
    public static void main(String[] args) {
        int[] nums = { 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 9 };
        Range range = findDuplicateNumberRange(nums, 3);

        System.out.println(range);
    }

    public static Range findDuplicateNumberRange(int[] nums, int toFind) {
        Range notFound = new Range(-1, -1);

        if (nums == null || nums.length == 0) {
            return notFound;
        }

        int startIndex = notFound.startIndex;
        int endIndex = notFound.endIndex;
        int n = nums.length;
        int low = 0;
        int high = n - 1;

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

            if (nums[mid] == toFind && (mid == 0 || nums[mid - 1] < toFind)) {
                startIndex = mid;
                break;
            } else if (nums[mid] < toFind) {
                low = mid + 1;
            } else if (nums[mid] >= toFind) {
                high = mid - 1;
            }
        }

        low = 0;
        high = n - 1;

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

            if (nums[mid] == toFind && (mid == n - 1 || nums[mid + 1] > toFind)) {
                endIndex = mid;
                break;
            } else if (nums[mid] <= toFind) {
                low = mid + 1;
            } else if (nums[mid] > toFind) {
                high = mid - 1;
            }
        }

        return new Range(startIndex, endIndex);
    }

    private static class Range {
        int startIndex;
        int endIndex;

        public Range(int startIndex, int endIndex) {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        public String toString() {
            return "[" + this.startIndex + ", " + this.endIndex + "]";
        }
    }
}

It may be error on my end, but Ron Teller's answer has an infinite loop when I've tested it. 这可能是我的错误,但是当我测试它时,Ron Teller的答案有一个无限循环。 Here's a working example in Java, that can be tested here if you change the searchRange function to not be static. 这是Java中的一个工作示例,如果将searchRange函数更改为不是静态的,可以在此处进行测试。

import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;

public class RangeInArray {
    // DO NOT MODIFY THE LIST
    public static ArrayList<Integer> searchRange(final List<Integer> a, int b) {
        ArrayList<Integer> range = new ArrayList<>();

        int startIndex = findStartIndex(a, b);

        if(a.get(startIndex) != b) {
            range.add(-1);
            range.add(-1);
            return range;
        }

        range.add(startIndex);
        range.add(findEndIndex(a, b));
        return range;
    }

    public static int findStartIndex(List<Integer> a, int b) {
        int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;

        while(lowerBound < upperBound) {
            midIndex = (upperBound + lowerBound) / 2;
            if(b <= a.get(midIndex)) upperBound = midIndex - 1;
            else lowerBound = midIndex + 1;
        }

        if(a.get(lowerBound) == b) return lowerBound;
        return lowerBound + 1;
    }

    public static int findEndIndex(List<Integer> a, int b) {
        int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;

        while(lowerBound < upperBound) {
            midIndex = (upperBound + lowerBound) / 2;
            if(b < a.get(midIndex)) upperBound = midIndex - 1;
            else lowerBound = midIndex + 1;
        }

        if(a.get(lowerBound) == b) return lowerBound;
        return lowerBound - 1;
    }

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(4);
        list.add(4);
        list.add(4);
        list.add(5);
        list.add(5);
        list.add(5);
        System.out.println("Calling search range");
        for(int n : searchRange(list, 2)) {
            System.out.print(n + " ");
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在O(log(N))时间内找到排序数组中在一定范围内的整数数? - How to find number of integers in a sorted array that are within a certain range in O(log(N)) time? 获取排序数组中属于log(n)时间的特定范围内的元素数 - Get number of elements in a sorted array that fall within a certain range in log(n) time 给定排序列表如何创建在 O(log(N)) 时间内不会落在某个范围内的整数列表? - Given sorted list how to create list of integers that wont fall in certain range in O(log(N)) time? 如何在排序数组上使用二进制搜索来查找特定范围内的整数数。 (有重复) - How to use a binarysearch on a sorted array to find the number of integers within a certain range. (with duplicates) 以O(log n)复杂度计算排序数组中值的出现次数 - Count the number of appearances of a value in a sorted array in O(log n) complexity 在 O(1) 时间内获取排序数组中给定范围内的元素总数 - Obtain the Total number of element in the Sorted Array that are within the given Range in O(1) time 在 O(n * log n) 时间内找到 Array 中加起来等于目标数的两个元素 - Find Two elements in the Array that add up to the Target number in O(n * log n) Time 给定一个+ ve整数数组,找到在O(n)时间和常数空间中出现奇数次的数字。 - Given an array of +ve integers, Find the number which occurs odd number of times in O(n) time & constant space. 在 O(n) 时间内在已排序的链表中查找重复项 - find duplicates in a sorted linkedlist in O(n) time 如何在O(n)中的排序数组中找到两个总和为给定数字的数字? - How to find two number whose sum is given number in sorted array in O(n)?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM