web-dev-qa-db-ja.com

配列内の多数の要素を見つける

多数決要素は、配列のサイズの半分を超える要素です。

O(n)の配列で多数決要素を見つける方法

入力例:

{2,1,2,3,4,2,1,2,2}

期待される出力:

2
50
Ali Tarhini

多数要素(存在する場合)も中央値になります。 O(n)で中央値を見つけ、それが実際にO(n)の有効な多数決要素であることを確認します。実装の詳細 link

26
Axn
// returns -1 if there is no element that is the majority element, otherwise that element

// funda :: if there is a majority element in an array, say x, then it is okay to discard 
// a part of that array that has no majority element, the remaining array will still have
// x as the majority element

// worst case complexity :  O(n)

int findMajorityElement(int* arr, int size) { 
    int count = 0, i, majorityElement;
    for (i = 0; i < size; i++) {
        if (count == 0)
            majorityElement = arr[i];
        if (arr[i] == majorityElement) 
            count++;
        else
            count--;
    }
    count = 0;
    for (i = 0; i < size; i++)
        if (arr[i] == majorityElement)
            count++;
    if (count > size/2)
        return majorityElement;
    return -1;
}
108
Hitesh Gupta

5年以内に誰もこの問題の適切な説明を書いていないのを見るのは悲しいです。

これはストリーミングアルゴリズム(巨大な(潜在的に無限)のデータストリームがある)の標準的な問題であり、このストリームから統計を計算して、このストリームを1回通過する必要があります。


明らかに、ハッシュまたは並べ替えでアプローチできますが、潜在的に無限のストリームでは、明らかにメモリ不足になる可能性があります。ですから、ここで何か賢いことをしなければなりません。


多数決要素は、配列のサイズの半分を超える要素です。これは、多数の要素が他のすべての要素の組み合わせよりも多く発生することを意味します。つまり、多数決要素が出現する回数をカウントし、他のすべての要素の出現回数を引くと、正の数が得られます。

そのため、ある要素の出現回数をカウントし、他のすべての要素の出現回数を減算して0を取得すると、元の要素は多数決要素にはなりません。これが正しいアルゴリズムの基礎です。

2つの変数、counterおよびpossible_elementを宣言します。カウンターが0の場合、ストリームを繰り返します-可能な要素を上書きし、数が可能な要素と同じ場合はカウンターを初期化します-カウンターを増やします、そうでなければ減らします。Pythonコード:

_def majority_element(arr):
    counter, possible_element = 0, None
    for i in arr:
        if counter == 0:
            possible_element, counter = i, 1
        Elif i == possible_element:
            counter += 1
        else:
            counter -= 1

    return possible_element
_

アルゴリズムがO(n)(3など)の前に非常に小さな定数を持つO(n)であることは明らかです。また、3つの変数のみが初期化されているため、スペースの複雑さはO(1)のように見えます。問題は、これらの変数の1つがn(配列が同じ数字で構成されている場合)まで成長する可能性のあるカウンターであるということです。そして、数値nを保存するには、O(log (n))スペースが必要です。したがって、理論的な観点からそれはO(n) timeおよびO(log(n)) spaceです。 実用的から、2 ^ 128の数値を倍長整数に収めることができ、配列内のこの要素の数は想像を絶するほど巨大です。

また、多数決要素がある場合にのみアルゴリズムが機能することに注意してください。そのような要素が存在しない場合でも、いくつかの番号が返されますが、間違いがあります。 (多数決要素が存在するかどうかを判断するためにアルゴリズムを変更するのは簡単です)

履歴チャンネル:このアルゴリズムは、1982年にボイヤー、ムーアによって発明され、 ボイヤー–ムーア多数決アルゴリズム と呼ばれました。

36
Salvador Dali

多数派要素:

サイズnの配列A []の多数決要素は、n/2回以上出現する要素です(したがって、そのような要素は最大で1つです)。

候補の検索:

