题目
春节期间小明使用微信收到非常多个红包。非常开心。在查看领取红包记录时发现。某个红包金额出现的次数超过了红包总数的一半。请帮小明找到该红包金额。
写出详细算法思路和代码实现,要求算法尽可能高效。
给定一个红包的金额数组gifts及它的大小n。请返回所求红包的金额。
没找到,返回0。
思路
思路一:部分高速排序
受高速排序的partition函数的启示,我们能够利用重复调用partition函数来求的该数字。我们如今数组中随机选取一个数字,而后通过Partition函数返回该数字在数组中的索引index。假设index刚好等于n/2,则这个数字便是数组的中位数。也即是要求的数,假设index大于n/2,则中位数肯定在index的左边,在左边继续寻找就可以,反之在右边寻找。这样能够仅仅在index的一边寻找,而不用两边都排序,降低了一半排序时间。
这样的情况的平均时间复杂度大致为:T(n) = n+n/2+n/4+n/8+….+1,非常明显当n非常大时。T(n)趋近于2n,也就是说平均情况下时间复杂度为O(n),可是这样的情况下,最坏的时间复杂度依旧为O(n*n)。最坏情况下。index总是位于数组的最左或最右边。这样时间复杂度为T(n) = n+n-1+n-2+n-3+….+1 = n(n-1)/2。显然,时间复杂度为O(n*n),空间复杂度为O(1)。
部分高速排序的扩展
对于一个乱序的数组,返回排序后。index为n的数字
对于以上的问题,也能够用部分高速排序思路去做
思路二
数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其它全部数字出现次数的和还要多。因此我们能够考虑在遍历数组的时候保存两个值: 一个是数组中的一个数字。 一个是次数。当我们遍历到下~个数字的时候,假设下一个数字和我们之前保存的数字同样,则次数加l :假设下一个数字和我们之前保存的数字,不同,则次数减1 。假设次数为霉,我们须要保存下一个数字,并把次数设为1 。
因为我们要找的数字出现的次数比其它全部数字出现的次数之和还要多。那么要找的数字肯定是最后一次把次数设为1 时相应的数字。
代码
public class FindOverNumber {
/**
* 题目:数组中有一个数字出现的次数超过数组长度的一半。请找出这个数字
*
* @param numbers 输入数组
* @return 返回的结果
*/
public static int moreThanHalfNum(int[] numbers) {
if (numbers == null || numbers.length < 1) {
throw new IllegalArgumentException("array length must large than 0");
}
// 用于记录出现次数大于数组一半的数
int result = numbers[0];
// 于当前记录的数不同的数的个数
int count = 1;
// 从第二个数開始向后找
for (int i = 1; i < numbers.length; i++) {
// 假设记数为0
if (count == 0) {
// 又一次记录一个数,假设它是出现次数大于数组一半的
result = numbers[i];
// 记录统计值
count = 1;
}
// 假设记录的值与统计值相等,记数值添加
else if (result == numbers[i]) {
count++;
}
// 假设不同样就降低,相互抵消
else {
count--;
}
}
// 最后的result可能是出现次数大于数组一半长度的值
// 统计result的出现次数
count = 0;
for (int number : numbers) {
if (result == number) {
count++;
}
}
// 假设出现次数大于数组的一半就返回相应的值
if (count > numbers.length / 2) {
return result;
}
// 否则输入异常
else {
throw new IllegalArgumentException("invalid input");
}
}
參考
http://blog.csdn.net/derrantcm/article/details/46736859
http://blog.csdn.net/ns_code/article/details/26957383