给定未排序的数组,找到指数之间的max
j - i
差异,j > i
中的a[j] > a[i]
和O(n)
。我能够在j
复杂度中使用琐碎的方法找到i
和O(n^2)
,但想知道如何在O(n)
中执行此操作?
输入:{9,2,3,4,5,6,7,8,18,0}
输出:8(j = 8,i = 0)
输入:{1,2,3,4,5,6}
输出:5(j = 5,i = 0)
答案 0 :(得分:32)
为了简洁起见,我将假设所有元素都是独一无二的。该算法可以扩展为处理非唯一元素的情况。
首先,请注意,如果x
和y
分别是您所需的最大和最小位置,那么就不能有a[i] > a[x]
和i > x
,同样,没有a[j] < a[y]
和j < y
。
因此,我们沿着数组a
扫描并构建数组S
,以便S[i]
保存a[0:i]
中最小元素的索引。类似地,数组T
保存a[n-1:i]
中最大元素的索引(即向后)。
现在我们可以看到a[S[i]]
和a[T[i]]
必然是递减序列,因为它们分别是i
的最小值和n
到i
的最小值
所以现在我们尝试进行类似合并的排序程序。在每一步,如果a[S[head]] < a[T[head]]
,我们会从T
中弹出一个元素,否则我们会从S
中弹出一个元素。在每个这样的步骤中,如果a[S[head]] < a[T[head]]
,我们记录S和T的头部差异。最大的差异可以为您提供答案。
编辑:这是Python实现算法的简单代码。
def getMaxDist(arr):
# get minima going forward
minimum = float("inf")
minima = collections.deque()
for i in range(len(arr)):
if arr[i] < minimum:
minimum = arr[i]
minima.append((arr[i], i))
# get maxima going back
maximum = float("-inf")
maxima = collections.deque()
for i in range(len(arr)-1,0,-1):
if arr[i] > maximum:
maximum = arr[i]
maxima.appendleft((arr[i], i))
# do merge between maxima and minima
maxdist = 0
while len(maxima) and len(minima):
if maxima[0][0] > minima[0][0]:
if maxima[0][1] - minima[0][1] > maxdist:
maxdist = maxima[0][1] - minima[0][1]
maxima.popleft()
else:
minima.popleft()
return maxdist
答案 1 :(得分:4)
让我们做这个简单的观察:如果我们有2个元素a [i],a [j],其中i <1。 j和a [i]&lt; a [j]然后我们可以确定j不会成为解决方案的一部分作为第一个元素(他可能是第二个元素,但这是第二个故事),因为我将是一个更好的选择。
这告诉我们的是,如果我们贪婪地建立一个从答案左边部分的元素逐渐减少的序列肯定会来自那里。
例如:12 3 61 23 51 2贪婪减少的序列是这样构建的:
12 - &gt; 12 3 - &gt;我们忽略61因为它比3还要差 - >我们忽略了23因为它比3还要差 - >我们忽略了51因为它比3还要差 - > 12 3 2。
所以答案将包含在左侧12 3或2。
现在在一个随机的情况下,它有O(log N)长度,所以你可以对每个元素进行二进制搜索作为答案的正确部分,你会得到O(N log log N)这是好的,如果你可以在一个随机的情况下在字符串的右边部分应用相同的逻辑,你可以得到O(log ^ 2 N + N(来自读数)),即O(N)。但我们也可以在非随机情况下做O(N)。
假设我们有这个递减的序列。我们从字符串的右边开始并执行以下操作,同时我们可以将最后一个递减序列与当前数字配对
1)如果我们通过采用最后一个递减序列和当前数字找到一个更好的解决方案而不是我们更新答案
2)即使我们更新了答案,我们也会弹出减少序列的最后一个元素,因为我们是完美的匹配(任何其他匹配将在左边,并给出一个较小的j - i的答案)
3)重复,同时我们可以配对这两个
示例代码:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int N; cin >> N;
vector<int> A(N + 1);
for (int i = 1; i <= N; ++i)
cin >> A[i];
// let's solve the problem
vector<int> decreasing;
pair<int, int> answer;
// build the decreasing sequence
decreasing.push_back(1);
for (int i = 1; i <= N; ++i)
if (A[i] < A[decreasing.back()])
decreasing.push_back(i); // we work with indexes because we might have equal values
for (int i = N; i > 0; --i) {
while (decreasing.size() and A[decreasing.back()] < A[i]) { // while we can pair these 2
pair<int, int> current_pair(decreasing.back(), i);
if (current_pair.second - current_pair.first > answer.second - answer.first)
answer = current_pair;
decreasing.pop_back();
}
}
cout << "Best pair found: (" << answer.first << ", " << answer.second << ") with values (" << A[answer.first] << ", " << A[answer.second] << ")\n";
}
后来编辑: 我看到你举了一个例子:我从1索引使其更清晰,我打印(i,j)而不是(j,i)。您可以根据需要更改它。
答案 2 :(得分:2)
要解决这个问题,我们需要得到两个arr []的最佳索引:左索引i和右索引j。对于元素arr [i],如果在arr [i]的左侧有一个小于arr [i]的元素,我们不需要考虑arr [i]左边的索引。类似地,如果arr [j]右侧有一个更大的元素,那么我们不需要将这个j视为右索引。因此我们构造了两个辅助数组LMin []和RMax [],使得LMin [i]保持arr [i]左侧的最小元素,包括arr [i],而RMax [j]保持右侧的最大元素arr [j]包括arr [j]。在构造这两个辅助数组之后,我们从左到右遍历这两个数组。如果我们看到LMin [i]大于RMax [j],则遍历LMin []和RMa []时,我们必须继续前进LMin [](或者做i ++),因为LMin [i]左边的所有元素都是大于或等于LMin [i]。否则我们必须在RMax [j]中继续前进以寻找更大的j-i值。这是在O(n)时间运行的c代码:
#include <stdio.h>
#include <stdlib.h>
/* Utility Functions to get max and minimum of two integers */
int max(int x, int y)
{
return x > y? x : y;
}
int min(int x, int y)
{
return x < y? x : y;
}
/* For a given array arr[], returns the maximum j – i such that
arr[j] > arr[i] */
int maxIndexDiff(int arr[], int n)
{
int maxDiff;
int i, j;
int *LMin = (int *)malloc(sizeof(int)*n);
int *RMax = (int *)malloc(sizeof(int)*n);
/* Construct LMin[] such that LMin[i] stores the minimum value
from (arr[0], arr[1], ... arr[i]) */
LMin[0] = arr[0];
for (i = 1; i < n; ++i)
LMin[i] = min(arr[i], LMin[i-1]);
/* Construct RMax[] such that RMax[j] stores the maximum value
from (arr[j], arr[j+1], ..arr[n-1]) */
RMax[n-1] = arr[n-1];
for (j = n-2; j >= 0; --j)
RMax[j] = max(arr[j], RMax[j+1]);
/* Traverse both arrays from left to right to find optimum j - i
This process is similar to merge() of MergeSort */
i = 0, j = 0, maxDiff = -1;
while (j < n && i < n)
{
if (LMin[i] < RMax[j])
{
maxDiff = max(maxDiff, j-i);
j = j + 1;
}
else
i = i+1;
}
return maxDiff;
}
/* Driver program to test above functions */
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6};
int n = sizeof(arr)/sizeof(arr[0]);
int maxDiff = maxIndexDiff(arr, n);
printf("\n %d", maxDiff);
getchar();
return 0;
}
答案 3 :(得分:2)
我们可以避免检查整个数组,从j-i
的最大差异开始,并比较arr[j]>arr[i]
所有可能的组合j和i的特定最大差异
每当我们得到(j,i)
与arr[j]>arr[i]
的组合时,我们就可以退出循环
示例:在
{2,3,4,5,8,1}
的数组中 第一个代码将检查最大差异5(5-0)
,即(arr[0],arr[5])
,如果arr[5]>arr[0]
函数将退出,则else将采用max diff4
(5,1) and (4,0) i.e arr[5],arr[1] and arr[4],arr[0]
的组合
int maxIndexDiff(int arr[], int n)
{
int maxDiff = n-1;
int i, j;
while (maxDiff>0)
{
j=n-1;
while(j>=maxDiff)
{
i=j-maxDiff;
if(arr[j]>arr[i])
{
return maxDiff;
}
j=j-1;
}
maxDiff=maxDiff-1;
}
return -1;
}`
答案 4 :(得分:1)
这是一个非常简单的O(n)Python实现的合并下行序列的想法。即使在重复值的情况下,实现仍然有效:
downs = [0]
for i in range(N):
if ar[i] < ar[downs[-1]]:
downs.append(i)
best = 0
i, j = len(downs)-1, N-1
while i >= 0:
if ar[downs[i]] <= ar[j]:
best = max(best, j-downs[i])
i -= 1
else:
j -= 1
print best
答案 5 :(得分:0)
我可以考虑改进O(n ^ 2),但需要在更坏的情况下验证这是否是O(n)。
- 创建变量
BestSoln=0;
并遍历第一个元素的数组 并存储第一个元素的最佳解决方案,即bestSoln=k;
。- 现在,对于第二个元素,只考虑
k
距离的元素 从第二个元素。- 如果
BestSol
n在这种情况下优于第一次迭代,则替换 否则就是这样。继续迭代其他元素。
如果我们为每个子阵列存储从i
到结尾的最大元素,则可以进一步改进。
这可以通过从末端遍历数组在O(n)中完成。
如果某个特定元素超过了它的局部最大值,则无需对该元素进行评估。
输入:
{9, 2, 3, 4, 5, 6, 7, 8, 18, 0}
为此数组创建本地最大数组:
[18,18,18,18,18,18,18,0,0] O(n).
现在,遍历数组9,这里最好的解决方案是i=0,j=8
。
现在对于第二个元素或之后,我们不需要评估。最佳解决方案是i=0,j=8
。
但假设数组是输入:
{19, 2, 3, 4, 5, 6, 7, 8, 18, 0,4}
局部最大数组[18,18,18,18,18,18,18,0,0]然后在第一次迭代中我们不需要评估,因为局部最大值小于当前元素。
现在进行第二次迭代,最佳解决方案是i=1,j=10
。现在对于其他元素,我们不需要考虑评估,因为它们无法提供最佳解决方案。
让我知道您对我的解决方案不适用的用例的看法。
答案 6 :(得分:0)
对于O(2n)速度和额外的~O(2n)空间(除输入数组外),这是一个非常简单的解决方案。以下实现在C:
中int findMaxDiff(int array[], int size) {
int index = 0;
int maxima[size];
int indexes[size];
while (index < size) {
int max = array[index];
int i;
for (i = index; i < size; i++) {
if (array[i] > max) {
max = array[i];
indexes[index] = i;
}
}
maxima[index] = max;
index++;
}
int j;
int result;
for (j = 0; j < size; j++) {
int max2 = 0;
if (maxima[j] - array[j] > max2) {
max2 = maxima[j] - array[j];
result = indexes[j];
}
}
return result;
}
第一个循环扫描数组一次,为每个元素找到其右边剩余元素的最大值。我们还将相对索引存储在单独的数组中。 第二个循环找到每个元素和对应的右侧最大值之间的最大值,并返回正确的索引。
答案 7 :(得分:0)
我的解决方案在O(log n)(如果我在计算这种复杂性时出错了,请在这里纠正我)时间......
想法是插入BST然后搜索节点,如果节点有一个正确的子节点,则遍历右子树以计算具有最大索引的节点。
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int t1 = Integer.parseInt(br.readLine());
for(int j=0;j<t1;j++){
int size = Integer.parseInt(br.readLine());
String input = br.readLine();
String[] t = input.split(" ");
Node root = new Node(Integer.parseInt(t[0]),0);
for(int i=1;i<size;i++){
Node addNode = new Node(Integer.parseInt(t[i]),i);
insertIntoBST(root,addNode);
}
for(String s: t){
Node nd = findNode(root,Integer.parseInt(s));
if(nd.right != null){
int i = nd.index;
int j1 = calculate(nd.right);
mVal = max(mVal,j1-i);
}
}
System.out.println(mVal);
mVal=0;
}
}
static int mVal =0;
public static int calculate (Node root){
if(root==null){
return -1;
}
int i = max(calculate(root.left),calculate(root.right));
return max(root.index,i);
}
public static Node findNode(Node root,int n){
if(root==null){
return null;
}
if(root.value == n){
return root;
}
Node result = findNode(root.left,n);
if(result ==null){
result = findNode(root.right,n);
}
return result;
}
public static int max(int a , int b){
return a<b?b:a;
}
public static class Node{
Node left;
Node right;
int value;
int index;
public Node(int value,int index){
this.value = value;
this.index = index;
}
}
public static void insertIntoBST(Node root, Node addNode){
if(root.value< addNode.value){
if(root.right!=null){
insertIntoBST(root.right,addNode);
}else{
root.right = addNode;
}
}
if(root.value>=addNode.value){
if(root.left!=null){
insertIntoBST(root.left,addNode);
}else{
root.left =addNode;
}
}
}
}
答案 8 :(得分:0)
来自Subhasis Das的简化算法答案:
# assume list is not empty
max_dist = 0
acceptable_min = (0, arr[0])
acceptable_max = (0, arr[0])
min = (0, arr[0])
for i in range(len(arr)):
if arr[i] < min[1]:
min = (i, arr[i])
elif arr[i] - min[1] > max_dist:
max_dist = arr[i] - min[1]
acceptable_min = min
acceptable_max = (i, arr[i])
# acceptable_min[0] is the i
# acceptable_max[0] is the j
# max_dist is the max difference
答案 9 :(得分:0)
Subhasis Das的简化版本使用辅助数组回答。
def maxdistance(nums):
n = len(nums)
minima ,maxima = [None]*n, [None]*n
minima[0],maxima[n-1] = nums[0],nums[n-1]
for i in range(1,n):
minima[i] = min(nums[i],minima[i-1])
for i in range(n-2,-1,-1):
maxima[i]= max(nums[i],maxima[i+1])
i,j,maxdist = 0,0,-1
while(i<n and j<n):
if minima[i] <maxima[j]:
maxdist = max(j-i,maxdist)
j = j+1
else:
i += 1
print maxdist
答案 10 :(得分:0)
以下是条件a[i] <= a[j]
的C ++解决方案。需要稍作修改以处理a[i] < a[j]
情况。
template<typename T>
std::size_t max_dist_sorted_pair(const std::vector<T>& seq)
{
const auto n = seq.size();
const auto less = [&seq](std::size_t i, std::size_t j)
{ return seq[i] < seq[j]; };
// max_right[i] is the position of the rightmost
// largest element in the suffix seq[i..]
std::vector<std::size_t> max_right(n);
max_right.back() = n - 1;
for (auto i = n - 1; i > 0; --i)
max_right[i - 1] = std::max(max_right[i], i - 1, less);
std::size_t max_dist = 0;
for (std::size_t i = 0, j = 0; i < n; ++i)
while (!less(max_right[j], i))
{
j = max_right[j];
max_dist = std::max(max_dist, j - i);
if (++j == n)
return max_dist;
}
return max_dist;
}
答案 11 :(得分:0)
请查看此解决方案以及可能失败的情况:
def maxIndexDiff(arr, n):
j = n-1
for i in range(0,n):
if j > i:
if arr[j] >= arr[i]:
return j-i
elif arr[j-1] >= arr[i]:
return (j-1) - i
elif arr[j] >= arr[i+1]:
return j - (i+1)
j -= 1
return -1
答案 12 :(得分:0)
int maxIndexDiff(int arr[], int n) {
// Your code here
vector<int> rightMax(n);
rightMax[n-1] = arr[n-1];
for(int i =n-2;i>=0;i--){
rightMax[i] = max(rightMax[i+1],arr[i]);
}
int i = 0,j=0,maxDis = 0;
while(i<n &&j<n){
if(rightMax[j]>=arr[i]){
maxDis = max(maxDis,j-i);
j++;
} else
i++;
}
return maxDis;
}
有保留 leftMin 和 rightMax 的概念,但 leftMin 并不是真正需要的,而且 leftMin 无论如何都会做这项工作。 我们正在选择 rightMax 并从头开始遍历直到我们得到一个比那个更小的值!
答案 13 :(得分:-1)
方法1 (简单但效率低下)
运行两个循环。在外循环中,从左侧逐个拾取元素。在内部循环中,将拾取的元素与从右侧开始的元素进行比较。当您看到一个大于拾取元素的元素时停止内循环并继续更新到目前为止的最大j-i。
#include <stdio.h>
/* For a given array arr[], returns the maximum j – i such that
arr[j] > arr[i] */
int maxIndexDiff(int arr[], int n)
{
int maxDiff = -1;
int i, j;
for (i = 0; i < n; ++i)
{
for (j = n-1; j > i; --j)
{
if(arr[j] > arr[i] && maxDiff < (j - i))
maxDiff = j - i;
}
}
return maxDiff;
}
int main()
{
int arr[] = {9, 2, 3, 4, 5, 6, 7, 8, 18, 0};
int n = sizeof(arr)/sizeof(arr[0]);
int maxDiff = maxIndexDiff(arr, n);
printf("\n %d", maxDiff);
getchar();
return 0;
}
时间复杂度:O(n ^ 2)
方法2(高效)
要解决这个问题,我们需要得到两个arr []的最佳索引:左索引i和右索引j。对于元素arr [i],如果在arr [i]的左侧有一个小于arr [i]的元素,我们不需要考虑arr [i]左边的索引。
类似地,如果arr [j]右侧有一个更大的元素,那么我们不需要将这个j视为右索引。因此我们构造了两个辅助数组LMin []和RMax [],使得LMin [i]保持arr [i]左侧的最小元素,包括arr [i],而RMax [j]保持右侧的最大元素arr [j]包括arr [j]。
在构造这两个辅助数组之后,我们从左到右遍历这两个数组。如果我们看到LMin [i]大于RMax [j],则遍历LMin []和RMa []时,我们必须继续前进LMin [](或者做i ++),因为LMin [i]左边的所有元素都是大于或等于LMin [i]。否则,我们必须在RMax [j]中继续前进以寻找更大的j - i值。
#include <stdio.h>
/* Utility Functions to get max and minimum of two integers */
int max(int x, int y)
{
return x > y? x : y;
}
int min(int x, int y)
{
return x < y? x : y;
}
/* For a given array arr[], returns the maximum j – i such that
arr[j] > arr[i] */
int maxIndexDiff(int arr[], int n)
{
int maxDiff;
int i, j;
int *LMin = (int *)malloc(sizeof(int)*n);
int *RMax = (int *)malloc(sizeof(int)*n);
/* Construct LMin[] such that LMin[i] stores the minimum value
from (arr[0], arr[1], ... arr[i]) */
LMin[0] = arr[0];
for (i = 1; i < n; ++i)
LMin[i] = min(arr[i], LMin[i-1]);
/* Construct RMax[] such that RMax[j] stores the maximum value
from (arr[j], arr[j+1], ..arr[n-1]) */
RMax[n-1] = arr[n-1];
for (j = n-2; j >= 0; --j)
RMax[j] = max(arr[j], RMax[j+1]);
/* Traverse both arrays from left to right to find optimum j - i
This process is similar to merge() of MergeSort */
i = 0, j = 0, maxDiff = -1;
while (j < n && i < n)
{
if (LMin[i] < RMax[j])
{
maxDiff = max(maxDiff, j-i);
j = j + 1;
}
else
i = i+1;
}
return maxDiff;
}
/* Driver program to test above functions */
int main()
{
int arr[] = {9, 2, 3, 4, 5, 6, 7, 8, 18, 0};
int n = sizeof(arr)/sizeof(arr[0]);
int maxDiff = maxIndexDiff(arr, n);
printf("\n %d", maxDiff);
getchar();
return 0;
}
时间复杂度:O(n)
辅助空间:O(n)
来源geeksforgeeks
答案 14 :(得分:-1)
我已经在这里解决了这个问题。
https://github.com/nagendra547/coding-practice/blob/master/src/arrays/FindMaxIndexDifference.java
也将代码放入此处。谢谢。
private static int findMaxIndexDifferenceOptimal(int[] a) {
int n = a.length;
// array containing minimums
int A[] = new int[n];
A[0] = a[0];
for (int i = 1; i < n; i++) {
A[i] = Math.min(a[i], A[i - 1]);
}
// array containing maximums
int B[] = new int[n];
B[n - 1] = a[n - 1];
for (int j = n - 2; j >= 0; j--) {
B[j] = Math.max(a[j], B[j + 1]);
}
int i = 0, maxDiff = -1;
int j = 0;
while (i < n && j < n) {
if (B[j] > A[i]) {
maxDiff = Math.max(j - i, maxDiff);
j++;
} else {
i++;
}
}
return maxDiff;
}