-
题目:
找出一个数组中出现次数超过一半的数字;
当不存在该目标数时,返回0; -
思路:
1.(最优解)摩尔投票:O(n):需要遍历两次,一次用来找候选人,第二次用来验证候选人是否真的超过半数,O(1)
目标数个数超过一半,哪怕剩下所有数联合起来对付我也是我赢,更何况其它数也可能内讧;
class Solution {
public:
int majorityElement(vector<int>& nums) {
int cnt = 0, val;//当前目标数val,以及一个计数器cnt
for (auto x : nums) {
if (cnt == 0) {
//第一个数,或抵消过后的下个数
val = x;
cnt = 1;
}
else if (x == val) ++cnt;
else --cnt;
}
//若该数组真的存在超过半数的数,则一定是val;
//但也可能数组中不存在这样的数,需要第二次遍历来确认
int len = nums.size();
cnt = 0;
for (auto x : nums)
if (x == val) ++cnt;
if (cnt > len / 2) return val;
else return 0;
}
};
//类似的题,[求众数Ⅱ](https://leetcode-cn.com/problems/majority-element-ii/),区别在于最多要找两个数(最多有两个数的个数超过1/3)
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
int mcnt = 0, m = 0;
int ncnt = 0, n = 0;
for (auto x : nums) {
//第一次遍历,找候选人m,n; 以下if-else的顺序有东西, m,n的初始化也有东西
if (x == m) ++mcnt;//唱到m,就累加mcnt
else if (x == n) ++ncnt;//唱到n,就累加ncnt
else if (mcnt == 0) m = x, mcnt = 1;
else if (ncnt == 0) n = x, ncnt = 1;
else --mcnt, --ncnt;//新出现了个竞争对手,则目前票数最高的俩人的优势减弱一票;
}
//重新遍历一次,验证m和n是否真的超过len/3
mcnt = 0, ncnt = 0;
for (auto x : nums) {
if (x == m) ++mcnt;
else if (x == n) ++ncnt;
}
vector<int> ans;
int len = nums.size();
if (mcnt > len / 3) ans.push_back(m);
if (ncnt > len / 3) ans.push_back(n);
return ans;
}
};
2.位运算:O(n):实际位O(32n):循环32次,每次循环确定目标数二进制的一位,O(1)
class Solution {
public:
int majorityElement(vector<int>& nums) {
int n = nums.size();
int ans = 0;//初始32位都为0,把它逐位改造成目标数
for (int i = 0; i < 32; ++i) {
int cnt = 0;
for (auto x : nums) {
//判断目标数的第i位是1还是0
if (x & (1 << i)) ++cnt;//如果x的第i位是1,就累加
}
if (cnt > n / 2) ans ^= (1 << i);//若统计第i位为1的数超过半数,说明目标数的第i位就是1,那就把ans的第i位置为1;否则说明目标数的第i位是0,那不动(本身就为0)
}
return ans;
}
};
3.排序:O(nlogn),O(1)
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
return nums[n / 2];//目标数这一段无论排在数组的哪一段,最中间处肯定都是目标数
}
};
4.哈希表:O(n),O(n)
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int, int> um;//<元素值,对应出现的次数>
int n = nums.size();
for (auto x : nums) {
if (++um[x] > n / 2) return x;
}
return -1;//说明没有个数超过一半的数
}
};