您的位置:首页 > 其它

LintCode majority numbe (主元素)

2015-12-26 13:46 393 查看

LintCode 主元素

主元素

给定一个整型数组,找出主元素,它在数组中的出现次数严格大于数组元素个数的二分之一。

给出数组[1,1,1,1,2,2,2],返回 1

挑战

要求时间复杂度为O(n),空间复杂度为O(1) 。

solution:

对于这个问题,有很多解决的方法。

Method1. 最基本的解决方法

使用两个for循环计算出每个元素出现的次数, 如果该次数大于数组元素的二分之一就返回该元素, 及伪代码为:

//arr[]
n = arr.length;  //数组元素的个数
for i = arr[0] to arr[n-1]
count = 0
for (j = arr[0] to arr[n-1]
if (arr[i] == arr[j] and i != j)
++count;
if count > size/2
return arr[i]


对于该方法,其时间复杂度为O(n2)O(n^{2}), 空间空间复杂度为O(1)O(1)。

Method2. 排序

很明显,题目中的意思是该元素一定存在。因此,可以对数组进行排序。那么arr[size/2]一定是该主元素。

/**
* @param nums: a list of integers
* @return: find a  majority number
*/
public int majorityNumber(ArrayList<Integer> nums) {
// write your code
Collections.sort(nums);
return nums.get(nums.size()/2);
}


注意:该方法只有当主元素一定存在时才有效。

如果使用快速排序对该数组进行原址排序那么其时间复杂度为O(nlogn)O(nlogn), 空间复杂度为O(1)O(1)。

Method3. 使用hashmap

使用HashMap来计算,其中key用来存储元素,而value表示元素出现的次数。

public static int majorityNumber(ArrayList<Integer> nums) {

Map<Integer, Integer> map = new HashMap<>();
for (int e : nums) {
Integer count  = map.get(e);
count = (count == null ? 1 : count+1);
if (count > nums.size()/2)
return e;
map.put(e, count);
}

return -1;
}


很显然写这篇博客的原因,并不是简简单单的为了解决该问题。而是为了学习。在题目中有个挑战:要求时间复杂度为O(n),空间复杂度为O(1) 。因此,Google时发现了Moore’s Voting Algorithm,该算法可以在O(n)O(n)解决该问题。

因此,以下内容翻译自:GeeksforGeeks Majority Element

该博客中还提到了一种解决该问题的方法,使用二叉查找树。

Method4. 二叉查找树

其中二叉查找树的数据域如下:

struct tree
{
int element;
int count;
}BST;


对于每一个元素都保存了一个计数器,用来计数每个元素出现的次数。

因此,对于数组中的元素,一个一个的插入到二叉树中。如果该元素已经存在二叉树中则计数器加一。并且判断该计数器是否大于size/2,如果是则返回该元素,否则继续插入元素。

其中,该方法的最好情况是所有的主元素都在数组中开头的位置。例如:{1, 1, 1, 1, 1, 2, 3, 4}.

时间复杂:对于一般的二叉树为O(n2)O(n^{2})。 如果使用平衡二叉查找树则时间复杂度为O(nlogn)O(nlogn)。

Method5. Moore’s Voting Algorithm

该算法有两个步骤:

1. 获取数组中出现次数最多的元素。这个过程将会确保如果存在一个主元素(majority element), 就讲该元素返回。

2. 检查从上面步奏获取的元素是否是主元素。

(1). 找出候选元素 (finding a Candidate):

我们可以使用Moore’s Voting Algorithm来找出该元素,其时间复杂度为O(n)O(n)。该算法最基本的思想就是:如果e是一个主元素, 那么我们就可以抵消所有与e不相同的元素对。并且剩下的元素一定是e。

也可以如下理解:

每次都找出一对不同的元素,从数组中删掉,直到数组为空或只有一种元素。 不难证明,如果存在元素e出现频率超过半数,那么数组中最后剩下的就只有e。

Moore’s Voting Algorithm伪代码为:

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]


Moore’s Voting Algorithm遍历了每一个元素,并且对元素a[maj_index]a[maj\_index]维持了一个计数器countcount,如果下一个元素与该元素相同那么countcount加1,如果不相同那么countcount减1。如果countcount等于0,那么把当前元素的索引i i 赋值给maj_indexmaj\_index并且设置countcount为1。

Moore’s Voting Algorithm 算法选出了一个候选元素(candidate element)。那么接下来,我们就要去检查该元素是不是一个majority element。该过程很简单并且很容易在O(n)O(n)下做到。我们只需要去检查该元素出现的次数是否大于n/2.

例如:

A[] = 2, 2, 3, 5, 2, 2, 6

1). 初始化:

maj_index = 0, count = 1 –> candidate 2 ?

i = 1;

2 = a[maj_index] –> count = 2

i = 2;

3 != a[maj_index] –> count = 1

i = 3;

5 != a[maj_index] –> count = 0

因为,count= 0, 所以改变candidate为5 –> maj_index = 3, count = 1

i = 4;

2 != a[maj_index] —> count = 0 —> candidate 2, maj_index=4, count = 1

i = 5;

2 = a[maj_index] –> count = 2

i = 6;

6 != a[maj_index] –> count = 1

那么就有majority element = 2.

(2). 检查第一步所得到的元素是不是主元素majority element

printMajority (a[], size)
1.  Find the candidate for majority
2.  If candidate is majority. i.e., appears more than n/2 times.
Print the candidate
3.  Else
Print "NONE"


注意:Moore’s Voting Algorithm 只有主元素一定存在时该算法才有效,否则该算法返回错误的结果。因此,第二步检查是有必要的!

Talk is cheap, show me the code !

// Moore’s Voting Algorithm
private static int findCandidate(ArrayList<Integer> nums) {
int maj_index = 0, count = 1;
for (int i = 1; i < nums.size(); ++i) {
if (nums.get(i).equals(nums.get(maj_index)))
count++;
else
count--;
if (count == 0) {
maj_index = i;
count = 1;
}
}

return nums.get(maj_index);
}

private static boolean isMajority(int [] nums, int cand) {
int count = 0;
for (int e : nums) {
if (e == cand)
++count;
}

return count > nums.length / 2 ;
}


Moore Voting Alogrithm 可以更加泛化的求解数组中至少出现n/kn/k次的元素,详情请看Moore’s Voting Algorithm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: