多数決要素は、配列のサイズの半分を超える要素です。
O(n)
の配列で多数決要素を見つける方法
入力例:
{2,1,2,3,4,2,1,2,2}
期待される出力:
2
多数要素(存在する場合)も中央値になります。 O(n)で中央値を見つけ、それが実際にO(n)の有効な多数決要素であることを確認します。実装の詳細 link
// 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;
}
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年にボイヤー、ムーアによって発明され、 ボイヤー–ムーア多数決アルゴリズム と呼ばれました。
多数派要素:
サイズ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 をお読みください
時間:O(n)
スペース:O(n)
ツリーを歩いて、ハッシュテーブル内の要素の出現をカウントします。
時間:O(n lg n)またはO(n * m)(使用するソートに依存)
スペース:(1)
配列を並べ替えてから、要素の出現をカウントします。
インタビューの正解:ムーアの投票アルゴリズム
時間:O(n)
スペース:O(1)
リストを歩いて、現在の数と現在の最良の推測数を比較します。数が現在の最良の推測数と等しい場合はカウンターをインクリメントし、そうでない場合はカウンターをデクリメントし、カウンターがゼロにヒットした場合は現在の最良の推測数を現在の数で置き換え、カウンターを1に設定します。最良の推測は候補者番号です。候補者のインスタンスを数えるだけでリストをもう一度調べてください。最終カウントがn/2より大きい場合、それは多数決です。それ以外の場合はありません。
ランダムサンプリングアプローチはどうですか?たとえば、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)過半数以外の他の要素は明確でない可能性があるため、追加の検証手順が必要です。
モンテカルロアルゴリズムでは、
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)
}
配列内の要素の大部分を見つけるには、ムーアの多数決アルゴリズムを使用できます。これは、そのための最適なアルゴリズムの1つです。
時間の複雑さ:O(n) or linear time
スペースの複雑さ:O(1) or constant space
ムーアの多数決アルゴリズム および GeeksforGeeks で詳細を読む
変更されたバージョンのボイヤーのアルゴリズム、
技術的には線形複雑度アルゴリズム(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;
}
分割統治を使用して、多数要素を見つけます。配列を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;
}
//配列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));
}
}
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();
}
}
これは、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;
}
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;
}
}
}
}
これは、ベクターとマルチマップ(リピートキーを含む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
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();
}
}
ハッシュテーブルの作成が許可され、ハッシュエントリルックアップが一定であると仮定した場合、各エントリをオカレンス数に対してhash_mapするだけです。
テーブルの2番目のパスを実行すると、カウントが最も高いテーブルを取得できますが、テーブル内の要素の数が事前にわかっている場合、必要な要素数。
もちろん、要素が連続して2回出現することさえ保証することはできません。たとえば、1010101010101010101には連続した1はありませんが、多数決要素です。
要素タイプに何らかの順序付けがあるかどうかについては何も言われていませんが、明らかに、2つの同等性を比較できる必要があります。
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)