web-dev-qa-db-fra.com

Trouver le 2e élément le plus grand dans un tableau avec un nombre minimal de comparaisons

Pour un tableau de taille N, quel est le nombre de comparaisons requises?

63
nababa

L'algorithme optimal utilise n + log n-2 comparaisons. Pensez aux éléments en tant que concurrents et un tournoi les classera.

Tout d'abord, comparez les éléments, comme dans l'arbre

   |
  / \
 |   |
/ \ / \
x x x x

cela prend n-1 comparaisons et chaque élément est impliqué dans la comparaison au plus log n fois. Vous trouverez le plus grand élément en tant que gagnant.

Le deuxième élément en importance doit avoir perdu un match contre le vainqueur (il ne peut pas perdre un match contre un élément différent), il est donc l’un des éléments log n contre lesquels le vainqueur a joué. Vous pouvez trouver lequel d’entre eux en utilisant les comparaisons de log n - 1.

L'optimalité est prouvée par l'argument de l'adversaire. Voir https://math.stackexchange.com/questions/1601 ou http://compgeom.cs.uiuc.edu/~jeffe/teaching/497/02-selection.pdf ou http://www.imada.sdu.dk/~jbj/DM19/lb06.pdf ou https://www.utdallas.edu/~chandra/documents/6363/lbd.pdf

108
sdcvvc

Vous pouvez trouver la deuxième plus grande valeur avec au plus 2 · (N- 1) comparaisons et deux variables qui contiennent la plus grande et la deuxième plus grande valeur:

largest := numbers[0];
secondLargest := null
for i=1 to numbers.length-1 do
    number := numbers[i];
    if number > largest then
        secondLargest := largest;
        largest := number;
    else
        if number > secondLargest then
            secondLargest := number;
        end;
    end;
end;
11
Gumbo

Utilisez un algorithme de tri à bulles ou de tri par sélection qui trie le tableau par ordre décroissant. Ne triez pas le tableau complètement. Juste deux passes. Le premier passage donne le plus grand élément et le second passe le deuxième.

Nombre de comparaisons pour le premier passage: n-1

Nombre de comparaisons pour le premier passage: n-2

N ° total de comparaison pour trouver le deuxième plus grand: 2n-3

Peut-être que vous pouvez généraliser cet algorithme. Si vous avez besoin du 3ème plus gros, vous faites 3 passes.

Par la stratégie ci-dessus, vous n'avez besoin d'aucune variable temporaire, car les types de tri par bulle et par sélection sont tri par défaut algorithmes.

9
Rohit Bansod

Voici un code qui pourrait ne pas être optimal mais qui au moins trouve réellement le 2e plus grand élément:

if( val[ 0 ] > val[ 1 ] )
{
    largest = val[ 0 ]
    secondLargest = val[ 1 ];
}
else
{
    largest = val[ 1 ]
    secondLargest = val[ 0 ];
}

for( i = 2; i < N; ++i )
{
    if( val[ i ] > secondLargest )
    {
        if( val[ i ] > largest )
        {
            secondLargest = largest;
            largest = val[ i ];
        }
        else
        {
            secondLargest = val[ i ];
        }
    }
}

Il faut au moins N-1 comparaisons si les 2 éléments les plus grands se trouvent au début du tableau et au plus 2N-3 dans le pire des cas (l'un des 2 premiers éléments est le plus petit du tableau).

2
x4u

Version PHP de l'algorithme Gumbo: http://sandbox.onlinephpfunctions.com/code/51e1b05dac2e648fd13e0b60f44a2abe1e4a8689

$numbers = [10, 9, 2, 3, 4, 5, 6, 7];

$largest = $numbers[0];
$secondLargest = null;
for ($i=1; $i < count($numbers); $i++) {
    $number = $numbers[$i];
    if ($number > $largest) {
        $secondLargest = $largest;
        $largest = $number;
    } else if ($number > $secondLargest) {
        $secondLargest = $number;
    }
}

echo "largest=$largest, secondLargest=$secondLargest";
1
forsberg

cas 1 -> 9 8 7 6 5 4 3 2 1
cas 2 -> 50 10 8 25 ........
cas 3 -> 50 50 10 8 25 .........
cas 4 -> 50 50 10 8 50 25 ....... 

public void second element()  
{
      int a[10],i,max1,max2;  
      max1=a[0],max2=a[1];  
      for(i=1;i<a.length();i++)  
      {  
         if(a[i]>max1)  
          {
             max2=max1;  
             max1=a[i];  
          }  
         else if(a[i]>max2 &&a[i]!=max1)  
           max2=a[i];  
         else if(max1==max2)  
           max2=a[i];  
      }  
}
1
achal kumar

Supposer que le tableau fourni est inPutArray = [1,2,5,8,7,3] O/P attendu -> 7 (deuxième plus grand)

 take temp array 
      temp = [0,0], int dummmy=0;
    for (no in inPutArray) {
    if(temp[1]<no)
     temp[1] = no
     if(temp[0]<temp[1]){
    dummmy = temp[0]
    temp[0] = temp[1]
    temp[1] = temp
      }
    }

    print("Second largest no is %d",temp[1])
1
Kiran K

Je sais que c'est une vieille question, mais voici ma tentative de la résoudre en utilisant l'algorithme du tournoi. C'est similaire à la solution utilisée par @sdcvvc, mais j'utilise un tableau à deux dimensions pour stocker des éléments.

Pour que les choses fonctionnent, il y a deux hypothèses:
1) le nombre d'éléments dans le tableau est la puissance de 2
2) il n'y a pas de doublons dans le tableau

