我得到了一个阵列。我需要找到第一个元素大于最后一个元素的最大子数组的长度。 例如,5 4 3 2 1.最大子阵列的长度是5,因为第一元素5大于最后一个元素1。 另一个例子,5 10 4 7 9 8.长度再次为5,数组从10开始,直到最后一个元素。 我知道天真的方法,即O(n²),但需要更好的方法。
答案 0 :(得分:4)
这是线性算法 O(n):
首先收集以下为真的所有指数 i :
任何j&gt; i: i &lt;一个<子>Ĵ子>
所以它们有点极小,因为它们右边没有较小或相等的值。
保留对其中最左边的引用,调用该索引 j 。该变量将作为范围的结束指数(包括)。
现在从左侧开始索引 i ,并且只要 i &gt; a j ,转到下一个“最小值”(所以 j 增加),此条件保持不变( i &gt; a Ĵ子>)。对于最后的 j ,这可能是一个解决方案:验证这是否是目前为止发现的最长范围。
每当 j&lt;我,也接下一个“mimimum”
每当没有更多的最小值时,停止算法。
这个想法在这里用数组值的图形表示来表示,其中红色标记是最小值,绿色标记是解决方案子数组可以开始的可能候选者:
以下是JavaScript中该算法的实现:您可以输入数组值,并在键入时显示最大的子数组:
function longestSubArray(a) {
// Preprocessing: collect all i, for which holds:
// if j > i, then a[i] < a[j]
var minima = [-1] // Add a special value first (see later)
var last = Infinity;
for (var i = a.length - 1; i >= 0; i--) {
if (a[i] < last) {
last = a[i];
minima.push(i);
// Optimisation: It is of no use to find more minima if
// this value is less than the first value in the aay
if (last < a[0]) break;
}
}
// Get first value from minima. This will be the rightmost
// value that is less than a[0], or if such does not exist,
// the minimum value in the aay.
var j = minima.pop();
var maxSize = 1;
var maxStart = 0;
// Look for ranges that start at i:
for (i = 0; i < a.length; i++) {
// Check if range (i, j) fulfills the requirement
while (j !== -1 && (a[i] > a[j] || j <= i) ) {
// Check if range (i, j) is the largest so far
if (j - i + 1 > maxSize) {
maxSize = j - i + 1;
maxStart = i;
}
// Take an end index that is more to the right,
// but which will represent a higher value also:
j = minima.pop(); // could be -1: which means "all done"
}
if (j == -1) break;
}
if (maxSize == 1) return []; // no solution
return a.slice(maxStart, maxStart+maxSize);
}
// I/0 handling -- not relevant to the algorithm:
var input = document.querySelector('input');
var output = document.querySelector('span');
input.oninput = function () {
// Translate text input to aay of integers
var a = input.value.match(/-?\d+/g).map(Number);
// Apply function
var sub = longestSubArray(a);
// Output result
output.textContent = sub.join(' ');
}
Enter array: <input size="60"><br>
Longest sub-array: <span></span>
请参阅here了解Python中的相同功能。
该算法具有线性时间复杂度:
所以: O(n)。
答案 1 :(得分:3)
您可以尝试应用O(n):
的卡特彼勒方法这个想法是使用两个索引,一个用于头部,另一个用于尾部以及最大长度变量。然后,只要条件有效,就会尝试前进:从数组的头部到结尾的子序列中的最小值小于尾部的值。
如果你不能抬头,因为条件不满足,那么你就推进了尾巴。因此,与毛虫的相似之处在于它移动时抬起头部然后是尾部。
该最小值可以在阵列的前一次迭代中预先计算。
这是python中可能的实现:
def subSeqLen(arr):
head = 0
tail = 0
maxLength = 0
minFromLast = [arr[-1]]*len(arr)
for i in xrange(len(arr)-2, -1, -1):
minFromLast[i] = min(minFromLast[i+1], arr[i])
while head >= tail and head < len(arr):
if arr[tail]>arr[head] and head-tail+1 > maxLength:
maxLength = head-tail+1
if head < len(arr)-1 and minFromLast[head+1]<=arr[tail]:
head += 1
else:
tail += 1
head = max(head, tail)
return maxLength
答案 2 :(得分:2)
创建另一个对数组,使第一个元素与数组相同,第二个元素是元素的索引,因此对于数组:
5 4 3 2 1
新数组将是这样的
5,1 4,2 3,3 2,4 1,5
现在对对数组进行排序,所以它看起来像这样:
1,5 2,4 3,3 4,2 5,1
现在使用已排序数组的索引构建Segment tree。
现在遍历数组并且对于每个元素在排序数组中找到它是等价的(这可以在O(1)中完成),假设它的索引是j,知道在段[1,i]中执行范围查询(因为段[1,i]中的所有元素都低于当前元素,因为对数组被排序)以找到log(n)中的最大索引值,最后答案是所有段长度中的最大值
复杂性是O(nlog(n))。
答案 3 :(得分:1)
您可以使用两种算法来解决问题:
让我们看一些 j 并考虑所有有效的子数组,以 j -th位置的元素结尾。答案将是 i a [i]&gt; a [j] 和 i&lt; Ĵ即可。迭代所有这样的 i 会给我们 O(n 2 )复杂性。
为了提高复杂性,请记住一个简单的想法。如果我们 i 1 和 a [i 1 ] 那么我们永远不会考虑另一个 i <当 i 1 &lt; i 1 时,sub> 2 作为有效子阵列的起点i 2 和 a [i 1 ]&gt; a [i 2 ] 因为 i 1 会产生更好的结果。让我们在数据结构 d 中存储(a [i],i)这样的对。
在任何时间点 d 看起来像(a [i 0 ],i 0 ),(a [i 1 ],i 1 )...(a [i n ],i n )< / strong>即可。 i p 和 a [i p ] 从左向右增加。
当我们将下一个 j 视为有效子阵列的结尾时,我们可以在 d 中查找当前最佳答案。 d 中的所有 i -s都低于当前的 j 。我们必须寻找最左边的一对(a [i],i),其中 a [i]&gt; a [j] 将 j - i + 1 作为当前答案。由于对是排序的,我们可以使用二进制搜索。
如果 a [j] 大于 d 中的所有 a [i] ,那么我们会有一对新( a [j],j)将附加到 d 。
ans <- 0
d <- [] // contains (val, idx) structures
for j = 0..n-1
l <- 0, r <- len(d) - 1
while l <= r
mid = (l + r) / 2 // integer division
if d[mid].val > a[j]
r <- mid - 1
else
l <- mid + 1
if l = len(d)
append (a[j], j) to d
else
curAns <- j - d[l].idx + 1
ans <- max(ans, curAns)
总体复杂性为 O(n * log(n))。
查看Abdenaceur Lichiheb's answer
对 a 值的限制没有限制,因为它们可以很容易地排序,然后映射到排序数组中的索引。
答案 4 :(得分:0)
如果最大的意思是说长度,那么确实存在简单的解决方案。
输入-
第一行。
第二行n个数字。
代码-
int main(){
fast;
int l=0;
int n;
cin>>n;
int a[n];
for(int i=0;i<n;i++){
cin>>a[i];
}
int m=-IINF;
int l1=0;
for(int i=0;i<n;i++){
if(a[i]>m){
m=a[i];
maximize(l,l1);
l1=1;
}
else{
l1+=1;
//cout<<l1<<" ";
}
}
if(l1>l){
cout<<l1<<endl;
return 0;
}
cout<<l<<endl;
return 0;
}