web-dev-qa-db-ja.com

比較の最小数で配列の2番目に大きい要素を見つけます

サイズNの配列の場合、必要な比較の数はいくつですか?

65
nababa

最適なアルゴリズムは、n + log n-2個の比較を使用します。要素を競合他社と考え、トーナメントでそれらをランク付けします。

最初に、ツリーのように要素を比較します

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

これにはn-1回の比較が必要で、各要素は最大でlog n回比較に関与します。あなたは勝者として最大の要素を見つけるでしょう。

2番目に大きい要素は勝者との試合に負けたに違いない(別の要素との試合に負けることはできない)ため、彼は勝者が対戦したlog n要素の1つです。 log n-1の比較を使用して、それらのどれを見つけることができます。

最適性は、敵対的な議論によって証明されます。 https://math.stackexchange.com/questions/1601 または http://compgeom.cs.uiuc.edu/~jeffe/teaching/497/02-selection.pdfを参照してください または http://www.imada.sdu.dk/~jbj/DM19/lb06.pdf または https://www.utdallas.edu/~chandra/documents /6363/lbd.pdf

109
sdcvvc

最大2・([〜#〜] n [〜#〜]-1)の比較と、最大値と2番目に大きい値を保持する2つの変数を使用して、2番目に大きい値を見つけることができます。

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;
12
Gumbo

バブルソートまたは選択ソートアルゴリズムを使用して、降順で配列をソートします。配列を完全にソートしないでください。わずか2パス。最初のパスは最大の要素を提供し、2番目のパスは2番目に大きな要素を提供します。

最初のパスの比較数:n-1

最初のパスの比較数:n-2

合計2番目に大きい値を見つけるための比較:2n-3

このアルゴリズムを一般化できるかもしれません。 3番目に大きいものが必要な場合は、3つのパスを作成します。

上記の戦略では、バブルソートと選択ソートは インプレースソート アルゴリズムであるため、一時変数は必要ありません。

9
Rohit Bansod

最適ではないかもしれませんが、少なくとも実際には2番目に大きい要素を見つけるコードを次に示します。

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

最大の2つの要素が配列の先頭にある場合は少なくともN-1回、最悪の場合は最大で2N-3回の比較が必要です(最初の2つの要素の1つが配列の中で最小です)。

2
x4u

GumboアルゴリズムのPHPバージョン: 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

ケース1-> 9 8 7 6 5 4 3 2 1
case 2-> 50 10 8 25 ........
case 3-> 50 50 10 8 25 .........
case 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

提供された配列がinPutArray = [1,2,5,8,7,3]であると想定しますO/P-> 7(2番目に大きい)

 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

これは古い質問であることは知っていますが、トーナメントアルゴリズムを利用して解決しようとしています。 @sdcvvcで使用されるソリューションに似ていますが、要素を格納するために2次元配列を使用しています。

物事を機能させるには、次の2つの前提があります。
1)配列の要素数は2のべき乗です
2)配列に重複がない

プロセス全体は2つのステップで構成されます。
1。 2 x 2の要素を比較して2D配列を作成します。 2D配列の最初の行は、入力配列全体になります。次の行には、前の行の比較の結果が含まれます。新しく構築された配列で比較を続け、1つの要素(最大の要素)の配列に達するまで2D配列を構築し続けます。
2。最後の行に要素が1つだけ含まれる2D配列があります:最大の要素です。下から上へと続け、各配列で最大の「ビート」の要素を見つけ、それを現在の「2番目に大きい」値と比較します。最大の要素に勝った要素を見つけ、O(n)比較を避けるために、前の行に最大の要素のインデックスを保存する必要があります。 。任意のレベル(ルートレベルより上)で、隣接する要素は次のように取得されます。

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

ここで、rootIndexは前のレベルの最大(ルート)要素のインデックスです。

私は質問がC++を要求していることを知っていますが、Javaでそれを解決しようとする私の試みです。 (配列サイズの乱雑な変更や不要な配列サイズの計算を避けるために、配列の代わりにリストを使用しました)

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

申し訳ありませんが、JSコード...

2つの入力でテスト済み:

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

これには、最大a.length * 2の比較が必要であり、リストを1回だけ通過します。

1
geekdenz

O(1)時間の複雑さを伴う良い方法は、max-heapを使用することです。heapifyを2回呼び出すと、答えが得られます。

0
Harsh Gupta

これを試して。

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

それは魅力のように動作するはずです。複雑さが低い。

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

カウントソートを使用してから、インデックス0から終わりに向かって2番目に大きい要素を見つけます。少なくとも1つの比較があり、最大でn-1(要素が1つしかない場合!)。

0
lyfzabrutalcode

次の解決策は2(N-1)比較を取ります:

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

C++ 11のsdcvvcで受け入れられているソリューション。

#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

N + ceil(log n)-2の比較で実行できます。

解決策:最小値を得るにはn-1回の比較が必要です。

しかし、最小限にするために、各要素がペアでグループ化されるトーナメントを構築します。テニストーナメントのように、あらゆるラウンドの勝者が前進します。

ラウンドごとに半分になるので、このツリーの高さはlog nになります。

2番目の最小値を取得するためのアイデアは、前のラウンドの1つで最小候補者に勝つことです。そのため、潜在的な候補者の中で最小のものを見つける必要があります(最小でbeatられます)。

潜在的な候補はlog n =ツリーの高さです

だからトーナメントツリーを使用して最小値を見つけるための比較はn-1であり、2番目の最小値はlog n -1合計= n + ceil(log n)-2

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

参照: 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

上記のすべての記事を読みましたが、トーナメントアルゴリズムの実装が最善のアプローチであると確信しています。 @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;

配列内で2番目に大きい数を見つける場合に非常に便利です。 (2n-1)個の比較があります。しかし、3番目に大きい数またはk番目に大きい数を計算する場合はどうでしょう。上記のアルゴリズムは機能しません。別の手順に行きました。

ですから、トーナメントアルゴリズムのアプローチが最善であり、そのための link がここにあると思います。

上記の「最適なアルゴリズムはn + log n-2比較を使用します」に従うと思いますが、値を保存するためにバイナリツリーを使用しないコードは次のようになります。

各再帰呼び出し中に、配列サイズは半分に削減されます。

したがって、比較の数は次のとおりです。

1回目の反復:n/2回の比較

2回目の反復:n/4比較

3回目の反復:n/8比較

...最大n回の反復を記録しますか?

したがって、合計=> n-1回の比較?

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

配列に2 ^ n個の数字が含まれると仮定します。

6つの数字がある場合、3つの数字が次のレベルに移動しますが、これは正しくありません。

8個の数字が必要=> 4個の数字=> 2個の数字=> 1個の数字=> 2 ^ n個の数字

0
Ou Ye

スペースが無関係であると仮定すると、これは私が手に入れることができる最小のものです。最悪の場合は2 * n回の比較が必要で、最良の場合はn回の比較が必要です。

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