有一个长度为n的数列,已知有一个数字出现的次数大于n/2,找出这个数字。
例: 输入一个数组(假设:{1,3,2,2,2,4,2}) 输出: 数组为:{1,3,2,2,2,4,2} 众数为:2
不妨把这个问题转换一下。假设有k个国家的士兵,一共n个人。其中有一个国家的士兵大于n/2,问这个国家是哪个。这样转换一下是不是生动多了? 那这个国家应该怎么找呢,会是什么国家呢? 。。。。。。。。。。。。。 开个玩笑。 比较容易想到的方法是一个一个去数,把同一个国家的士兵堆在一起,最后数一下那个国家的士兵最多。但是这样子时间复杂度非常高。 有个很简单的方法,既然是比哪个国家的士兵最多,那么不如。。。
我们不妨让他们“打一架”,两两配对,如果是不同国家的士兵,就让他们各自捅对方一刀,同归于尽。如实是同一个国家的士兵,就抱团一起去打别人。那么最后剩下来的士兵一定是来自士兵最多的那个国家的。 简称,同归于尽算法。
先做一个标记,起始为数组的最左端。这个标记的左边为士兵的“尸体”,右边是还存活的士兵。标记命名为left。 从数组最左端开始向右逐个读取数字。 若当前读取的元素与下一个元素不同: 则将下一个元素与left标记的元素互换位置,并将left标记右移一位。
算法中涉及到数组元素交换的方法,所以我们先来写一个交换数组元素的方法:
/** * 交换数组中两个元素的位置 * @param a 数组 * @param i 其中一个元素的下标 * @param j 另一个元素的下标 */ private void exchange(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; }然后是算法主体部分:
/** * 找众数 * @description 已知某个数组长为n,其中有一个数字出现次数次数大于n/2,找出这个数字 * <br> * “同归于尽算法”: * <br> * 相邻的数字两两比较,将不同的数字“堆放”在数组最左边,最终剩下的数字就是众数 * @param a 数组 * @return 找到的众数 */ public int find(int[] a) { int left = 0; for (int i = 0; i < a.length - 1;i ++) { if (a[i] != a[i + 1]) //同归于尽 { exchange(a,left,i + 1); left += 2; } } return a[left]; }这就完成啦。