给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋
次的元素。
示例 1:
输入:[3,2,3] 输出:[3]
示例 2:
输入:nums = [1] 输出:[1]
示例 3:
输入:[1,1,1,3,3,2,2,2] 输出:[1,2]
提示:
1 <= nums.length <= 5 * 104
-109 <= nums[i] <= 109
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。
简要思路就是:
- 留3个位置记录数字和次数,
- 挨个从头开始读,记录到步骤1的值里,如果数字相同累加,如果不同找个位子记下来
- 当凑满3个不同的数字的时候,所有数字统计数都减一
- 最后加进来的那个数字只有一个,所以最后那个数字个数变成了0,但是前面也是有可能有其他数字之前也只有一个,现在剩下0个的
- 把0个的数字占的位子清空,继续添加下一个数字,重复上面的操作,直到最后
- 剩下的两个数字中一定是有超过
1/3
的个数的 - 回去再遍历一遍,统计下数量,(为了省下其他数字数量统计的空间也是煞费苦心了),最后超过
1/3
的留下
不好理解的话,我们换个角度。假如你有一根火腿肠,但是吃火腿肠的话有一个奇怪的习惯,需要把火腿肠分成N段,然后凑齐其中的3段,并排在一起,然后一起咬一块。
大概就。。这样?一根长度为9的火腿肠(毕竟比较好除以3)
此时你切成的N段火腿肠中,确保有一段是长度大于1/3
的,那么思考一下,在每次都要拿着3段一起吃的情况下,即使你一直拿着那个大于1/3
的那段,到最后这段也会生下来,毕竟到最后凑不出3段一起吃了。
或者换个思路,除去最长的大于1/3
的那段,剩下的总和是小于2/3
的,那么这个2/3
再分成两段的,最极限情况下,要保持3段的情况,这边分出来的也都是小于1/3
的。
最后剩余的情况
剩下的数字并不一定的大于1/3
的,还是上面的长度9的举例子,比如分成了4,3,2
这样的长度组合的,3个一起进行消除,最后剩下的长度的2,1
,所以还得回去再统计一遍,不过再统计的时候只需要统计剩下的两个数字的,如果是剩下1个,必然是大于1/3
的。
先决条件:需要保证给出的数组中一定存在数量超过1/3的数字
那么在理解了思想的情况下这个之后,如果是类似的题目就基本都是这个套路能解决了
代码
class Solution {
/**
* 初始化了边界外的数值,表示一个空位子
*/
int[] nums = {1000000001,1000000001,1000000001};
int[] countArr = {0,0,0};
public List<Integer> majorityElement(int[] nums) {
for (int i = 0; i < nums.length; i++) {
processNum(nums[i]);
}
//数量重新归零
countArr[0] = 0;
countArr[1] = 0;
countArr[2] = 0;
for (int i = 0; i < nums.length; i++) {
//统计剩下的数字出现的次数
countArr[0] += this.nums[0] == nums[i]?1:0;
countArr[1] += this.nums[1] == nums[i]?1:0;
countArr[2] += this.nums[2] == nums[i]?1:0;
}
List<Integer> list = new ArrayList<>();
for (int i = 0; i < this.nums.length; i++) {
//留下大于1/3的
if (this.nums[i] < 1000000001 && countArr[i] > nums.length/3){
list.add(this.nums[i]);
}
}
return list;
}
public void processNum(int num){
//是否是新插入的
boolean insertFlag = false;
for (int i = 0; i < 3; i++) {
//原有有这个数字
if (nums[i] == num){
//数量加1
countArr[i]++;
insertFlag = true;
}
}
//是原来不存在的数字,
if (!insertFlag){
for (int i = 0; i < 3; i++) {
//找个空位子给安排下,并计数为1
if (nums[i] == 1000000001){
nums[i] = num;
countArr[i] = 1;
break;
}
}
}
//如果没凑到3个数字,就继续往里一个新数字来处理
if (countArr[0]==0 || countArr[1] == 0 || countArr[2]==0){
return;
}
//如果是3个不同的数字,每个数字减一,
// 并把剩余0的那个数字占的位子空出来
for (int i = 0; i < 3; i++) {
countArr[i]--;
if (countArr[i]==0){
nums[i] = 1000000001;
}
}
}
}
发表评论