题外话

最近有些网友来信问我博客怎么不更新了,是不是不刷题了,真是惭愧啊,题还是在刷的,不过刷题的频率没以前高了,看完《算法导论》后感觉网上很多讨论的题目其实在导论中都已经有非常好的算法以及数学证明,只是照搬的话好像意义也不是很大,希望找到些有代表性的题目在更新,另外希望能接着前面的《穷举递归和回溯算法终结篇》一系列如动态规划、贪心算法类的终结篇,在梳理自己知识结构的同时也能够帮助读者们更系统的学习算法思想。好了话不多说,进入正题。

问题描述

给定一个数组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)的更多相关文章

  1. [经典算法题]寻找数组中第K大的数的方法总结

    [经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26   字体:[大 中 小] 打印复制链接我要评论   今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...

  2. Java实现 蓝桥杯 算法训练 寻找数组中最大值

    算法训练 寻找数组中最大值 时间限制:1.0s 内存限制:512.0MB 提交此题 问题描述 对于给定整数数组a[],寻找其中最大值,并返回下标. 输入格式 整数数组a[],数组元素个数小于1等于10 ...

  3. 减治算法之寻找第K小元素问题

    一.问题描写叙述 给定一个整数数列,寻找其按递增排序后的第k个位置上的元素. 二.问题分析 借助类似快排思想实现pation函数.再利用递归思想寻找k位置. 三.算法代码 public static ...

  4. 算法导论 寻找第i小元素 9.2

    PS1:如果单纯为做出这道题那么这个代价是O(nlgn),通过排序就可以了. 这里讨论的是O(n)的算法.那么来分析一下这个算法是如何做到O(n)的,算了不分析了,这个推到看起来太麻烦了.其实我想知道 ...

  5. 主元素问题 Majority Element

    2018-09-23 13:25:40 主元素问题是一个非常经典的问题,一般来说,主元素问题指的是数组中元素个数大于一半的数字,显然这个问题可以通过遍历计数解决,时间复杂度为O(n),空间复杂度为O( ...

  6. lintcode 中等题:Majority number II 主元素 II

    题目 主元素II 给定一个整型数组,找到主元素,它在数组中的出现次数严格大于数组元素个数的三分之一. 样例 给出数组[1,2,1,2,1,3,3] 返回 1 注意 数组中只有唯一的主元素 挑战 要求时 ...

  7. Ex 2_23 如果一个数组超过半数的元素都相同时,该数组被称为含有一个主元素..._第二次作业

    将数组A划分为两个数组A1和A2 ,各含有A的一半元素或一半多一个.若A中含有主元素x,则A1和A2中至少有一个数组含有主元素x,对A1和A2递归地计算有无主元素,若A只含有一个元素,则A的主元素就是 ...

  8. Leetcode算法【34在排序数组中查找元素】

    在之前ARTS打卡中,我每次都把算法.英文文档.技巧都写在一个文章里,这样对我的帮助是挺大的,但是可能给读者来说,一下子有这么多的输入,还是需要长时间的消化. 那我现在改变下方式,将每一个模块细分化, ...

  9. 寻找数组中第K频繁的元素

    问题是:给你一个数组,求解出现次数第K多的元素.当然leetcode上的要求是算法复杂度不能大于O(N*logN). 首先这个问题我先是在leetcode上看到,当时想了两种做法,做到一半都觉得不是很 ...

随机推荐

  1. InnoDB FULLTEXT

    1.概要 InnoDB引擎对FULLTEXT索引的支持是MySQL5.6新引入的特性,之前只有MyISAM引擎支持FULLTEXT索引.对于FULLTEXT索引的内容可以使用MATCH()…AGAIN ...

  2. 登录时显示403 Access Denied

    用户名及密码设置如下: 在tomcat安装目录\conf\tomcat-users.xml中的<tomcat-users>标签内设置: <role rolename="ma ...

  3. 元素的定位tag_name,link_text,class_name

    tag_name 就是根据HTML的标签的名称来定位的: 案例:打开我要自学网,会有用户名和密码的输入框 例如:拿51zxw.net为例 from time import sleep #加载浏览器驱动 ...

  4. 元素的定位id和name

    1.元素定位: 元素的定位是自动化测试的核心,要想操作一个元素,首先应该识别这个元素 webdriver提供了一系列的元素定位方法,常用的有以下几种 id name class name partia ...

  5. springboot CommandLineRunner

    @SpringBootApplicationpublic class Application implements CommandLineRunner { public static void mai ...

  6. ASP.NET中修改从数据库获取的datatable中的值

    有些时候,我们从数据库表中获取一个实体的对象,但有些内容并不是最终显示的内容,格式也都是不一样.经过一番尝试,发现datatable中的数值如果跟想要改变的类型不一致,就无法更改,只有添加新列,然后把 ...

  7. 使用DW工具给图片添加热点MAP

    一.准备一张图片.     准备一张需要给不同区域添加不同热点的图片. 二.插入图片: 打开Dreamweaver,新建一个网页,将图片插入到页面中. 三.找到地图工具: 单击鼠标左键点击图片,这时候 ...

  8. 18-javaweb-ssm 开发中错误总结

    由于web课设于是,写了几天的javaweb,在写的过程中总会遇到奇奇怪怪的一些bug, 一般都得花很多时间解决. 但是解决多了,后面碰到类似的简单多了. 总结下: 一.前端错误: 1.js错误,看前 ...

  9. JTemplate学习(一)

    使用模板绑定数据,可以嵌套循环 参考:http://www.doc88.com/p-6621237324128.html <!DOCTYPE html PUBLIC "-//W3C// ...

  10. YAML教程

    一.简介 YAML是一种人们可以轻松阅读的数据序列化格式,并且它非常适合对动态编程语言中使用的数据类型进行编码.YAML是YAML Ain't Markup Language简写,和GNU(" ...