我正在尝试制作一个算法,找到将目标插入已排序数组的最佳位置。
目标是返回列表中存在的项目位置,否则返回它将保持列表排序的位置。
所以说我有一个清单:
0 1 2 3 4 5 6
---------------------------------
| 1 | 2 | 4 | 9 | 10 | 39 | 100 |
---------------------------------
我的目标项目为14
它应返回5
我目前有伪代码:
array = generateSomeArrayOfOrderedNumbers()
number findBestIndex(target, start, end)
mid = abs(end - start) / 2
if (mid < 2)
// Not really sure what to put here
return start + 1 // ??
if (target < array[mid])
// The target belongs on the left side of our list //
return findBestIndex(target, start, mid - 1)
else
// The target belongs on the right side of our list //
return findBestIndex(target, mid + 1, end)
我不确定此时要放什么。我尝试采用二分搜索方法来解决这个问题,但这是我在5次重写之后能想到的最好的方法。
答案 0 :(得分:8)
您的代码存在一些问题:
mid = abs(end - start) / 2
不 start
和end
之间的中间位置,它们之间的距离是一半(向下舍入到整数)。后来你使用它就像它确实是一个有效的索引:
findBestIndex(target, start, mid - 1)
它不是。你可能想在这里使用mid = (start + end) // 2
或其他东西。
你也错过了几个指数,因为你跳过了中期:
return findBestIndex(target, start, mid - 1)
...
return findBestIndex(target, mid + 1, end)
现在,您的基本情况也必须表达不同。一个好的候选人是条件
if start == end
因为现在你肯定知道你已经完成了搜索。请注意,您还应该考虑所有数组元素都小于target
的情况,因此您需要在最后插入它。
如果你以前从未做过二进制搜索,那么很难做到这一点。如果我进行二分搜索,我通常会使用以下模式:
lo, hi = 0, n // [lo, hi] is the search range, but hi will never be inspected.
while lo < hi:
mid = (lo + hi) // 2
if check(mid): hi = mid
else: lo = mid + 1
在check
是单调二元谓词的情况下(从某点开始,false
始终为true
,从该点开始为lo == hi
),在此循环之后{{1} }将是[0..n]
check(lo) == true
范围内的第一个数字。隐含地认为check(n)
是真的(这是这种方法的魔力之一)。
那么所有指数的true
单调谓词是什么,包括和在我们的目标位置之后,以及false
之前的所有位置?
如果我们考虑一下,我们想要找到数组中第一个大于目标的数字,所以我们只需插入它就可以了:我们很高兴:
lo, hi = 0, n
while lo < hi:
mid = (lo + hi) // 2
if (a[mid] > target): hi = mid
else: lo = mid + 1
return lo;
答案 1 :(得分:1)
你走在正确的轨道上。
首先,mid = abs(end + start) / 2
假设abs在这里表示绝对值,因为end应该始终不小于start,除非你的代码中有一些错误。所以这里绝对没有帮助,但可能隐藏你的问题使其难以调试。
你也不需要if (mid < 2)
部分,没有什么特别关于mid小于2。
array = generateSomeArrayOfOrderedNumbers()
int start = 0;
int end = array.size();
int findBestIndex(target, start, end){
if (start == end){ //you already searched entire array, return the position to insert
if (stat == 0) return 0; // if it's the beginning of the array just return 0.
if(array[start] > target) return start -1; //if last searched index is bigger than target return the position before it.
else return start;
}
mid = (end - start) / 2
// find correct position
if(target == array[mid]) return mid;
if (target < array[mid])
{
// The target belongs on the left side of our list //
return findBestIndex(target, start, mid - 1)
}
else
{
// The target belongs on the right side of our list //
return findBestIndex(target, mid + 1, end)
}
}
答案 2 :(得分:1)
我通过计算严格小于(&lt;)的元素数量来解决这个问题。检索到的计数是插入位置。这是Java中随时可用的实现:
int binarySearchCount(int array[], int left, int right, int key) {
if(left > right) {
return -1; // or throw exception
}
int mid = -1; //init with arbitrary value
while (left <= right) {
// Middle element
mid = (left + right) / 2;
// If the search key on the left half
if (key < array[mid]) {
right = mid - 1;
}
// If the search key on the right half
else if (key > array[mid]) {
left = mid + 1;
}
// We found the key
else {
// handle duplicates
while(mid > 0 && array[mid-1] == array[mid]) {
--mid;
}
break;
}
}
// return the number of elements that are strictly smaller (<) than the key
return key <= array[mid] ? mid : mid + 1;
}
答案 3 :(得分:1)
这是我用过的代码:
int binarySearch( float arr[] , float x , int low , int high )
{
int mid;
while( low < high ) {
mid = ( high + low ) / 2;
if( arr[mid]== x ) {
break;
}
else if( arr[mid] > x ) {
high=mid-1;
}
else {
low= mid+1;
}
}
mid = ( high + low ) / 2;
if (x<=arr[mid])
return mid;
else
return mid+1;
}
重点是,即使低位等于高,你也必须检查。
例如,请参阅此示例: 0.5-&GT; 0.75 而你正在寻找0.7或1的真实位置。
在两种情况下,当走出while循环时:low = high = 1 但其中一个应放在位置1,另一个放在位置2。
答案 4 :(得分:1)
下面是用于从排序后的数组(包含重复值)中搜索目标值(它是数组的列表)的代码。
它返回可以插入目标值的位置数组。
希望此代码能以任何方式为您提供帮助。
欢迎提出任何建议。
componentWillUnmount
答案 5 :(得分:0)
这是通过使用 python 调整二进制搜索的解决方案。
def func(x, y):
start = 0
end = len(x)
while start <= end:
mid = (start + end)//2
print(start, end, mid)
if mid + 1 >= len(x):
return mid + 1
if x[mid] < y and x[mid + 1] > y:
return mid + 1
elif x[mid] > y:
end = mid - 1
else:
start = mid + 1
return 0
func([1,2,4,5], 3)
答案 6 :(得分:-2)
该算法对数组进行二进制搜索,其复杂度为O(log n)。
start
和end
是数组第一个和最后一个元素的索引。 mid
是start
和end
之间的索引。
第一个if
语句检查我们尝试插入的元素是否恰好位于mid
和mid
之后的元素之间。
如果mid
之后的元素不在数组的末尾,则在这种情况下,我们选择数组的最后一个元素进行比较。此检查很有用,因为在某些情况下,我们可能会在所有log(N)次迭代之前找到目标位置。
如果目标位置不是恰好在mid + 1
上,则如果新元素的值大于mid
,则在array[mid]
的右侧继续搜索,否则在左侧。
当start
和end
相遇时,表示找到了解决方案。
以下是C#中的实现:
private static int FindInsertionIndex(int[] array, int newElement)
{
int start = 0,
end = array.Length,
mid = (start + end) / 2;
while (start != end)
{
if (newElement >= array[mid] && newElement <= array[Math.Min(mid + 1, array.Length - 1)])
{
start = mid + 1;
end = start;
}
else if (newElement > array[mid])
{
start = mid + 1;
mid += (end - mid) / 2;
}
else
{
end = mid;
mid = start + (end - start) / 2;
}
}
return start;
}