28.数组中出现超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路一:
对数组排序,判断中间那个元素的重复度是否超过了数组长度的一半,如果超过了就是它,否则就是没有
1 import java.util.Arrays; 2 public class Solution { 3 public int MoreThanHalfNum_Solution(int [] array) { 4 if(array == null || array.length == 0){ 5 return 0; 6 } 7 // 排序 8 Arrays.sort(array); 9 // 统计中间的那个元素的个数 10 int center = array[array.length / 2]; 11 int cnt = 0; 12 for(int i = 0; i < array.length; i++){ 13 if(array[i] == center){ 14 cnt++; 15 } 16 } 17 18 // 如果大于 一半则输出,否则不存在 19 return (cnt > array.length / 2) ? center : 0; 20 } 21 }
思路二:
采用阵地攻守的思想(另一种叫法是摩尔投票法):
记录当前重复的值和重复次数
遍历数组,如果当前元素和重复的值是同一个,重复次数加一(给它投一张支持票),否则重复次数减一(投一张反对票),重复次数减为0的时候,重复的元素更新为当前元素,重复次数置为1。
最后重复的值的变量中存储的值就有可能是出现了一半的数字。
再加一次循环,记录这个士兵的个数看是否大于数组一般即可。
1 import java.util.Arrays; 2 public class Solution { 3 public int MoreThanHalfNum_Solution(int [] array) { 4 if(array == null || array.length == 0){ 5 return 0; 6 } 7 int soldier = array[0]; 8 int count = 1; 9 for(int i = 1; i < array.length; i++){ 10 if(array[i] == soldier){ 11 count++; 12 }else{ 13 count--; 14 if(count == 0){ 15 soldier = array[i]; 16 count++; 17 } 18 } 19 } 20 count = 0; 21 for(int i = 0; i < array.length; i++){ 22 if(array[i] == soldier){ 23 count++; 24 } 25 } 26 return (count > array.length / 2) ? soldier : 0; 27 } 28 }
leetcode运行时间为:1ms, 空间为41.5MB
复杂度分析:
时间复杂度:遍历了两次数组,所以时间复杂度为O(n)
空间复杂度 :没有借助额外的空间,所以空间复杂度为O(1)
简化写法:
1 class Solution { 2 public int majorityElement(int[] nums) { 3 // 记录当前重复的值和重复次数 4 // 遍历数组,如果当前元素和重复的值是同一个,重复次数加一,否则重复次数减一,重复次数减为0的时候,重复的元素更新为当前元素,重复次数置为1 5 int repVal = nums[0]; 6 int votes = 0; 7 int len = nums.length; 8 for(int i = 0; i < len; i++){ 9 if(votes == 0){ 10 repVal = nums[i]; 11 } 12 votes += nums[i] == repVal ? 1 : -1; 13 14 } 15 return repVal; 16 } 17 }