所谓求众数,就是求数组中出现次数超过n/2的数。
之前剑指中做过同一道题:https://blog.csdn.net/chengda321/article/details/93139959
学习花花酱的视频讲解,共有5类解法,10种写法。
第一类:基于哈希表的方法
第1种 Hash Table:对应STL容器unordered_map
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int, int> count;
const int n = nums.size();
for(int &num : nums) {
if(++count[num]>n/2) return num;
}
return -1;
}
};
第2种 BST:二叉搜索树,在C++ STL容器中对应map
class Solution {
public:
int majorityElement(vector<int>& nums) {
map<int, int> count;
const int n = nums.size();
for(int &num : nums) {
if(++count[num]>n/2) return num;
}
return -1;
}
};
另外附上一种简单的BST递归实现
struct node {
int val;
node* left;
node* right;
};
node* createNewNode(int x)
{
node* nn = new node;
nn->val = x;
nn->left = nullptr;
nn->right = nullptr;
return nn;
}
void bstInsert(node* &root, int x)
{
if(root == nullptr) {
root = createNewNode(x);
return;
}
if(x < root->val)
{
if(root->left == nullptr) {
root->left = createNewNode(x);
return;
} else {
bstInsert(root->left, x);
}
}
if( x > root->val )
{
if(root->right == nullptr) {
root->right = createNewNode(x);
return;
} else {
bstInsert(root->right, x);
}
}
}
int main()
{
node* root = nullptr;
int x;
while(cin >> x) {
bstInsert(root, x);
}
return 0;
}
第二类:基于随机数的方法
方法3 从统计学意义上讲,平均每随机2次就可以得到众数
class Solution {
public:
int majorityElement(vector<int>& nums) {
srand(time(nullptr));
const int n = nums.size();
while(true) {
int index = rand()%n; //随机产生一个索引
int majority = nums[index]; //假设它就是众数
int count = 0;
for(int & num : nums) {
if(num == majority && ++count>n/2) return num; //此处用到了逻辑与&&的短路特性
}
}
//return -1; 写不写都可,因为这道题假设存在众数
}
};
第三类:vote-based
方法4 bit vote 思路:众数的每一个二进制位都是众数
class Solution {
public:
int majorityElement(vector<int>& nums) {
const int n = nums.size();
int majority = 0;
for(int i=0; i<32; i++) { //32bits
int mask = 1 << i ; //错把1打成i
int count = 0;
for(int& num : nums) {
if((mask & num) && ++count>n/2) {
majority |= mask;
break;
}
}
}
return majority;
}
};
方法5 Moore vote 不同的数对消,最后留下的一定是众数
class Solution {
public:
int majorityElement(vector<int>& nums) {
const int n = nums.size();
int count = 1;
int majority = nums[0]; //首先,认为第一个元素就是众数
for(int i=1; i<n; i++) {
if(count==0) {
count = 1;
majority = nums[i];
}
else {
if(majority == nums[i]) count++;
else count--;
}
}
return majority;
}
};
第四类 基于排序的方法
方法6 Full Sort 排序,取中间的数
class Solution {
public:
int majorityElement(vector<int>& nums) {
const int n = nums.size();
std::sort(nums.begin(), nums.end());
return nums[n/2];
}
};
方法7 Partial Sort 与快排中的经典的Partial思想一样 (?)
class Solution {
public:
int majorityElement(vector<int>& nums) {
const int n = nums.size();
std::nth_element(nums.begin(), nums.begin()+n/2, nums.end());
return nums[n/2];
}
};
方法8 自己实现Partial函数
自己实现的方法,总有测试用例超过时间限制
class Solution {
public:
int majorityElement(vector<int>& nums) {
int n = nums.size();
int mid = Partition(nums, 0, n-1);
while(mid!=n/2) {
if(mid > n/2)
mid = Partition(nums, 0, mid-1);
else
mid = Partition(nums, mid+1, n-1);
}
return nums[mid];
}
private:
//经典的Partition函数实现
int Partition(vector<int>& nums, int left, int right) {
srand(time(nullptr));
int index = left + rand() % (right-left+1);
Swap(nums[index], nums[right]);
int small = left-1;
for(int i = left; i<right; ++i) {
if(nums[i] < nums[right]) {
++small; //又增加了一个比 标准数 小的数
if(small < i) {
Swap(nums[small], nums[i]); //出现的第small个比标准数小的,0-small位都应该是比标准数小的数
}
}
}
Swap(nums[small+1], nums[right]);
return small+1; //把分界线的index返回
}
void Swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
};
第五类 分治法
方法9 不断缩小问题规模,查找左右子集的众数,并返回给上一级
class Solution {
public:
int majorityElement(vector<int>& nums) {
const int n = nums.size();
return majorityElement(nums, 0, n-1);
}
private:
int majorityElement(vector<int>& nums, int l, int r) {
if(l == r) return nums[l]; //分到最后只剩下1个元素
int mid = l + (r - l)/2;
int ml = majorityElement(nums, l, mid);
int mr = majorityElement(nums, mid+1, r);
if(ml == mr) return ml;
return count(nums.begin()+l, nums.begin()+r+1, ml) > count(nums.begin()+l, nums.begin()+r+1, mr) ? ml : mr;
}
};
方法10 左右子集的众数不同时,不需要在全部集合上对两个目标计数来判定哪个为众数。
只需要计数一侧就可以:需要把众数出现的次数作为pair的一部分一起返回。
class Solution {
public:
int majorityElement(vector<int>& nums) {
const int n = nums.size();
return majorityElement(nums, 0, n-1).first;
}
private:
pair<int, int> majorityElement(vector<int>& nums, int l, int r) {
if (l == r) return {nums[l], 1};
int mid = l + (r-l)/2;
auto ml = majorityElement(nums, l, mid);
auto mr = majorityElement(nums, mid+1, r);
if(ml.first == mr.first) return {ml.first, ml.second + mr.second}; //等号打成了赋值号,样例运行没错,但后面的会出错
if(ml.second > mr.second) {
return {ml.first, ml.second + count(nums.begin()+mid+1, nums.begin()+r+1, ml.first)};
}
else {
return {mr.first, mr.second + count(nums.begin()+l, nums.begin()+mid+1, mr.first)};
}
}
};