O(n)で動作する第1フェーズのアルゴリズムは、ムーアの投票アルゴリズムとして知られています。アルゴリズムの基本的な考え方は、要素eの各出現を他のすべての要素とともにキャンセルすることです。 eと異なる場合、eが過半数要素である場合、eは最後まで存在します。

findCandidate(a[], size)
1.  Initialize index and count of majority element
     maj_index = 0, count = 1
2.  Loop for i = 1 to size – 1
    (a)If a[maj_index] == a[i]
        count++
    (b)Else
        count--;
    (c)If count == 0
        maj_index = i;
        count = 1
3.  Return a[maj_index]

上記のアルゴリズムは各要素をループし、a [maj_index]のカウントを維持します。次の要素が同じ場合はカウントをインクリメントし、次の要素が同じでない場合はカウントをデクリメントし、カウントが0に達するとmaj_indexを現在の要素を指定し、カウントを1に設定します。第1フェーズアルゴリズムは、候補要素を提供します。第2フェーズでは、候補が本当に多数派要素であるかどうかを確認する必要があります。

第2フェーズは単純で、O(n)で簡単に実行できます。候補要素のカウントがn/2より大きいかどうかを確認するだけです。

詳細については、 geeksforgeeks をお読みください

16
Aditya

時間:O(n)

スペース:O(n)

ツリーを歩いて、ハッシュテーブル内の要素の出現をカウントします。

時間:O(n lg n)またはO(n * m)(使用するソートに依存)

スペース:(1)

配列を並べ替えてから、要素の出現をカウントします。

インタビューの正解:ムーアの投票アルゴリズム

時間:O(n)

スペース:O(1)

リストを歩いて、現在の数と現在の最良の推測数を比較します。数が現在の最良の推測数と等しい場合はカウンターをインクリメントし、そうでない場合はカウンターをデクリメントし、カウンターがゼロにヒットした場合は現在の最良の推測数を現在の数で置き換え、カウンターを1に設定します。最良の推測は候補者番号です。候補者のインスタンスを数えるだけでリストをもう一度調べてください。最終カウントがn/2より大きい場合、それは多数決です。それ以外の場合はありません。

4
stonemetal

ランダムサンプリングアプローチはどうですか?たとえば、sqrt(n)要素をサンプリングし、sqrt(n)/ 4回以上発生した各要素について(O(n) time and O(sqrt(n)) space)、O(n) timeの過半数要素であるかどうかを確認できます。

この方法では、最大でn ^ {1/4}/2の標準偏差で、予想で少なくともsqrt(n)/ 2回サンプリングされるため、多数の要素が高い確率で検出されます。

重複リンクの1つで見たアプローチに似たもう1つのサンプリングアプローチは、2つのサンプルを描画し、それらが等しい場合、O(n)過半数以外の他の要素は明確でない可能性があるため、追加の検証手順が必要です。

3
jonderry

モンテカルロアルゴリズムでは、

Majority (a,n)
//a[]-array of 'n' natural numbers
 {
  j=random(0,1,....,n-1)
  /*Selecting the integers at random ranging from 0 to n-1*/
  b=a[j];c=0;
  for k from 0 to n-1 do
   { 
    if a[k]=b then,
    c=c+1;
    }
    return (c>n/2)
   }
2

配列内の要素の大部分を見つけるには、ムーアの多数決アルゴリズムを使用できます。これは、そのための最適なアルゴリズムの1つです。

時間の複雑さ:O(n) or linear time

スペースの複雑さ:O(1) or constant space

ムーアの多数決アルゴリズム および GeeksforGeeks で詳細を読む

1
Deepak Chawla

変更されたバージョンのボイヤーのアルゴリズム、

  • 3パス、
    • 最初のパスでは、配列の前方反復を行います
    • 2番目のパスでは、配列の逆の反復を行います。
    • 3回目のパスでは、1回目と2回目のパスで取得した多数の要素の両方のカウントを取得します。

技術的には線形複雑度アルゴリズム(O(3n))。これは、少なくともn/2回発生する多数決要素を持つ配列に対して機能するはずです。

