描述
给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
数据范围:n \le 50000n≤50000,数组中元素的值 0 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1),时间复杂度 O(n)
输入描述:保证数组输入非空,且保证有解
解题思路
题目要求“空间复杂度:O(1),时间复杂度 O(n)”,说明不能借助外部数组等数据结构,并且只能一遍遍历数组。
数组中有一个数字出现的次数超过数组长度的一半,这个是题目重点。数字出现数量超过一半也就意味着假如把数组中两两不同的数字都单独拎出来,那么剩下的至少超过1个数字,这个数字必然就是我们要找的数字。那我们如何模拟在遍历数组过程中把两两不同的数字拎出来呢?
我想到的一种方案是
1、使用变量target标记为我们要找的值,初始化该值为数组第零个元素。
2、再用一个变量来表示已遍历到的位置处把两两不同元素去除后,还剩下的相同元素的数量。
3、从数组第2个位置开始遍历,如果当前遍历到的元素和target相同,则仅仅让sameNum加1,然后进入下一次循环。
4、如果当前遍历到的下标index的元素和target目标值不同,这时候判断sameNum减1,然后判断sameNum值是否等于0,如果等于0也就意味着位于0~index之间的所有数字满足两两不同。这时候把target设置为当前index下标的值,sameNum恢复为1;
5、遍历完数组后,最后target保存的值一定是那个出现数量超过数组长度一般的数字。
举个例子吧,方便理解:
数组为:array = [1,2,3,2,2,2,5,4,2] 目标值为2
首先设置target=array[0], sameNum=1。从下标1开始遍历
1、第一次遍历,index = 1,2!=1, 所以sameNum = sameNum-1。当sameNum=0这时候需要重新设置target=array[index],即target=2, sameNum=1;
2、第二次遍历,index =2, 3!=2,和上面一样操作。最后target=3, sameNum=1;
3、第三次遍历,index =3,2!=3,和上面一样操作。最后target=2, sameNum=1;
4、第四次遍历,index=4,2==2,sameNum=sameNum+1,最后sameNum=2;
5、第五次遍历,2==2,同上,最后sameNum=3;
6、第七次遍历,5!=2,sameNum = sameNum-1,所以sameNum=2,不会重置target;
7、第八次遍历,4!=2,sameNum = sameNum-1,所以sameNum=1,不会重置target;
8、第九次遍历,2==2,sameNum = sameNum+1,所以sameNum=2,不会重置target;
9、返回target=2。
贴上代码:
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
//某个数超过数组长度一半,意味着把两两不同的数都去掉,肯定能剩下一些数,并且这些数字相同
int target = array[0];
int length = array.length;
int sameNum = 1; //用来统计相同数字的数量
for (int i = 1; i < length; i++) {
if (array[i] != target) {
sameNum--;
if (sameNum == 0) {
target = array[i];
sameNum = 1;
}
} else {
sameNum++;
}
}
return target;
}
}
也可以参考下网上的解题方法:
CodeSolution/剑指Offer28-数组中出现次数超过一半的数字.md at main · Damaer/CodeSolution · GitHub