web-dev-qa-db-fra.com

trouver la différence maximale entre les indices j et i tels que j> i et a [j]> a [i] dans O(n)

Étant donné un tableau non trié, recherchez la différence maxj - i Entre les indices tels que j > i Et a[j] > a[i] Dans O(n). Je suis capable de trouver j et i en utilisant des méthodes triviales dans O(n^2) complexité mais je voudrais savoir comment faire cela dans O(n)?

Entrée: {9, 2, 3, 4, 5, 6, 7, 8, 18, 0}

Sortie: 8 (j = 8, i = 0)

Entrée: {1, 2, 3, 4, 5, 6}

Sortie: 5 (j = 5, i = 0)

43
manav m-n

Par souci de concision, je vais supposer que tous les éléments sont uniques. L'algorithme peut être étendu pour gérer les cas d'éléments non uniques.

Tout d'abord, notez que si x et y sont respectivement vos emplacements max et min souhaités, il ne peut pas y avoir a[i] > a[x] et i > x, et de même, non a[j] < a[y] et j < y.

Nous analysons donc le tableau a et construisons un tableau S tel que S[i] contient l'index de l'élément minimum dans a[0:i]. De même, un tableau T qui contient l'index de l'élément maximum dans a[n-1:i] (c'est-à-dire vers l'arrière).

Maintenant, nous pouvons voir que a[S[i]] et a[T[i]] sont nécessairement des séquences décroissantes, car elles étaient respectivement le minimum jusqu'à i et le maximum de n à i.

Alors maintenant, nous essayons de faire une procédure de type fusion. À chaque étape, si a[S[head]] < a[T[head]], nous supprimons un élément de T, sinon nous supprimons un élément de S. À chacune de ces étapes, nous enregistrons la différence en tête de S et de T si a[S[head]] < a[T[head]]. La différence maximale vous donne votre réponse.

EDIT: Voici un code simple dans Python implémentant l'algorithme.

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
34
Subhasis Das

Faisons cette observation simple: si nous avons 2 éléments a [i], a [j] avec i <j et a [i] <a [j] alors nous pouvons être sûrs que j ne fera pas partie de la solution car le premier élément (il peut être le deuxième mais c'est une deuxième histoire) parce que je serais une meilleure alternative.

Ce que cela nous dit, c'est que si nous construisons goulûment une séquence décroissante à partir des éléments de la partie gauche de la réponse viendra sûrement de là.

Par exemple pour: 12 3 61 23 51 2 la séquence goulûment décroissante est construite comme ceci:

12 -> 12 3 -> nous ignorons 61 parce que c'est pire que 3 -> nous ignorons 23 parce que c'est pire que 3 -> nous ignorons 51 parce que c'est pire que 3 -> 12 3 2.

Donc, la réponse contiendrait du côté gauche 12 3 ou 2.

Maintenant, dans un cas aléatoire, cela a une longueur O (log N) de sorte que vous pouvez rechercher binaire dessus pour chaque élément comme la bonne partie de la réponse et vous obtiendrez O (N log log N) qui est bon, et si vous appliquez le même logique sur la partie droite de la chaîne sur un cas aléatoire vous pourriez obtenir O (log ^ 2 N + N (d'après la lecture)) qui est O (N). Mais nous pouvons aussi faire O(N) sur un cas non aléatoire.

Supposons que nous ayons cette séquence décroissante. Nous commençons à droite de la chaîne et faisons ce qui suit pendant que nous pouvons associer la dernière séquence décroissante avec le nombre actuel

1) Si nous avons trouvé une meilleure solution en prenant le dernier de la séquence décroissante et le nombre actuel que nous mettons à jour la réponse

2) Même si nous avons mis à jour la réponse ou non, nous sautons le dernier élément de la séquence décroissante parce que nous sommes c'est une correspondance parfaite (toute autre correspondance serait à gauche et donnerait une réponse avec un plus petit j - i)

3) Répétez pendant que nous pouvons coupler ces 2

Exemple de code:

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

Édition ultérieure: je vois que vous avez donné un exemple: j'ai indexé à partir de 1 pour le rendre plus clair et j'imprime (i, j) au lieu de (j, i). Vous pouvez le modifier comme bon vous semble.

4
adrian.budau

