【算法31】寻找数组的主元素(Majority Element)
题外话
最近有些网友来信问我博客怎么不更新了,是不是不刷题了,真是惭愧啊,题还是在刷的,不过刷题的频率没以前高了,看完《算法导论》后感觉网上很多讨论的题目其实在导论中都已经有非常好的算法以及数学证明,只是照搬的话好像意义也不是很大,希望找到些有代表性的题目在更新,另外希望能接着前面的《穷举递归和回溯算法终结篇》一系列如动态规划、贪心算法类的终结篇,在梳理自己知识结构的同时也能够帮助读者们更系统的学习算法思想。好了话不多说,进入正题。
问题描述
给定一个数组A[n], 定义数组的主元素 ( Majority Element) 为数组中出现次数超过 n/2 的元素。设计一个高效的算法来寻找数组的主元素。题目来源在这里 。
解法一
最容易想到的方法就是便利数组进行元素计数,然后返回元素个数大于 n/2 的元素,这种方法需要 O(n) 的时间复杂度 和 O(n) 空间复杂度,不算是一个好方法。
解法二
在解法一的基础上考虑消去 O(n) 的空间复杂度,如果元素出现次数超过 n/2,那么假设数组已经排序的话,那么中位数就是我们要找的数。进一步的我们除了中位数,我们不需要其他的数排好序。问题进一步转化为求数组的中位数,推广版本就是在O(n)的时间内寻找第 i 大的数,这在算法导论上有详细的论述,网上资料也很多。基本来说,就是利用快排的 partition 对数组进行划分,分为 [..., pivot, ...] 三个部分,假设划分后 pivot 是第 m 个元素,如果 m == i, 则pivot 即为第 i 大元素;反之对如果 pivot 的位置在大于 i (m > i),则对 left 部分进行递归寻找第 i 大元素;反之对 right 部分进行递归寻找第 (i - m) 大元素。代码如下, 然而这种算法在大数组的情况下回超时。
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <queue>
#include <stack>
#include <algorithm>
#include <functional>
#include <utility>
#include <cstdio>
#include <cstdlib>
using namespace std; /*
* Return a position k, such that all the elements in [left, k)
* are smaller than or equal to num[k] && all the elements in
* (k, right] are larger than num[k]
*
* Note that here is the randomize version of partition, every time
* we choose a random number as the pivot.
*/
int RandomPartition(vector<int> &num, int left, int right)
{
// random partition
int rnd = rand() % (right - left + ) + left;
swap(num[rnd], num[right]); // pivot
int x = num[right];
int i = left - ;
for (int j = left; j < right; ++j)
{
if (num[j] <= x)
{
swap(num[j], num[i + ]);
i += ;
}
}
swap(num[right], num[i+]);
return i + ;
} /*
* Return the i-th ordered element in num[left, right] but without
* sorting the array.
*/ int RandomSelect(vector<int> &num, int left, int right, int i)
{
if (right - left + < i || left > right) return -; // partition the num[], return the pivot position m
int m = RandomPartition(num, left, right); // k is the number of element in num[left, m]
int k = m - left + ; if (k == i)
{
return num[m];
}
else if (k > i)
{
// find the i-th ordered element in num[left, m-1]
return RandomSelect(num, left, m - , i);
}
else
{
// find the (i-k)-th ordered element in num[m+1, right]
return RandomSelect(num, m + , right, i - k);
}
} /*
* return the median of num[]
*/
int majorityElement(vector<int> &num)
{
int n = num.size();
int mid = n / ;
return RandomSelect(num, , n-, mid);
} int main()
{
int a[] = {, , };
int b[] = {, , , }; vector<int> v1(a, a + );
vector<int> v2(b, b + ); cout << majorityElement(v1) << endl;
cout << majorityElement(v2) << endl; return ;
}
解法三
这种方法的思想是把 majority element 看成是 1,而把其他的元素看成是 -1。算法首先取第一个元素 x 作为 majority element,并计 mark = 1;而后遍历所有的元素,如果元素和 x 相等, 则 mark ++;否则如果不等, 则 mark--, 如果 mark == 0, 则重置 mark = 1, 并且更新 x 为当前元素。 由于majority element 的数量大于一半,所以最后剩下的必然是majority element. AC code 如下.
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <queue>
#include <stack>
#include <algorithm>
#include <functional>
#include <utility>
#include <cstdio>
#include <cstdlib>
using namespace std; int majorityElement(vector<int>& num)
{
int n = num.size();
if (n < ) return -;
if (n == ) return num[]; int x = num[];
int mark = ;
for (int i = ; i < n; ++i)
{
if (mark == )
{
mark = ;
x = num[i];
}
else if (num[i] == x)
{
mark++;
}
else if (num[i] != x)
{
mark--;
}
}
return x;
} int main()
{
int a[] = {, , };
int b[] = {, , , }; vector<int> v1(a, a + );
vector<int> v2(b, b + ); cout << majorityElement(v1) << endl;
cout << majorityElement(v2) << endl; return ;
}
参考文献
[1] 《算法导论》第二版,第九章 《中位数和顺序统计学》.
[2] http://people.cis.ksu.edu/~subbu/Papers/Majority%20Element.pdf
【算法31】寻找数组的主元素(Majority Element)的更多相关文章
- [经典算法题]寻找数组中第K大的数的方法总结
[经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26 字体:[大 中 小] 打印复制链接我要评论 今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...
- Java实现 蓝桥杯 算法训练 寻找数组中最大值
算法训练 寻找数组中最大值 时间限制:1.0s 内存限制:512.0MB 提交此题 问题描述 对于给定整数数组a[],寻找其中最大值,并返回下标. 输入格式 整数数组a[],数组元素个数小于1等于10 ...
- 减治算法之寻找第K小元素问题
一.问题描写叙述 给定一个整数数列,寻找其按递增排序后的第k个位置上的元素. 二.问题分析 借助类似快排思想实现pation函数.再利用递归思想寻找k位置. 三.算法代码 public static ...
- 算法导论 寻找第i小元素 9.2
PS1:如果单纯为做出这道题那么这个代价是O(nlgn),通过排序就可以了. 这里讨论的是O(n)的算法.那么来分析一下这个算法是如何做到O(n)的,算了不分析了,这个推到看起来太麻烦了.其实我想知道 ...
- 主元素问题 Majority Element
2018-09-23 13:25:40 主元素问题是一个非常经典的问题,一般来说,主元素问题指的是数组中元素个数大于一半的数字,显然这个问题可以通过遍历计数解决,时间复杂度为O(n),空间复杂度为O( ...
- lintcode 中等题:Majority number II 主元素 II
题目 主元素II 给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的三分之一. 样例 给出数组[1,2,1,2,1,3,3] 返回 1 注意 数组中只有唯一的主元素 挑战 要求时 ...
- Ex 2_23 如果一个数组超过半数的元素都相同时,该数组被称为含有一个主元素..._第二次作业
将数组A划分为两个数组A1和A2 ,各含有A的一半元素或一半多一个.若A中含有主元素x,则A1和A2中至少有一个数组含有主元素x,对A1和A2递归地计算有无主元素,若A只含有一个元素,则A的主元素就是 ...
- Leetcode算法【34在排序数组中查找元素】
在之前ARTS打卡中,我每次都把算法.英文文档.技巧都写在一个文章里,这样对我的帮助是挺大的,但是可能给读者来说,一下子有这么多的输入,还是需要长时间的消化. 那我现在改变下方式,将每一个模块细分化, ...
- 寻找数组中第K频繁的元素
问题是:给你一个数组,求解出现次数第K多的元素.当然leetcode上的要求是算法复杂度不能大于O(N*logN). 首先这个问题我先是在leetcode上看到,当时想了两种做法,做到一半都觉得不是很 ...
随机推荐
- 查询中mybatis的if判断里传入0
1.传入的是long 或者 Integer类型 ,<if test="id != null "> 但是id传值为0时(前提是id对应的类型为long 或者 Intege ...
- IIS7配置下载apk以及目录浏览
IIS7为了增加安全性,如果需要禁止目录浏览.只需要按下面的步骤执行就可以 1.选择站点:2.选择功能视图:3.选择IIS下面的目录浏览:4.在右上角的操作中选择“打开功能”:5.选择右边的禁用. 今 ...
- jquery获取input file的文件名,具有兼容性
var str=$(this).val();var arr=str.split('\\');//注split可以用字符或字符串分割var fileName=arr[arr.length-1];//这就 ...
- racktables 的介绍及搭建指南
Racktables RackTables称自己为一个“机架空间.IP地址.服务器.交换机.路由器等 的管理框架”.它拥有一个web界面,执行报告和配置,并管理名字服务.RackTables以PHP5 ...
- phpStudy3——往数据库中添加数据
前言: 前边介绍了查询数据库的方法,这里介绍下往数据库中添加数据的方法. 项目需求: 用户在前端页面输入的用户名和手机号码,点击提交后后端判断手机号码是否已经存在.如果不存在,那么插入数据库到数据库, ...
- python之多线程队列
# 一共有以下3种队列# 1.先进先出# 2.后进先出# 3.存储数据的时候可设置优先级的队列,设置不同的优先级,取的时候按照优先级的顺序来取 下面介绍一下队列的方法,如果要使用队列,则需要导入一个模 ...
- memcache简单操作
<?php $m = new Memcache(); $m->connect('localhost',11211); //获取版本 echo "server's version: ...
- Spring整合JMS——事务管理
Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理.这将允许JMS应用利用Spring的事务管理特性.JmsTransactio ...
- 使用jdbc编程实现对数据库的操作以及jdbc问题总结
1.创建数据库名为mybatis. 2. 在数据库中建立两张表,user与orders表: (1)user表: (2)orders表: 3.创建工程 * 开发环境: * eclipse mars * ...
- css样式优先级和权重问题
内联样式: <div style="font-size: 12px;">姓名</div> 外部样式: <link rel="styleshe ...