我遇到了一个需要在O(logn)时间复杂度内完成的面试问题。
给定一个已排序的整数数组和一个数字,找出该数字在数组中的起始和结束索引。
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}
我试图找到一个高效的算法,但目前为止还没有成功。
我遇到了一个需要在O(logn)时间复杂度内完成的面试问题。
给定一个已排序的整数数组和一个数字,找出该数字在数组中的起始和结束索引。
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}
我试图找到一个高效的算法,但目前为止还没有成功。
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;
}
整个过程如下:
int start = findStartIndex(A, num);
if (A[start]!=num)
{
print("-1,-1");
}
else
{
int end = findEndIndex(A, num);
print(start, end);
}
听起来像是二分查找 -- 我记得对数图表示每次增加时的"折半"效果,基本上就是二分查找。
伪代码:
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
解决方案是同时(实际上不必并行:P)在开头对数组进行二分搜索。关键在于左侧和右侧的搜索略有不同。对于右侧,如果遇到重复项,则必须向右搜索;对于左侧,如果遇到重复项,则必须向左搜索。您要搜索的是边界,因此在右侧,您需要检查以下内容。
yournum, not_yournum
这是边界,左侧您只需朝相反方向搜索边界。最后返回边界的索引。
可能是我的问题,但 Ron Teller 的答案在我测试时有一个无限循环。这里有一个在 Java 中的工作示例,如果您将 searchRange
函数更改为非静态函数,则可以在此处进行测试 here。
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 + " ");
}
}
}
由于还没有人发布可用的代码,我来发布一些(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 + "]";
}
}
}