Pour résoudre ce problème, nous devons obtenir deux indices optimaux d'arr []: l'indice gauche i et l'indice droit j. Pour un élément arr [i], nous n'avons pas besoin de considérer arr [i] pour l'index gauche s'il y a un élément plus petit que arr [i] sur le côté gauche de arr [i]. De même, s'il y a un plus grand élément sur le côté droit de arr [j] alors nous n'avons pas besoin de considérer ce j pour l'indice de droite. Nous construisons donc deux tableaux auxiliaires LMin [] et RMax [] de telle sorte que LMin [i] contient le plus petit élément sur le côté gauche de arr [i] y compris arr [i], et RMax [j] détient le plus grand élément sur le côté droit de arr [j] y compris arr [j]. Après avoir construit ces deux tableaux auxiliaires, nous parcourons ces deux tableaux de gauche à droite. En parcourant LMin [] et RMa [] si nous voyons que LMin [i] est supérieur à RMax [j], alors nous devons avancer dans LMin [] (ou faire i ++) car tous les éléments à gauche de LMin [i] sont supérieur ou égal à LMin [i]. Sinon, nous devons avancer dans RMax [j] pour chercher une plus grande valeur j - i. Voici le code c exécuté en O(n) fois:

#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;
}
2
jfly

Nous pouvons éviter de vérifier l'ensemble du tableau en partant de la différence maximale de j-i Et en comparant arr[j]>arr[i] Pour toutes les combinaisons possibles j et i pour cette différence maximale particulière Chaque fois que nous obtenons une combinaison de (j,i) Avec arr[j]>arr[i] Nous pouvons sortir de la boucle

Exemple: dans un tableau de {2,3,4,5,8,1}, Le premier code vérifiera la différence maximale 5(5-0) c'est-à-dire (arr[0],arr[5]), Si arr[5]>arr[0] Se fermera, sinon il faudra des combinaisons de max diff 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

2
Anish Antony

Voici une très simple O(n) Python implémentation de l'idée de séquence descendante fusionnée. L'implémentation fonctionne même dans le cas de valeurs en double:

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
1
Thomas Ahle

Voici une solution C++ pour la condition a[i] <= a[j]. Il a besoin d'une légère modification pour gérer le cas 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;
}
0
Evg

Un algorithme simplifié de la réponse de 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
0
Chenxi Yuan

Version simplifiée de la réponse Subhasis Das utilisant des tableaux auxiliaires.

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
0
J.S

Je peux penser à une amélioration par rapport à O (n ^ 2), mais je dois vérifier si c'est O(n) dans le pire des cas ou non.

  • Créez une variable BestSoln=0; et parcourir le tableau pour le premier élément et stocker la meilleure solution pour le premier élément, c'est-à-dire bestSoln=k;.
  • Maintenant, pour le 2e élément, considérons uniquement les éléments qui sont à k distances du deuxième élément.
  • Si BestSoln dans ce cas est meilleur que la première itération, remplacez-le sinon laissez-le être comme ça. Continuez à itérer pour les autres éléments.

Il peut être encore amélioré si nous stockons l'élément max pour chaque sous-tableau à partir de i jusqu'à la fin. Cela peut être fait dans O(n) en traversant le tableau de la fin. Si un élément particulier est plus que son max local alors il n'est pas nécessaire de faire une évaluation pour cet élément.

Contribution:

{9, 2, 3, 4, 5, 6, 7, 8, 18, 0}

créer un tableau max local pour ce tableau:

[18,18,18,18,18,18,18,0,0] O(n).

Maintenant, parcourez le tableau pour 9, ici la meilleure solution sera i=0,j=8. Maintenant, pour le deuxième élément ou après, nous n'avons pas besoin d'évaluer. et la meilleure solution est i=0,j=8.

Mais supposons que le tableau soit Input:

{19, 2, 3, 4, 5, 6, 7, 8, 18, 0,4}

Tableau max local [18,18,18,18,18,18,18,0,0] alors dans la première itération nous n'avons pas besoin d'évaluer car le max local est inférieur à l'elem actuel.

Maintenant, pour la deuxième itération, la meilleure solution est, i=1,j=10. Maintenant, pour d'autres éléments, nous n'avons pas besoin de considérer l'évaluation car ils ne peuvent pas donner la meilleure solution.

Faites-moi part de votre avis sur votre cas d'utilisation auquel ma solution n'est pas applicable.

0
AKS

Il s'agit d'une solution très simple pour O(2n) de vitesse et ~ O (2n) d'espace supplémentaire (en plus du tableau d'entrée). L'implémentation suivante est en 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;
}

La première boucle balaye le tableau une fois, trouvant pour chaque élément le maximum des éléments restants à sa droite. Nous stockons également l'index relatif dans un tableau séparé. La seconde boucle trouve le maximum entre chaque élément et le maximum correspondant à droite, et retourne l'index de droite.

0
darmat

Ma solution avec en O (log n) (Veuillez me corriger ici si je me trompe dans le calcul de cette complexité) le temps ...

L'idée est d'insérer dans un BST puis de rechercher le nœud et si le nœud a un enfant droit, puis de parcourir le sous-arbre de droite pour calculer le nœud avec l'indice maximum.

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


}
0
Sumeet Sharma

Veuillez examiner cette solution et les cas où elle pourrait échouer:

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
0
neo