数组中出现超过一半次数的数字
2015-07-20
次访问
剑指offer面试题系列
题目:数组中有一个数字出现的次数超过了数组长度的一半,请找出这个数字。
分析
首先要明白一个统计学规律:一个排序数组中出现次数超过一般的数字必然位于此数组的中间处。
此题有两种实现方法:
自己只想出来方法一:
思路: 由快速排序的分割思想得到启发,因为快排的每次分割数组都是将枢纽元放在正确的位置上,于是就想如果这个枢纽元正好位于此数组的中心就好了。但是并不是每次分割运气都这么好,于是可以使用递归的方式,当这个枢纽元的位置在数组中心的前面,则分割后面的部分,反之则分割前面的部分,如果位于中央,则直接返回此数字即可。
这样的算法复杂度:O(NlgN)级别。
看了剑指offer之后,发现有复杂度更小的精妙算法。根据这个思路写出了代码中的Solution2。
思路:可以声明一个变量 time,如果数组的下一个元素与前面的一个元素不一致,则减一,如果一致则加一。如果数组中有出现超过一半的数字,则此数字必然就是最后使 time 的值变为 0 的那个。妙哉!!!!!
这样的算法复杂度仅仅为:O(n).
代码实现
package com.study;
import java.util.Random;
/** 求数组中出现次数超过总次数一半的数字 */
public class suanfa16 {
// solution 1
/*
* 最好在查出满足条件的元素时,检查一下数组是否符合题意,即 存在出现次数超过一半的数字
*/
public static int FindNumberOverHalf(int[] arr, int start, int end) {
int index = new Random().nextInt(end - start) + start;
System.out.println("Index is : " + index);
int pivot = arr[index];
SwapNum(arr, index, end);
int PartionNum = 0;
int i = start - 1;
int j = end;
for (;;) {
while (arr[++i] < pivot && i < end)
;
while (arr[--j] > pivot && j > start)
;
if (i >= j)
break;
else
SwapNum(arr, i, j);
}
SwapNum(arr, i, end);
PartionNum = i;
if (PartionNum < arr.length / 2)
PartionNum = FindNumberOverHalf(arr, PartionNum, end);
if (PartionNum > arr.length / 2)
PartionNum = FindNumberOverHalf(arr, start, PartionNum);
return PartionNum;
}
public static void SwapNum(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// solution 2
public static int FindPartionNum(int[] arr) {
int time = 1;
int preNum = arr[0];
int result = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[i] != preNum) {
time--;
} else {
time++;
}
if (time == 0)
result = arr[i];
preNum = arr[i];
}
return result;
}
public static void main(String[] args) {
int[] arr1 = { 1, 7, 2, 8, 7, 7, 7, 4, 7 };
int[] arr = { 1, 7, 2, 4, 4, 4, 5, 4, 4, 2, 2, 2, 2, 2, 2, 2 };
int index = FindPartionNum(arr);
System.out.println("Index is : " + index
+ ", The number you want to find is : " + arr[index]);
}
}
备注
如果需要考虑不规范数组,即数组中没有这样的数字存在的情况,还需要加一部分代码,以实现检查数组规范的情况。
总结
快排思想的再次运用。快速排序真是一个特别牛逼的排序啊,其思想不止在排序方面牛逼闪闪啊,还可以用到好多地方。
递归,这是一把神器。做了剑指offer的题也一半多了,总是会用到它。试着慢慢理解它吧!