#include <iostream>
#include <vector>

template <typename DataType>
DataType FindMajorityElement(std::vector<DataType> arr) {
    // Modified BOYERS ALGORITHM with forward and reverse passes
    // Count will stay positive if there is a majority element
    auto GetMajority = [](auto seq_begin, auto seq_end) -> DataType{
        int count = 1;
        DataType majority = *(seq_begin);
        for (auto itr = seq_begin+1; itr != seq_end; ++itr) {
            count += (*itr == majority) ? 1 : -1;
            if (count <= 0) {   // Flip the majority and set the count to zero whenever it falls below zero
                majority = *(itr);
                count = 0;
            }
        }
        return majority;
    };
    DataType majority1 = GetMajority(arr.begin(), arr.end());
    DataType majority2 = GetMajority(arr.rbegin(), arr.rend());
    int maj1_count = 0, maj2_count = 0;
    // Check if any of the the majority elements is really the majority
    for (const auto& itr: arr) {
        maj1_count += majority1 == itr ? 1 : 0;
        maj2_count += majority2 == itr ? 1 : 0;
    }
    if (maj1_count >= arr.size()/2)
        return majority1;
    if (maj2_count >= arr.size()/2)
        return majority2;
    // else return -1
    return -1;
}

ここでテストされたコード

0
blueskin

分割統治を使用して、多数要素を見つけます。配列を2つの半分に分割する場合、多数決要素は半分の半分になります。先に進み、サブ配列を結合すると、多数決要素も結合配列の過半数であるかどうかを確認できます。これにはO(nlogN)の複雑さがあります。

C++の実装は次のとおりです。

#include <algorithm>
#include <iostream>
#include <vector>

using std::vector;

// return the count of elem in the array
int count(vector <int> &a, int elem, int low, int high)
{
    if (elem == -1) {
        return -1;
    }

    int num = 0;
    for (int i = low; i <= high; i++) {
        if (a[i] == elem) {
            num++;
        }
    }

    return num;
}

// return the majority element of combined sub-array. If no majority return -1
int combined(vector <int> &a, int maj1, int maj2, int low, int mid, int high)
{
    // if both sub arrays have same majority elem then we can safely say
    // the entire array has same majority elem.
    // NOTE: No majority ie. -1 will be taken care too
    if (maj1 == maj2) {
        return maj1;
    }

    // Conflicting majorities
    if (maj1 != maj2) {
        // Find the count of each maj1 and maj2 in complete array
        int num_maj1 = count(a, maj1, low, high);
        int num_maj2 = count(a, maj2, low, high);
        if (num_maj1 == num_maj2) {
            return -1;
        }
        int half = (high - low + 1) / 2;
        if (num_maj1 > half) {
            return maj1;
        } else if (num_maj2 > half) {
            return maj2;
        }
    }
    return -1;
}

// Divide the array into 2 sub-arrays. If we have a majority element, then it
// should be a majority in at least one of the half. In combine step we will
// check if this majority element is majority of the combination of sub-arrays.
// array a and low is lower index and high is the higher index of array
int get_majority_elem(vector<int> &a, int low, int high)
{
  if (low > high) return -1;
  if (low == high) return a[low];

  int mid = (low + high) / 2;

  int h1 = get_majority_elem(a, low, mid);
  int h2 = get_majority_elem(a, mid + 1, high);

  // calculate the majority from combined sub arrays
  int me = combined(a, h1, h2, low, mid, high);
  return me;
}

0
anubhavr

//配列Aが与えられたとします。//与えられた配列にすべての要素があり、各要素がKよりも小さい場合、追加の配列Bを長さK + 1で作成できます。

// 0で配列の各インデックスの値を初期化します。//指定された配列Aを反復処理し、配列値A [i]ごとに、作成された配列の対応するインデックスA [i]で値を1増やしますB.

//配列Aを反復処理した後、配列Bを反復処理して最大値を見つけます。 n/2より大きい値を見つけた場合、その特定のインデックスを返します。

