使用二进制搜索的多个键的最后索引
Last index of multiple keys using binary-search?
我有一个键在一个排序数组中多次出现,我想对它们执行二进制搜索,一个普通的二进制搜索返回一些随机索引的键有多次出现,而我想要的是该键的最后一次出现的索引。
int data[] = [1,2,3,4,4,4,4,5,5,6,6];
int key = 4;
int index = upperBoundBinarySearch(data, 0, data.length-1, key);
Index Returned = 6
这个答案中的Java实现查找第一个出现的键。关于如何将其更改为查找最后一个出现的注释,但是该建议会导致无限循环。不过,这个想法似乎很合理。
EDIT:经过一番研究,我在The Algo Blog上找到了一个简洁的解决方案。由于第一个找到的匹配不一定是需要的匹配,因此您需要跟踪到目前为止的"最佳"匹配。当你得到一个匹配,你存储它,并继续在该匹配(low = mid + 1
)的右边二进制搜索。
public static int binarySearch(int[] a, int key) {
return binarySearch(a, 0, a.length, key);
}
private static int binarySearch(int[] a, int fromIndex, int toIndex,
int key) {
int low = fromIndex;
int high = toIndex - 1;
int found = -1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
found = mid;
// For last occurrence:
low = mid + 1;
// For first occurrence:
// high = mid - 1;
}
}
return found;
}
这个改变保持了O(log n)
的复杂性。但是,实际性能取决于应用程序。当数组的长度远远大于所查找键的重复次数时,对最后一次出现的数组进行线性搜索可能会更快。但是,当存在大量重复时,这种修改后的二进制搜索可能更可取。
假设你想要一个O(log N)解决方案?(否则你可以直接进行线性搜索。)
在c++中,一种可能是使用std::upper_bound。这将给你一个迭代器,指向第一个大于你要求的元素,所以你需要检查前一个元素。这确实是O(log N)。
我不知道Java是否提供了一个标准库方法。然而,upper_bound
的伪代码在上面的链接中给出,并且应该很容易重新实现。
好吧,感谢所有人,尤其是@Mattias,这个算法听起来不错。无论如何,我已经完成了我自己的,似乎我给出了更好的结果,但如果有人能帮助我衡量出我和@Mattias算法的复杂性,或者任何一个有更好的解决方案,欢迎.....总之,这是我找到的解决问题的方法,
int upperBound(int[] array,int lo, int hi, int key)
{
int low = lo-1, high = hi;
while (low+1 != high)
{
int mid = (low+high)>>>1;
if (array[mid]> key) high=mid;
else low=mid;
}
int p = low;
if ( p >= hi || array[p] != key )
p=-1;//no key found
return p;
}
这是第一次出现,我也更新相同的另一个类似的帖子第一次出现在二进制搜索
int lowerBound(int[] array,int lo, int hi, int key)
{
int low = lo-1, high = hi;
while (low+1 != high)
{
int mid = (low+high)>>>1;
if (array[mid]< key) low=mid;
else high=mid;
}
int p = high;
if ( p >= hi || array[p] != key )
p=-1;//no key found
return p;
}
当你找到钥匙。不是返回它,而是对数组进行顺序搜索以获得最后一个。这是O(N)个解。
这是一个递归的版本的二进制搜索。稍微调整一下这个版本,你就会得到最后一个索引或第一个索引,而不需要付出任何努力,复杂度为O(log-n)。
最初的二进制搜索递归版本是这样的:
public static int binarySearch(List<Integer> a, int startIndex, int endIndex, int key) {
int midIndex = (endIndex - startIndex)/2 + startIndex;
if (a.get(midIndex) == key) // found!
return midIndex;
if (startIndex == endIndex || startIndex == endIndex - 1)
return -1;
else if (a.get(midIndex) > key) // Search in the left
return binarySearch(a, 0, midIndex, key);
else if (a.get(midIndex) < key) // Search in the right
return binarySearch(a, midIndex, endIndex, key);
else
return -1; // not found
}
对第一个if语句做一个小改动,就可以得到第一个索引:
public static int binarySearchLowIndex(List<Integer> a, int startIndex, int endIndex, int key) {
int midIndex = (endIndex - startIndex)/2 + startIndex;
if (a.get(midIndex) == key && a.get(midIndex - 1) != key) // found!
return midIndex;
if (startIndex == endIndex || startIndex == endIndex - 1)
return -1;
else if (a.get(midIndex) >= key) // Search in the left
return binarySearchLowIndex(a, 0, midIndex, key);
else if (a.get(midIndex) < key) // Search in the right
return binarySearchLowIndex(a, midIndex, endIndex, key);
else
return -1; // not found
}
最后一个索引也是如此:
public static int binarySearchHighIndex(List<Integer> a, int startIndex, int endIndex, int key) {
int midIndex = (endIndex - startIndex)/2 + startIndex;
if (a.get(midIndex) == key **&& a.get(midIndex + 1) != key**) // found!
return midIndex;
if (startIndex == endIndex || startIndex == endIndex - 1)
return -1;
else if (a.get(midIndex) > key) // Search in the left
return binarySearchHighIndex(a, 0, midIndex, key);
else if (a.get(midIndex) <= key) // Search in the right
return binarySearchHighIndex(a, midIndex, endIndex, key);
else
return -1; // not found
}
下面是一些测试示例(基于Junit):
@Test
public void binarySearchTest() {
assert(BinarySearch.binarySearch(Arrays.asList(5, 7, 7, 8, 8, 10), 0, 5, 5) == 0);
}
@Test
public void binarySearchLowIndexTest() {
assert(BinarySearch.binarySearchLowIndex(Arrays.asList(5, 8, 8, 8, 8, 10), 0, 5, 8) == 1);
}
@Test
public void binarySearchHighIndexTest() {
assert(BinarySearch.binarySearchHighIndex(Arrays.asList(5, 8, 8, 8, 8, 10), 0, 5, 8) == 4);
}
这是我使用递归的解决方案:
public static int upperBoundBinarySearch(List<Integer> arr, int left, int right, int target) {
if (left <= right) {
int m = (int) (left + Math.floor((right - left) / 2));
if (arr.get(m) == target) {
if (m == arr.size() || arr.get(m + 1) != target)
return m;
else
// Check the upper part only
return upperBoundBinarySearch(arr, m + 1, right, target);
}
// Normal Binary search
else if (arr.get(m) < target)
return upperBoundBinarySearch(arr, m + 1, right, target);
else
return upperBoundBinarySearch(arr, left, m - 1, target);
}
return -1;
}
在二进制搜索中,您将键与数组data[i]的元素进行比较。为了得到最后一个匹配索引,你应该改变你的比较函数,以便它给出不平等,即使关键是等于数据[i]和数据[i+1]。
int upperBoundBinarySearch(int data[],int start, int end, int key) {
while(start < end) {
int middle = start + (end-start)/2;
if (data[middle] == key && (middle == end || data[middle+1] != key))
return middle;
if (data[middle] > key)
end = middle;
else {
if (start == middle)
return start;
start = middle;
}
}
return start;
}
- 数组索引的值没有增加
- 读取文件的最后一行并输入到链接列表时出错
- 芬威克树(BIT).找到具有给定累积频率的最小索引,单位为 O(logN)
- CPP:如何获取字符的最后一个索引
- 我正在尝试将我的 c++ 数组从第二个索引切到最后一个索引
- "如何使用C++将字符串的第一个和最后一个索引返回到向量中?
- 查找数组的第一个和最后一个索引,其中 from 和 to 元素的顺序总和最大
- 数组:如何在指定索引之前显示数组的第一个和最后一个元素以及数组元素的差异?
- 数组 - 最后一个索引
- 在向量C++中查找特定的第一个和最后一个索引<float>
- 在C 字符串中,为什么在最后一个字符之后,通过索引和()访问索引时行为是不同的
- 将指针分配给字符串对象的第一个也是最后一个索引
- C++ C 样式字符串的最后一个索引
- 填充 c++ 数组时遇到问题,每个索引处只有最后一项
- 为什么我可以索引到数字超出范围的三维数组中,但仍然可以访问数组中的最后一个数字
- 在不使用图形的情况下,以最小的乘积从第一个索引到最后一个索引
- std::vector size()-1 ALWAYS给出最后一个元素的索引吗?
- 返回常量字符数组中字符最后出现的索引
- 查找数组中给定索引的第一个和最后一个字节的地址
- 使用二进制搜索的多个键的最后索引