题目描述
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
这道题很简单,有多种思路,在这里我主要介绍四种,很有意思的一种思路叫做摩尔投票。这个是从网上借鉴的,很不错的一种思路,用来求解次数大于 ⌊ n/k⌋ 的元素数(k=2,3..)这类题很方便。
第一种思路:这道题就是求数组中出现次数最多的元素,而且还给出了众数的定义,就是在数组中次数大于数组容量一半的数。这样来看,我们就可以使用map来做,键为数组元素,值为出现的次数,这样最后判断次数大于n/2的那个数即为众数。
第二种思路:用栈来做,这种思路也很巧妙,将数组元素从第一开始入栈,当遇到不相同元素时出栈,相同元素时入栈,这样栈中剩余的那个元素就是众数。(因为剩余的这个元素他存在的次数是最多的)
第三种思路:就是先将数组排序(取决于排序方法,这里我用的内置排序函数,当然可以用其他的快排,归并等等),取中间的那个数,因为最多的那个数总是在中间。
第四种思路:就是摩尔投票,也可以叫做多数投票算法,用这个方法我们可以达到线性的时间复杂度以及常量级的空间复杂度,具体:摩尔投票法的基本思想很简单,在每一轮投票过程中,从数组中找出一对不同的元素,将其从数组中删除。这样不断的删除直到无法再进行投票,如果数组为空,则没有任何元素出现的次数超过该数组长度的一半。如果只存在一种元素,那么这个元素则可能为目标元素。
这个地方说明一下,一个数组中出现次数大于数组长度一半的数只有一个,不可能出现最后剩下2种元素以上的情况,加入这样的话我们可以再进行一次投票。
第一种方法代码如下:
// 第一种 思路很简单,用hashmap,键为数组元素,值为数组元素出现的词数
int a[]={2,2,1,1,1,2,2};
Map<Integer,Integer> map =new HashMap <> ( );
for (int i=0;i<a.length;i++){
if (map.containsKey ( a[i] )){
map.put ( a[i] ,map.get ( a[i] )+1);
}else {
map.put ( a[i] ,1);
}
}
int num=a.length/2;//超过这个的才是众数
//遍历map,进行判断
for (Integer result:map.keySet ()){
if (map.get ( result )>num){
System.out.println("众数为:"+result);
break;
}
}
执行结果:
执行用时:
第二种方法:
//第二种,用栈:如果栈为空或者数组元素等于栈顶元素时,将数组元素入栈
// 如果当数组元素不等于栈顶元素时出栈,这样,最后栈中留下来的元素就肯定是存在最多的数。返回栈定元素即可。
Stack<Integer> stack =new Stack <> ();
// stack.peek ()返回栈顶元素但不删除(出栈但不删除元素)
for (int i=0;i<a.length;i++){
if (stack.empty ()||a[i]==stack.peek ()){
stack.push ( a[i] );
}
else {
stack.pop ();
}
}
System.out.println(stack.peek ());
执行用时:
第三种方法:
Arrays.sort ( a );//重点在排序方法上,用快速排序,堆排序都可以。这里用到内置函数排序
System.out.println(a[a.length/2]);
执行用时:
第四种方法:
//摩尔投票(借鉴):在任何数组中,出现次数大于该数组长度一半的值只能有一个。
// 在每一轮投票过程中,从数组中找出一对(不同的元素),将其从数组中删除。这样不断的删除直到无法再进行投票,如果数组为空,则没有任何元素出现的次数超过该数组长度的一半。如果只存在一种元素,那么这个元素则可能为目标元素。(最终只能是剩下零个或一个元素。)
/**
* 程序开始之前,元素c为空,f(c)=0。遍历数组A:
* * 如果f(c)为0,表示截至到当前子数组,并没有候选元素。也就是说之前的遍历过程中并没有找到超过半数的元素。那么,如果超过半数的元素c存在,那么c在剩下的子数组中,出现次数也一定超过半数。因此我们可以将原始问题转化为它的子问题。此时c赋值为当前元素, 同时f(c)=1。
*
* * 如果当前元素A[i] == c, 那么f(c) += 1。(没有找到不同元素,只需要把相同元素累计起来)
*
* * 如果当前元素A[i] != c,那么f(c) -= 1 (相当于删除1个c),不对A[i]做任何处理(相当于删除A[i])
*/
int moer =0;//当前判断的元素
int count=0;//计算当前的数字出现的次数
for (int i=0;i<a.length;i++){
if (count==0){//当次数为0时,则换下一判断元素
moer=a[i];
count++;
}else if(moer==a[i]){
count++;//当前元素等于判断元素,次数加一
}else {
count--;//不等于则次数减一
}
}
System.out.println(moer);
执行用时:
总结:
这道题很简单有多种方法,学习了一种新的算法,摩尔投票法,用来找数组中超过一般次数的数十分有效。
2019-7-26