【问题标题】:find the max difference between j and i indices such that j > i and a[j] > a[i] in O(n)找到 j 和 i 索引之间的最大差异,使得 j > i 和 a[j] > a[i] in O(n)
【发布时间】:2013-08-19 08:24:55
【问题描述】:

给定一个未排序的数组,找出maxj - i 之间的索引差异,例如j > ia[j] > a[i]O(n) 中。我能够在O(n^2) 复杂性中使用琐碎的方法找到ji,但想知道如何在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)

【问题讨论】:

  • 我可以想到两种方法在 O(NlogN) 中做到这一点。我得考虑一下,看看 O(N) 是否可行。
  • 如果数组按降序排序会发生什么?没有解决办法?
  • 我看不出什么排序或二分搜索或任何在其 big-O 复杂性中具有lg N 因素的算法可能与解决方案有关?数组未排序。该问题要求其中的两个元素最远满足不等式。
  • 这被称为“股市问题”,是本科算法课程的标准作业。
  • @PhilMiller 你是在说问题中的问题吗?

标签: algorithm sorting data-structures


【解决方案1】:

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

首先,观察如果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]] 必然是递减序列,因为它们分别是直到i 的最小值和从ni 的最大值。

所以现在我们尝试做一个类似合并排序的过程。在每一步,如果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

【讨论】:

  • 记住 popleft 需要线性时间
  • @ThomasAhle 这是一个作为链表实现的双端队列(希望如此),所以不应该是线性时间,docs.python.org/release/2.5.2/lib/deque-objects.html
  • 抱歉,没看到您没有使用列表 :-)
  • 你弄错了popleft。 if maxima[0][0] > minima[0][0]: minima.popleft() else: maxima.popleft()
【解决方案2】:

让我们做一个简单的观察:如果我们有 2 个元素 a[i]、a[j] 且 i

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

例如: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) 当我们可以将这 2 个配对时重复此操作

示例代码:

#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)。您可以根据需要对其进行更改。

【讨论】:

  • 你在“假设我们有这个递减序列”之后的话。无法理解
【解决方案3】:

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

示例:在{2,3,4,5,8,1} 的数组中 第一个代码将检查最大差异5(5-0),即(arr[0],arr[5]),如果arr[5]&gt;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

【讨论】:

    【解决方案4】:

    这是一个非常简单的 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】:

      为了解决这个问题,我们需要得到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;
      }
      

      【讨论】:

        【解决方案6】:

        使用辅助数组的简化版 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
        

        【讨论】:

          【解决方案7】:

          我可以想到对 O(n^2) 的改进,但需要验证这是否是 O(n) 在更坏的情况下。

          • 创建一个变量BestSoln=0;并遍历数组的第一个元素 并存储第一个元素的最佳解决方案,即bestSoln=k;
          • 现在对于第二个元素,只考虑距离 k 远的元素 从第二个元素。
          • 如果在这种情况下BestSoln 比第一次迭代更好,则替换 否则就让它这样吧。继续迭代其他元素。

          如果我们为每个子数组存储从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。现在对于其他元素,我们不需要考虑评估,因为它们无法给出最佳解决方案。

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

          【讨论】:

            【解决方案8】:

            对于 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;
            }
            

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

            【讨论】:

              【解决方案9】:

              我在 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;
                          }
                      }
                  }
              
              
              }
              

              【讨论】:

              • 您必须至少访问每个数组元素一次,因此最佳解决方案应该花费Ω(N) 时间。另请注意,BST 不会自动给你log N,它应该是一个自平衡的。
              【解决方案10】:

              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
              

              【讨论】:

                【解决方案11】:

                下面是条件a[i] &lt;= a[j] 的C++ 解决方案。处理a[i] &lt; 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;
                }
                

                【讨论】:

                  【解决方案12】:

                  请查看此解决方案以及可能失败的情况:

                  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
                  

                  【讨论】:

                    【解决方案13】:

                    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 并从头开始遍历,直到我们得到比这更小的值!

                    【讨论】:

                      【解决方案14】:

                      创建对数组列表,其中键是数组元素,值是索引。对这个数组列表进行排序。遍历这个对数组列表以获得(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);
                          }
                      }
                      

                      【讨论】:

                        【解决方案15】:

                        我已经在这里解决了这个问题。

                        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;
                            }
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2021-04-09
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-10-20
                          • 2017-09-10
                          • 2013-05-06
                          • 1970-01-01
                          • 2019-04-19
                          相关资源
                          最近更新 更多