169.多数元素
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
首先,拿到题目抓重点,他说多数元素出现次数大于一半,说明什么??说明它是王者,即便它一对一和其他所有值单打最后都能留一个站在场上。
方法一:
那么看到“在数组中出现次数 大于 ⌊ n/2 ⌋ ”这句话,在我们脑子里第一印象一定是这样的:[(()^*********************#¥@!……&],对不对?那么就会想到让一样的排排站,这样就不得不想到排序,既然它数量过半,不论它多大,这一坨一样的数字排好序后都会在中间出现对不对,最次:[1,1,1,1,1,1,2,3,4,5,6],刚好多一个,所以我们可以排完序后取中间值,这个数一定是多数元素。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int n=nums.size();//数组元素个数
std::sort(nums.begin(), nums.end());//排序
if(n%2==0)//偶数个元素,取下标等于n/2的值
return nums[n/2];
else//奇数个元素,取下标等于(n-1)/2的值
return nums[(n-1)/2];
}
};
关于就判断这里,要是没看懂,举个例子nums1=[1,1,1,1,1,1,2,3,4,5,6],n=11,奇数个元素,加粗元素位置一定是多数元素,他的下标是5=(n-1)/2=(11-1)/2;nums2=[1,1,1,1,1,1,1,2,3,4,5,6],n=12,偶数个元素,加粗元素位置一定是多数元素,他的下标是6=n/2=12/2,记住不会有nums3=[1,1,1,1,1,2,3,4,5,6]这种情况,因为不符合题目要求“在数组中出现次数 大于 ⌊ n/2 ⌋”
方法一用了sort有个缺陷,我们只是简单的调了这个方法感觉很便利,但是计算机要累死了,那可是全排啊,费老大劲了。
那么方法二来了,我上边提到一句话“它一对一和其他所有值单打最后都能留一个站在场上”,这便衍生出一个进阶方法,那我们就让它和所有元素打一架吧,谁留到最后谁就是王者。
举个例子:
战场一:nums1=[1,1,1,1,1,1,2,3,4,5,6],我们让1和其他元素打架,由于它自带主角光环,天生能量多,所以,1🤺2,1🤺3,1🤺4,1🤺5,1🤺6过后还剩一个1在战场,那么毋庸置疑1是最多的。
现在我们来到第二战场:nums2=[1,1,2,1,3,1,1,4,5,6,1],从头开始遍历,两个1,遇到一个2,1🤺2战死一个1,此时剩下一个1了,向后走又遇到了1,此时剩下两个1,遇到3后1🤺3战死一个,还剩一个1,接着又是俩1,目前场上仨1,经过1🤺4,1🤺5,1🤺6后没有1了,但是最后一个数是1,所以最终留在场上的还是1;
现在看第三战场:nums3=[2,1,3,1,1,4,5,6,1,1,1],假设此时我们不知道谁会是冠军,开始遍历,上来一个2,不管了,立2为大将军(假设2是多数元素),然后2🤺1,大将军没了(此时不能说明2不是大将军,万一后边还有2的千军万马呢),大将军难道是后边的3?假装是吧,然后3🤺1死了,在看nums[4]与nums[5],他俩也不一样,碰死,然后是5和6也碰死,到此时为止前边碰死的元素(2,1,3,4,5,6)都没援军,那么接下来好日子来了,1的援军到了,最后留在场上的1是最多的,1胜了。
提问提问:为什么战场二只剩一个1,战场三剩下三个???
答:战场二其实是最坏情况,就是每一个少数元素都是用一个1抵消的,那么之能剩下一个,而战场三有不同元素的内耗,比如5🤺6这种情况,就不用消耗1了,但凡5或者6是多数元素,到后边也会和1抵消的,从而留下5或6。
简单查了下这个思想叫候选人算法,实际上场景更复杂,有时间自己看看吧,别问我,我已经蒙了!!!
class Solution {
public:
int majorityElement(vector<int>& nums) {
//候选人法
int count=1,candidate=nums[0];//candidate就是多数元素,初始假设就是第一个元素,count就是它出现的次数
for(int i=1;i!=nums.size();i++)
{
if(nums[i]==candidate)//遇到一次就++
count++;
if(nums[i]!=candidate)//碰死一次--
count--;
if(count<0)//数量小于0,换人
{
candidate=nums[i];
count=1;//换人初始为1,毕竟人家还没跟人打仗呢,数量1
}
}
return candidate;
}
};
关于下边这块,新的候选人为什么是nums[i],而不是nums[i+1]?
if(count<0)//数量小于等于0,换人
{
candidate=nums[i];
count=1;//换人初始为1,毕竟人家还没跟人打仗呢,数量1
}
有两点原因:
1.i+1容易数组越界,因为i的范围最大值是数组末尾,i+1是越界的;
2.用nums=[2,2,1,1,1,2,2]举例子,初始候选人是2,当经历两个2🤺1后,2的个数为0,我们可以继续把2假设成候选人,但是又遇到一个1此时i=4,2的个数=-1,彻底败了,实际上此时1的个数已经比2多一个了,我们可以认定候选人为nums2[4]=1,也就是nums2[i]。
小彩蛋:上述代码
if(count<0)
也可以写成
if(count<=0)
你猜为啥???因为大将军无所畏惧