假设我有一个数组a [i]为0&lt; = i&lt; = n-1。我可以使用复杂度O(log n)的算法找到i,使得1 <= i <= n-2,a [i]&lt; = a [i + 1]并且a [i]&lt; = a [I-1]?也就是说,我可以在对数时间内找到局部最小值吗?
注意:我将问题(已多次更改)编辑为可以合理回答的问题。我删除了早期版本中出现的奇怪的结束条件,因为这个版本更简单但又不失一般性。
答案 0 :(得分:5)
首先,我们需要考虑如何定义局部最小值:
a[i] < a[i-1] and a[i] < a[i+1]
从这个条件,我们看到如果我们要在X / Y图上绘制数组(X =索引,Y =值),局部最小值将在谷值。因此,为确保存在局部最小值,我们必须保证斜率符号的变化(从减少到增加)。
如果您知道范围的端点斜率行为,则可以知道其中是否存在局部最小值。此外,您的数组必须具有从[0]到[1]的行为减少斜率符号,并且从[n-1]到[n]增加斜率符号,否则问题很简单。考虑:
a = [1,2,3,4,5] (increasing, increasing) a[0] is an LM
a = [5,4,3,2,1] (decreasing, decreasing) a[n] is an LM
a = [1,2,2,2,1] (increasing, decreasing) a[0] and a[n] are LMs
我认为这应该足以让你完成这个方法。
请注意,扩展此方法仅适用于唯一值,例如所有1的数组,除非您执行某些边缘大小写检测,否则它将不具有O(log n)运行时。
答案 1 :(得分:3)
除非您的数组有其他约束,否则在没有(至少)线性时间预处理的情况下,您无法在O(log n)中找到局部最小值,因为在最坏的情况下,您需要检查数组中的每个元素。正式地证明这个陈述并不困难,想法是为每个扫描方法构造这样的数组,这个方法将在构造数组的线性时间内工作。
例如,想象一下,如果您从头到尾进行大小为n
的数组的简单扫描:如果您的最小值位于n-1
- 位置,那么您将发现它仅在n-1
次迭代后O(n)
答案 2 :(得分:0)
它在O(log n)中类似于二进制搜索方法解决,但仅限于阵列中有一个局部最小值和不同数字的情况。您的数组必须类似于以下内容:
8 5 4 3 [1] 2 6 9
这是当地的最低限度。
检查边界。 如果a [0]&lt; a [1],a [1]是局部最小值。 如果a [n-1]> a [n],a [n]是局部最小值。 如果这些条件都不正确 - 开始分割:
检查[n / 2],如果[n / 2]> a [n / 2 + 1]然后是阵列右侧的局部最小值,否则在左侧。之后递归地解决问题。
答案 3 :(得分:0)
我无法反驳这个解决方案的工作方式,所以如果有人可以,我会很高兴。
考虑从1到n索引的数组'a'。
low = 1
high = n
mid = (low + high)/2
在不失一般性的情况下,我将假设数组值是不同的。 所以,在中间,a [mid - 1],a [mid],[mid + 1]可以像:
Case 1:
/\
Case 2:
\/
Case 3:
/
/
Case 4:
\
\
Case 5(boundary):
/
Case 6(boundary):
\
&amp; m = mid
可以使用以下条件检查每个案例:
1: a[m] > a[m-1] && a[m] > a[m+1]
2: a[m] < a[m-1] && a[m] < a[m+1]
3: a[m] > a[m-1] && a[m] < a[m+1]
4: a[m] < a[m-1] && a[m] > a[m+1]
我也会忽略解释边界情况:
第二种情况是[m]是局部最小值
的理想情况第三种情况:
左边总是有一个局部最小值,因此,设置h = m - 1并继续
第四种情况:
右边总是有一个局部最小值,因此,l = m + 1并继续
对于第一种情况,我可以继续前进: 我相信这是有效的,因为你减少你正在考虑的片段的唯一时间是当你确定缩小片段中存在局部最小值时,所以你搜索的片段总是会有一些局部最小值。
注意:这仅用于查找单个局部最小值而不是每个局部最小值。后一种情况需要O(n),因为你必须至少看一次整个数组。
答案 4 :(得分:0)
可以通过使用一种二分搜索在O(logn)
中解决问题。
这个技巧在http://www.dsalgo.com/2013/03/find-local-minima-in-array.html中得到了很好的解释。该网站提供了递归实施。
我实际上想出了另一个迭代实现如下。
#include <cstdio>
#include <vector>
using std::vector;
int SearchLocalMinima(const vector<int>& sspace) {
if (sspace.size() < 2)
return -1;
if (sspace.size() == 2)
return 0;
int L = 0, U = sspace.size() - 1;
while (L<U) {
int M = L + (U - L) / 2;
if (M - 1 == L) {
if (sspace[M] <= sspace[M + 1])
return M;
else
return M + 1;
} else {
if (sspace[M] <= sspace[M + 1])
U = M + 1;
else
L = M;
}
}
return -1;
}
int main() {
vector<int> values{64, 14, 52, 27, 71, 19, 63, 1, 16, 57};
printf("Local minima: %d\n", SearchLocalMinima(values));
return 0;
}
输出:Local minima: 7
答案 5 :(得分:-1)
编辑:仅当本地最小值由“&lt; =”而非“&lt;”定义时,此方法才有效。但是,用“&lt;”没有解决方案。所以我会把它保留在这里,注意它没有解决OP的(无法解决的)问题。
如果存在至少一个局部最小值,以下分而治之方法应该在O(log n)中找到局部最小值。
在每种情况下2. - 6.你继续使用一个元素,左边和右边有更高的元素(虽然不一定直接),或者在边界处。每次要考虑的元素数量减半时,它就是O(log n)。最后,您只考虑3个或更少的元素,其中最小数量是局部最小值。
答案 6 :(得分:-2)
我认为这是一个家庭作业问题,所以我正在回答。如果您正在寻找当地最低要求,则必须为您提供起始位置,因为您需要参考什么是“本地”。如果左侧的元素小于该元素,请转到该元素并再次尝试。否则,如果右边的元素小于当前元素,请右键再试一次。否则,当前元素是本地最小值。
编辑:我在阅读问题后添加了对O(log n)时间的要求。我会考虑满足这一要求的解决方案。
另一个编辑:我不认为这对于未排序数组的O(log n)的时间要求是不可能的,因为没有办法将问题分成两半。另一方面,对于排序数组有一个简单的O(1)解决方案:)