问题:数组中一个数字出现的次数超过了数组长度的一半,找出这个数字。
我这里给出的是只考虑了正常情况的算法,没有考虑数组无效,以及不符合次数超过数组长度一半的情况。
思路:
(1)将数组排序(排序的原因是为了下面方便统计出次数)
(2)排好序后,分别统计每个元素出现的次数,并对应记录相应的元素。
(3)找出次数最多的元素。
(补充,其实排好序后不需要统计次数了,应为这个数字出现的次数超过了数组长度的一半,那么中间的元素必定是它,所以2,3步多于)
算法:
//这是将数组A排序,为了简单考虑,先用插入排序做实验,为了提高性能可以选择合并排序,堆排序或者快速排序
public static void sort(int[] a)
{
for(int i = 1; i < a.length; i++)
{
int key = a[i];
int j = i - 1;
while(j >= 0 && a[j]> key)
{
a[j+1] = a[j];
j--;
}
a[j+1] = key;
}
}
//统计次数,并找出元素
public static int find(int[] a)
{
sort(a);
int m = a.length / 2 + 1; //由于有一个数字出现的次数超过了数组长度的一般,我用来统计每个数字出现次数的数组长度至多和m一样大。可能用不了这么大,但是不影响我统计。
int[] b = new int[m]; //存放排好序的元素出现的次数
for(int i = 0; i < m; i++)
b[i] = 1; //首先假定每个元素至少出现一次。
int[] c = new int[m]; //c[i]表示排好序的每个元素是什么(无重复),对应出现次数在b[i]中,也就是b[i]表示了c[i]这个元素出现的次数。
int i,j = 0, k = 0; //j表示c数组和b数组的下标,k用来维护k左边的元素都是相同的,从k开始就不同于k-1这个元素。
for(i = 1; i < a.length; i++)
{
if(a[i] == a[i-1])
b[j]++;
else
{
k = i;
c[j] = a[k-1];
j++;
}
}
//找出b中最大的元素,就是出现次数最多的下标j,对应的c[j]就是出现次数最多
int max = 0;
for(int p = 1; p < b.length; p++)
{
if(b[p] > b[max])
max = p;
}
return c[max];
}
心得:我想,大家一看到这个问题基本都是和我想的差不多,如果从效率方面,上面的算法不是一个好的算法。这个问题还有别的方法,可以充分利用次数超过一半这个条件。感兴趣的同学可以讨论回复。后继我会给出更快的算法。
思路2:
为了省去排序的时间,我们完全可以利用java集合类库或者C++标准模板库来统计次数和对应的元素。这里给出用java.HashMap写的算法。
public static int find(int[] a)
{
HashMap<Integer,Integer> h = new HashMap<Integer,Integer>();//建立一个HashMap,key用来表示数组中出现的元素,value表示其出现的次数。
Set<Integer> kk = h.keySet(); //首先建立一个key视图,如果key视图中有此元素,将其value+1,没有则加入,并将其值设为1。
//有同学会考虑你这kk一开始没有值,之后是h发生了变化,而不是kk发生了变化,这样可以吗?我写了一个输出语句验证了得到的kk是同步的。
for(int i = 0; i < a.length; i++)
{
System.out.println(kk);//验证得到的kk是同步的。
if(kk.contains(a[i]))
{
int j = h.get(a[i]);
j = j + 1;
h.put(a[i], j);
}
else
{
h.put(a[i], 1);
}
}
//遍历这个HashMap集,找出最大value对应的key就行。
int maxKey = 0, maxValue = 0;
for(Map.Entry<Integer, Integer> entry : h.entrySet())
{
int key = entry.getKey();
int value = entry.getValue();
if(value > maxValue)
{
maxValue = value;
maxKey = key;
}
}
return maxKey;
}
心得:因为我接触实际项目不是很多,所以有的时候不会想到去用java集合类库,只是从数组去考虑,如果题目没有明确要求我们完全可以利用已有的工具。
思路3:
利用题目给出的条件。希望大家可以讨论讨论,过两天给给出答案。
考虑条件,如果每次都删除两个不同的ID,则剩下的肯定是它:
public static int find(int[] a)
{
int ID = a[0];//这个是假设中的ID
int count = 0;//用来统计ID的次数,如果相同就+1,如果不同就-1,
for(int i = 0; i < a.length; i++)
{
if(count == 0)//说明抵消
{
ID = a[i];
count++;
}
else
if(ID == a[i])//相同就增加
count++;
else//不同就相减
count--;
}
return ID;
}