[英]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: 您可以使用二进制搜索的概念来查找起始和结束索引:
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.