// Time Complexityは、K <= nの場合O(n + K)になり、O(n)と同等になります。

//ここに、配列のすべての要素がO(K)であるという制約があります。 //各要素が100以下であると仮定すると、この場合、Kは100です。

import javax.print.attribute.standard.Finishings;
public class MajorityElement {

    private static int maxElement=100;

    //Will have all zero values initially
    private static int arrB[]=new int[maxElement+1];
    static int findMajorityElement(int[] arrA) { 
         int count = 0, i, majorityElement;
         int n=arrA.length;
         for (i = 0; i < n; i++) {
             arrB[arrA[i]]+=1;
         }

         int maxElementIndex=1;
         for (i = 2; i < arrB.length; i++){
             if (arrB[i]>n/2) {
                maxElementIndex=i;
                break;
            }
        }
        return maxElementIndex;
    }`

    public static void main(String[] args) {
         int arr[]={2,6,3,2,2,3,2,2};
         System.out.println(findMajorityElement(arr));
    }
}
0
Avi Tyagi
public class MajorityElement {

   public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int testCases = sc.nextInt();
    while(testCases-- > 0) {
        int n = sc.nextInt();
        int a[] = new int[n];
        int maxCount = 0;
        int index = -1;
        for(int i = 0 ; i < n; i++) {
            a[i] = sc.nextInt();
        }
        for(int i = 0; i < n; i++) {
            int count =0;
            for(int j = 0; j < n; j++) {
                if(a[i] == a[j])
                    count++;
            }
            if(count > maxCount) {
                maxCount = count;
                index = i;
            }
        }
        if(maxCount > n/2)
            System.out.println(a[index]);
        else
            System.out.println(-1);
    }
    sc.close();
   }

}
0

これは、2つの要素が同じ回数繰り返される場合、何も表示されない場合に役立ちます。

int findCandidate(int a[], int size)
{
int count,temp=0,i,j, maj;

for (i = 0; i < size; i++) {
count=0;      
for(j=i;j<size;j++)
{
    if(a[j]==a[i])
    count++;
}
if(count>temp)
{   
    temp=count;
    maj=i;
}
else if(count==temp)
{   
    maj=-1; 
}
}


return maj;
}
0
Shivam Dawar
public class MajorityElement
    {
       public static void main(String[] args) 
          {
             int arr[]={3,4,3,5,3,90,3,3};

              for(int i=0;i<arr.length;i++)
                {
                  int count=0;
                   int j=0;

                    while(j<arr.length-1)
                     { 
                        if(i==j)
                        j=j+1;
                          if(arr[i]==arr[j])
                            count++;
                          j++;
                                  }

                             if(count>=arr.length/2)
                               {
                              System.out.println("majority element"+arr[i]);
                                   break;
    }
    }


}

}

0
rishabh singh

これは、ベクターとマルチマップ(リピートキーを含むJSON)を使用してC++で行う方法です。

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <iterator>

using namespace std;

vector <int> majorityTwoElement(vector <int> nums) {
  // declare variables
  multimap <int, int> nums_map;
  vector <int> ret_vec, nums_unique (nums);
  int count = 0;
  bool debug = false;

  try {
    // get vector of unique numbers and sort them
    sort(nums_unique.begin(), nums_unique.end());
    nums_unique.erase(unique(nums_unique.begin(), nums_unique.end()), nums_unique.end());

    // create map of numbers and their count
    for(size_t i = 0; i < nums_unique.size(); i++){
      // get number
      int num = nums_unique.at(i);
      if (debug) {
        cout << "num = " << num << endl;
      }

      // get count of number
      count = 0;  // reset count
      for(size_t j = 0; j < nums.size(); j++) {
        if (num == nums.at(j)) {
          count++;
        }
      }

      // insert number and their count into map (sorted in ascending order by default)
      if (debug) {
        cout << "num = " << num << "; count = " << count << endl;
      }
      nums_map.insert(pair<int, int> (count, num));
    }

    // print map
    if (debug) {
      for (const auto &p : nums_map) {
          cout << "nums_map[" << p.first << "] = " << p.second << endl;
      }
    }

    // create return vector
    if (!nums_map.empty()) {
      // get data
      auto it = prev(nums_map.end(), 1);
      auto it1 = prev(nums_map.end(), 2);
      int last_key = it->first;
      int second_last_key = it1->first;

      // handle data
      if (last_key == second_last_key) {  // tie for repeat count
        ret_vec.Push_back(it->second);
        ret_vec.Push_back(it1->second);
      } else {  // no tie
        ret_vec.Push_back(it->second);
      }
    }    
  } catch(const std::exception& e) {
    cerr << "e.what() = " << e.what() << endl;
    throw -1;
  }

  return ret_vec;
}

int main() {
  vector <int> nums = {2, 1, 2, 3, 4, 2, 1, 2, 2};

  try {
    // get vector
    vector <int> result = majorityTwoElement(nums);

    // print vector
    for(size_t i = 0; i < result.size(); i++) {
      cout << "result.at(" << i << ") = " << result.at(i) << endl;
    }
  } catch(int error) {
    cerr << "error = " << error << endl;
    return -1;
  }

  return 0;
}

// g++ test.cpp
// ./a.out
0
xinthose

Bob Boyerのアルゴリズム :)を知るきっかけとなった以前の回答に感謝します。

Javaジェネリックバージョン:Boyerのアルゴリズムの修正バージョン

注:プリミティブ型の配列はラッパーを使用できます。

import com.Sun.deploy.util.ArrayUtil;
import com.Sun.tools.javac.util.ArrayUtils;

/**
 * Created by yesimroy on 11/6/16.
 */
public class FindTheMajority {

/**
 *
 * @param array
 * @return the value of the majority elements
 */
public static <E> E findTheMajority(E[] array){
    E majority =null;
    int count =0;

    for(int i=0; i<array.length; i++){
        if(count==0){
            majority = array[i];
        }
        if(array[i].equals(majority)){
            count++;
        }else{
            count--;
        }

    }

    count = 0;
    for(int i=0; i<array.length ; i++){
        if(array[i].equals(majority)){
            count++;
        }
    }

    if(count > (array.length /2)){
        return majority;
    }else{
        return null;
    }
}

public static void main(String[] args){
    String[] test_case1 = {"Roy","Roy","Roy","Ane","Dan","Dan","Ane","Ane","Ane","Ane","Ane"};
    Integer[] test_case2 = {1,3,2,3,3,3,3,4,5};

    System.out.println("test_case1_result:" + findTheMajority(test_case1));
    System.out.println("test case1 the number of majority element should greater than" + test_case1.length/2);
    System.out.println();

    System.out.println("test_case2_result:" + findTheMajority(test_case2));
    System.out.println("test case2 the number of majority element should greater than" + test_case2.length/2);
    System.out.println();
}

}

0
Roy Guanyu

ハッシュテーブルの作成が許可され、ハッシュエントリルックアップが一定であると仮定した場合、各エントリをオカレンス数に対してhash_mapするだけです。

テーブルの2番目のパスを実行すると、カウントが最も高いテーブルを取得できますが、テーブル内の要素の数が事前にわかっている場合、必要な要素数。

もちろん、要素が連続して2回出現することさえ保証することはできません。たとえば、1010101010101010101には連続した1はありませんが、多数決要素です。

要素タイプに何らかの順序付けがあるかどうかについては何も言われていませんが、明らかに、2つの同等性を比較できる必要があります。

0
CashCow
    int majorityElement(int[] num) {
       int major=num[0], count = 1;
       for(int i=1; i<num.length;i++){
           if(count==0){
               count++;
               major=num[i];
           }
           else if(major==num[i]){
                    count++;
           }
           else 
               count--;
   }            
    return major;
}

時間の複雑さO(n)

0
Rajnish