47

给定一个未排序的数组,找出和in的max j - i索引之间的差异。我能够找到并使用复杂的琐碎方法,但想知道如何做到这一点?j > ia[j] > a[i]O(n)jiO(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)

4

15 回答 15

40

为简洁起见,我将假设所有元素都是独一无二的。该算法可以扩展到处理非唯一元素的情况。

首先,观察如果xy分别是您想要的最大和最小位置,则不能有任何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]]必然是递减序列,因为它们分别是从until 到的最小值i和最大值。ni

所以现在我们尝试做一个类似合并排序的过程。在每一步,如果a[S[head]] < a[T[head]],我们从 中弹出一个元素T,否则我们从 中弹出一个元素S。在每个这样的步骤中,我们记录 S 和 T 的头部的差异 if a[S[head]] < a[T[head]]。最大的这种差异会给你答案。

编辑:这是用 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
于 2013-08-16T21:14:21.743 回答
4

让我们做一个简单的观察:如果我们有 2 个元素 a[i]、a[j] 且 i < j 和 a[i] < a[j],那么我们可以确定 j 不会成为解决方案的一部分,因为第一个元素(他可以是第二个,但那是第二个故事)因为我会是一个更好的选择。

这告诉我们的是,如果我们贪婪地从答案左侧的元素中构建一个递减序列,那么答案肯定会来自那里。

例如:12 3 61 23 51 2 贪婪递减序列是这样构建的:

12 -> 12 3 -> 我们忽略 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(from the reading)),即 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)。您可以根据需要更改它。

于 2013-08-16T20:46:54.710 回答
3

我们可以避免检查整个数组,方法是从 的最大差异开始j-i并比较arr[j]>arr[i]所有可能的组合 j 和 i 的特定最大差异每当我们得到 with 的组合时(j,i)arr[j]>arr[i]我们就可以退出循环

示例:在{2,3,4,5,8,1} 第一个代码数组中将检查最大差异,5(5-0)(arr[0],arr[5]),如果arr[5]>arr[0]函数将退出,否则将采用最大差异的组合4 (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;  
}`

https://ide.geeksforgeeks.org/cjCW3wXjcj

于 2018-05-29T05:28:45.117 回答
2

这是合并下序列思想的一个非常简单的 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
于 2013-08-18T17:20:29.367 回答
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[] 和 RMa[] 时,如果我们看到 LMin[i] 大于 RMax[j],那么我们必须在 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;
}
于 2013-08-23T08:41:04.930 回答
1

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
于 2018-01-08T08:25:02.227 回答
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. 现在对于其他元素,我们不需要考虑评估,因为它们不能给出最佳解决方案。

让我知道您对我的解决方案不适用的用例的看法。

于 2013-08-17T16:18:13.860 回答
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;
}

第一个循环扫描数组一次,为每个元素找到其右侧剩余元素的最大值。我们还将相对索引存储在单独的数组中。第二个循环找到每个元素和对应的右侧最大值之间的最大值,并返回正确的索引。

于 2013-08-26T16:22:41.773 回答
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;
            }
        }
    }


}
于 2016-07-07T13:21:01.563 回答
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
于 2017-10-02T07:47:09.960 回答
0

下面是条件的 C++ 解决方案a[i] <= a[j]。它需要稍微修改来处理这种情况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;
}
于 2019-02-21T21:30:46.010 回答
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
于 2019-09-15T23:31:45.310 回答
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 并从头开始遍历,直到我们得到比这更小的值!

于 2021-07-16T21:35:50.117 回答
0

创建对的 Arraylist,其中键是数组元素,值是索引。对这个数组列表进行排序。遍历这个对数组列表以获得(maxj-i)之间的最大间隙。还要跟踪 maxj 并在找到新的 maxj 时更新。请找到我的 java 解决方案,它需要 O(nlogn) 时间复杂度和 O(n) 空间复杂度。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

class MaxDistanceSolution {
    private class Pair implements Comparable<Pair> {
        int key;
        int value;

        public int getKey() {
            return key;
        }

        public int getValue() {
            return value;
        }

        Pair(int key, int value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public int compareTo(Pair o) {
            return this.getKey() - o.getKey();
        }
    }

    public int maximumGap(final ArrayList<Integer> A) {
        int n = A.size();
        ArrayList<Pair> B = new ArrayList<>();
        for (int i = 0 ; i < n; i++)
            B.add(new Pair(A.get(i), i));
        Collections.sort(B);
        int maxJ = B.get(n-1).getValue();
        int gaps = 0;
        for (int i = n - 2; i >= 0; i--) {
            gaps = Math.max(gaps, maxJ - B.get(i).getValue());
            maxJ = Math.max(maxJ, B.get(i).getValue());
        }
        return gaps;
    }
}

public class MaxDistance {
    public static void main(String[] args) {
        MaxDistanceSolution sol = new MaxDistanceSolution();
        ArrayList<Integer> A = new ArrayList<>(Arrays.asList(3, 5, 4, 2));
        int gaps = sol.maximumGap(A);
        System.out.println(gaps);
    }
}
于 2021-09-21T13:41:30.713 回答
-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;
    }
于 2019-07-10T07:54:33.650 回答