L'ensemble du processus comprend deux étapes:
1. Construire un tableau 2D en comparant deux par deux éléments. La première ligne du tableau 2D sera le tableau entier. La rangée suivante contient les résultats des comparaisons de la rangée précédente. Nous continuons les comparaisons sur le tableau nouvellement construit et continuons à construire le tableau 2D jusqu'à ce qu'un tableau d'un seul élément (le plus grand) soit atteint.
2. nous avons un tableau 2D où la dernière ligne ne contient qu'un seul élément: le plus grand. Nous continuons d'aller de bas en haut, dans chaque tableau, recherchant l'élément "battu" par le plus grand et le comparant à la "deuxième plus grande" valeur actuelle. Pour trouver l'élément battu par le plus grand et éviter les comparaisons O(n), nous devons stocker l'index du plus grand élément dans la ligne précédente. De cette façon, nous pouvons facilement vérifier les éléments adjacents. À n'importe quel niveau (au-dessus du niveau racine), les éléments adjacents sont obtenus sous la forme:

leftAdjacent = rootIndex*2
rightAdjacent = rootIndex*2+1,

où rootIndex est l'index du plus grand élément (root) au niveau précédent.

Je sais que la question demande le C++, mais voici ma tentative de le résoudre en Java. (J'ai utilisé des listes au lieu de tableaux, pour éviter les modifications désordonnées de la taille du tableau et/ou des calculs inutiles de la taille du tableau)

public static Integer findSecondLargest(List<Integer> list) {
        if (list == null) {
            return null;
        }
        if (list.size() == 1) {
            return list.get(0);
        }
        List<List<Integer>> structure = buildUpStructure(list);
        System.out.println(structure);
        return secondLargest(structure);

    }

    public static List<List<Integer>> buildUpStructure(List<Integer> list) {
        List<List<Integer>> newList = new ArrayList<List<Integer>>();
        List<Integer> tmpList = new ArrayList<Integer>(list);
        newList.add(tmpList);
        int n = list.size();
        while (n>1) {
            tmpList = new ArrayList<Integer>();
            for (int i = 0; i<n; i=i+2) {
                Integer i1 = list.get(i);
                Integer i2 = list.get(i+1);
                tmpList.add(Math.max(i1, i2));
            }
            n/= 2;
            newList.add(tmpList);   
            list = tmpList;
        }
        return newList;
    }

    public static Integer secondLargest(List<List<Integer>> structure) {
        int n = structure.size();
        int rootIndex = 0;
        Integer largest = structure.get(n-1).get(rootIndex);
        List<Integer> tmpList = structure.get(n-2);
        Integer secondLargest = Integer.MIN_VALUE;
        Integer leftAdjacent = -1;
        Integer rightAdjacent = -1;
        for (int i = n-2; i>=0; i--) {
            rootIndex*=2;
            tmpList = structure.get(i);
            leftAdjacent = tmpList.get(rootIndex);
            rightAdjacent = tmpList.get(rootIndex+1); 
            if (leftAdjacent.equals(largest)) {
                if (rightAdjacent > secondLargest) {
                    secondLargest = rightAdjacent;
                }
            }
            if (rightAdjacent.equals(largest)) {
                if (leftAdjacent > secondLargest) {
                    secondLargest = leftAdjacent;
                }
                rootIndex=rootIndex+1;
            }
        }

        return secondLargest;
    }
1
Maggie

Désolé, code JS ...

Testé avec les deux entrées:

a = [55,11,66,77,72];
a = [ 0, 12, 13, 4, 5, 32, 8 ];

var first = Number.MIN_VALUE;
var second = Number.MIN_VALUE;
for (var i = -1, len = a.length; ++i < len;) {
    var dist = a[i];
    // get the largest 2
    if (dist > first) {
        second = first;
        first = dist;
    } else if (dist > second) { // && dist < first) { // this is actually not needed, I believe
        second = dist;
    }
}

console.log('largest, second largest',first,second);
largest, second largest 32 13

Cela doit comporter un maximum de 2 comparaisons et ne parcourir la liste qu'une fois.

1
geekdenz

Une bonne manière avec O(1) la complexité temporelle serait d'utiliser un tas max. Appelez l'heapify deux fois et vous avez la réponse.

0
Harsh Gupta

essaye ça.

max1 = a[0].
max2.
for i = 0, until length:
  if a[i] > max:
     max2 = max1.
     max1 = a[i].
     #end IF
  #end FOR
return min2.

cela devrait fonctionner comme un charme. faible complexité.

voici un code Java.

int secondlLargestValue(int[] secondMax){
int max1 = secondMax[0]; // assign the first element of the array, no matter what, sorted or not.
int max2 = 0; // anything really work, but zero is just fundamental.
   for(int n = 0; n < secondMax.length; n++){ // start at zero, end when larger than length, grow by 1. 
        if(secondMax[n] > max1){ // nth element of the array is larger than max1, if so.
           max2 = max1; // largest in now second largest,
           max1 = secondMax[n]; // and this nth element is now max.
        }//end IF
    }//end FOR
    return max2;
}//end secondLargestValue()
0
sabbibJAVA
#include<stdio.h>
main()
{
        int a[5] = {55,11,66,77,72};
        int max,min,i;
        int smax,smin;
        max = min = a[0];
        smax = smin = a[0];
        for(i=0;i<=4;i++)
        {
                if(a[i]>max)
                {
                        smax = max;
                        max = a[i];
                }
                if(max>a[i]&&smax<a[i])
                {
                        smax = a[i];
                }
        }
        printf("the first max element z %d\n",max);
        printf("the second max element z %d\n",smax);
}
0
prawin

Utilisez la méthode de comptage, puis recherchez le deuxième élément le plus grand, en partant de l'indice 0 vers la fin. Il devrait y avoir au moins une comparaison, au plus n-1 (quand il n'y a qu'un seul élément!). 

0
lyfzabrutalcode

La solution suivante prendrait 2(N-1) comparaisons:

arr  #array with 'n' elements
first=arr[0]
second=-999999  #large negative no
i=1
while i is less than length(arr):
    if arr[i] greater than first:
        second=first
        first=arr[i]
    else:
        if arr[i] is greater than second and arr[i] less than first:
            second=arr[i]
    i=i+1
print second
0
user3214392

La solution acceptée par sdcvvc en C++ 11.

#include <algorithm>
#include <iostream>
#include <vector>
#include <cassert>
#include <climits>

using std::vector;
using std::cout;
using std::endl;
using std::random_shuffle;
using std::min;
using std::max;

vector<int> create_tournament(const vector<int>& input) {
  // make sure we have at least two elements, so the problem is interesting
  if (input.size() <= 1) {
    return input;
  }

  vector<int> result(2 * input.size() - 1, -1);

  int i = 0;
  for (const auto& el : input) {
    result[input.size() - 1 + i] = el;
    ++i;
  }

  for (uint j = input.size() / 2; j > 0; j >>= 1) {
    for (uint k = 0; k < 2 * j; k += 2) {
      result[j - 1 + k / 2] = min(result[2 * j - 1 + k], result[2 * j + k]);
    }
  }

  return result;
}

int second_smaller(const vector<int>& tournament) {
  const auto& minimum = tournament[0];
  int second = INT_MAX;

  for (uint j = 0; j < tournament.size() / 2; ) {
    if (tournament[2 * j + 1] == minimum) {
      second = min(second, tournament[2 * j + 2]);
      j = 2 * j + 1;
    }
    else {
      second = min(second, tournament[2 * j + 1]);
      j = 2 * j + 2;
    }
  }

  return second;
}

void print_vector(const vector<int>& v) {
  for (const auto& el : v) {
    cout << el << " ";
  }
  cout << endl;
}

int main() {

  vector<int> a;
  for (int i = 1; i <= 2048; ++i)
    a.Push_back(i);

  for (int i = 0; i < 1000; i++) {
    random_shuffle(a.begin(), a.end());
    const auto& v = create_tournament(a);
    assert (second_smaller(v) == 2);
  }

  return 0;
}
0

Cela peut être fait avec une comparaison n + ceil (log n) - 2.

Solution: Il faut n-1 comparaisons pour obtenir le minimum.

Mais pour obtenir le minimum, nous allons construire un tournoi dans lequel chaque élément sera regroupé par paires. comme un tournoi de tennis et vainqueur d'un tour sera aller de l'avant.

La hauteur de cet arbre sera enregistrée puisque nous en sommes à chaque tour.

L’idée d’obtenir le deuxième minimum est qu’il sera battu par le candidat le moins élevé lors du tour précédent. Il faut donc trouver le minimum de candidats potentiels (battu par le minimum).

Les candidats potentiels seront log n = hauteur de l'arbre

Donc non. La comparaison pour trouver le minimum en utilisant l’arbre du tournoi est n-1 Pour le second minimum, log n-1

Voici le code C++

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>

using namespace std;

typedef pair<int,int> ii;

bool isPowerOfTwo (int x)
{
  /* First x in the below expression is for the case when x is 0 */
  return x && (!(x&(x-1)));
}
// modified
int log_2(unsigned int n) {
    int bits = 0;
    if (!isPowerOfTwo(n))
        bits++;
    if (n > 32767) {
        n >>= 16;
        bits += 16;
    }
    if (n > 127) {
        n >>= 8;
        bits += 8;
    }
    if (n > 7) {
        n >>= 4;
        bits += 4;
    }
    if (n > 1) {
        n >>= 2;
        bits += 2;
    }
    if (n > 0) {
        bits++;
    }
    return bits;
}

int second_minima(int a[], unsigned int n) {

    // build a tree of size of log2n in the form of 2d array
    // 1st row represents all elements which fights for min
    // candidate pairwise. winner of each pair moves to 2nd
    // row and so on
    int log_2n = log_2(n);
    long comparison_count = 0;
    // pair of ints : first element stores value and second
    //                stores index of its first row
    ii **p = new ii*[log_2n];
    int i, j, k;
    for (i = 0, j = n; i < log_2n; i++) {
        p[i] = new ii[j];
        j = j&1 ? j/2+1 : j/2;
    }
    for (i = 0; i < n; i++)
        p[0][i] = make_pair(a[i], i);



    // find minima using pair wise fighting
    for (i = 1, j = n; i < log_2n; i++) {
        // for each pair
        for (k = 0; k+1 < j; k += 2) {
            // find its winner
            if (++comparison_count && p[i-1][k].first < p[i-1][k+1].first) {
                p[i][k/2].first = p[i-1][k].first;
                p[i][k/2].second = p[i-1][k].second;
            }
            else {
                p[i][k/2].first = p[i-1][k+1].first;
                p[i][k/2].second = p[i-1][k+1].second;
            }

        }
        // if no. of elements in row is odd the last element
        // directly moves to next round (row)
        if (j&1) {
            p[i][j/2].first = p[i-1][j-1].first;
            p[i][j/2].second = p[i-1][j-1].second;
        }
        j = j&1 ? j/2+1 : j/2;
    }



    int minima, second_minima;
    int index;
    minima = p[log_2n-1][0].first;
    // initialize second minima by its final (last 2nd row)
    // potential candidate with which its final took place
    second_minima = minima == p[log_2n-2][0].first ? p[log_2n-2][1].first : p[log_2n-2][0].first;
    // minima original index
    index = p[log_2n-1][0].second;
    for (i = 0, j = n; i <= log_2n - 3; i++) {
        // if its last candidate in any round then there is
        // no potential candidate
        if (j&1 && index == j-1) {
            index /= 2;
            j = j/2+1;
            continue;
        }
        // if minima index is odd, then it fighted with its index - 1
        // else its index + 1
        // this is a potential candidate for second minima, so check it
        if (index&1) {
            if (++comparison_count && second_minima > p[i][index-1].first)
                second_minima = p[i][index-1].first;
        }
        else {
            if (++comparison_count && second_minima > p[i][index+1].first)
                second_minima = p[i][index+1].first;
        }
        index/=2;
        j = j&1 ? j/2+1 : j/2;
    }


    printf("-------------------------------------------------------------------------------\n");
    printf("Minimum          : %d\n", minima);
    printf("Second Minimum   : %d\n", second_minima);
    printf("comparison count : %ld\n", comparison_count);
    printf("Least No. Of Comparisons (");
    printf("n+ceil(log2_n)-2) : %d\n", (int)(n+ceil(log(n)/log(2))-2));
    return 0;
}

int main()
{
    unsigned int n;
    scanf("%u", &n);
    int a[n];
    int i;
    for (i = 0; i < n; i++)
        scanf("%d", &a[i]);
    second_minima(a,n);
    return 0;
}
0
prakharjain
function findSecondLargeNumber(arr){

    var fLargeNum = 0;
    var sLargeNum = 0;

    for(var i=0; i<arr.length; i++){
        if(fLargeNum < arr[i]){
            sLargeNum = fLargeNum;
            fLargeNum = arr[i];         
        }else if(sLargeNum < arr[i]){
            sLargeNum = arr[i];
        }
    }

    return sLargeNum;

}
var myArray = [799, -85, 8, -1, 6, 4, 3, -2, -15, 0, 207, 75, 785, 122, 17];

Réf: http://www.ajaybadgujar.com/finding-second-largest-number-from-array-in-javascript/

0
coder
    int[] int_array = {4, 6, 2, 9, 1, 7, 4, 2, 9, 0, 3, 6, 1, 6, 8};
    int largst=int_array[0];
    int second=int_array[0];
    for (int i=0; i<int_array.length; i++){        
        if(int_array[i]>largst) { 
            second=largst;
            largst=int_array[i];
        }  
        else if(int_array[i]>second  &&  int_array[i]<largst) { 
            second=int_array[i];
        } 
    }
0
Usman

J'ai parcouru tous les articles ci-dessus, mais je suis convaincu que la mise en œuvre de l'algorithme Tournament est la meilleure approche. Considérons l'algorithme suivant posté par @Gumbo

largest := numbers[0];
secondLargest := null
for i=1 to numbers.length-1 do
    number := numbers[i];
    if number > largest then
        secondLargest := largest;
        largest := number;
    else
        if number > secondLargest then
            secondLargest := number;
        end;
    end;
end;

C'est très bien au cas où nous allons trouver le deuxième plus grand nombre dans un tableau. Il a (2n-1) nombre de comparaisons. Mais que se passe-t-il si vous voulez calculer le troisième plus grand nombre ou un kème plus grand nombre? L'algorithme ci-dessus ne fonctionne pas. Vous avez une autre procédure. 

Donc, je crois que l’approche par algorithme de tournoi est la meilleure et voici le link pour cela.

Je suppose que suivez l’algorithme optimal qui utilise les comparaisons n + log n-2 ci-dessus, le code que j’ai trouvé qui n’utilise pas d’arbre binaire pour stocker la valeur serait le suivant:

Lors de chaque appel récursif, la taille de la matrice est réduite de moitié.

Le nombre de comparaison est donc: 

1ère itération: n/2 comparaisons

2ème itération: n/4 comparaisons

3ème itération: n/8 comparaisons

... Jusqu'à enregistrer n itérations?

Donc, total => n - 1 comparaisons?

function findSecondLargestInArray(array) {
    let winner = [];
    if (array.length === 2) {
        if (array[0] < array[1]) {
            return array[0];
        } else {
            return array[1];
        }
    }
    for (let i = 1; i <= Math.floor(array.length / 2); i++) {
        if (array[2 * i - 1] > array[2 * i - 2]) {
            winner.Push(array[2 * i - 1]);
        } else {
            winner.Push(array[2 * i - 2]);
        }
    }
    return findSecondLargestInArray(winner);
}

En supposant que tableau contient 2 ^ n nombre de nombres.

S'il y a 6 nombres, alors 3 numéros passeront au niveau suivant, ce qui n'est pas correct.

Besoin comme 8 chiffres => 4 nombre => 2 nombre => 1 nombre => 2 ^ n nombre de nombre

0
Ou Ye

En supposant que l’espace soit sans importance, c’est le plus petit que je puisse obtenir. Cela nécessite 2 * n comparaisons dans le pire des cas, et n comparaisons dans le meilleur des cas:

arr = [ 0, 12, 13, 4, 5, 32, 8 ]
max = [ -1, -1 ]

for i in range(len(arr)):
     if( arr[i] > max[0] ):
        max.insert(0,arr[i])
     Elif( arr[i] > max[1] ):
        max.insert(1,arr[i])

print max[1]
0
riwalk