面试题29:数组中出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
分析:
如果一个数字才数组中出现的次数超过了数组长度的一半,那么对这个数组进行排序,位于数组中间位置的那个数就是出现次数超过一半的那个数。对数组排序的时间复杂度是O(nlog(n)),但是对于这道题目,还有更好的算法,能够在时间复杂度O(n)内求出。我们写过快速排序算法,其中的Partition()方法是一个最重要的方法,该方法返回一个index,能够保证index位置的数是已排序完成的,在index左边的数都比index所在的数小,在index右边的数都比index所在的数大。那么本题就可以利用这样的思路来解。
- 通过Partition()返回index,如果index==mid,那么就表明找到了数组的中位数;如果index<mid,表明中位数在[index+1,end]之间;如果index>mid,表明中位数在[start,index-1]之间。知道最后求得index==mid循环结束。
- 根据求得的index,遍历一遍数组,每当出现一个等于index所指向的数时time++,最后判断time是否大于数组长度的一半,如果大于则表明index所指向的数就是所求的数,如果不是,则表明不存在一个数出现的次数超过数组长度的一半。
代码实例:
View Code
#include<iostream> #include<stdlib.h> using namespace std; //函数声明 int MoreThanHalf(int arry[],int start,int end,int len);//函数入口 int Partition(int arry[],int start,int end);//返回一个index,使index左边的数都比index所在的数小,index右边的数都比index所在数大 bool CheckMoreThanHalf(int arry[],int len,int result);//判断一个数在数组中是否有超过一半 int Partition(int arry[],int start,int end) { int pivotkey=arry[start]; while(start<end) { while(start<end&&arry[end]>=pivotkey) end--; arry[start]=arry[end]; while(start<end&&arry[start]<=pivotkey) start++; arry[end]=arry[start]; } arry[start]=pivotkey; return start; } bool CheckMoreThanHalf(int arry[],int len,int result) { int time=0; for(int i=0;i<len;i++) { if(arry[i]==result) ++time; } bool isMoreThanHalf=true; if(time*2<=len) isMoreThanHalf=false; return isMoreThanHalf; } int MoreThanHalf(int arry[],int start,int end,int len) { if(arry==NULL&&len<=0) return -1; int index=Partition(arry,start,end); int middle=len/2;//中间位置 while(index!=middle) { if(index>middle)//如果调整数组以后获得的index大于middle,则继续调整start到index-1区段的数组 index=Partition(arry,start,index-1); else//否则调整index+1到end区段的数组 index=Partition(arry,index+1,end); } //最后获取的index=middle,此时在middle左边的数小于arry[middle],在其右边的数大于arry[middle] int result=arry[middle]; if(!CheckMoreThanHalf(arry,len,result)) return -1; return arry[middle]; } void main() { //int arry[]={5,1,7,3,0,2,8};//定义数组 int arry[]={2,2,1,1,3};//定义数组 int len=sizeof(arry)/sizeof(int);//求数组长度 int half=MoreThanHalf(arry,0,len-1,len); cout<<half<<endl; system("pause"); }
题目变种
一个文件中有每一个保存一个单词,其中有一个单词出现的次数超过一半,求这个次数超过一半的单词。
解体思路
这道题目其实本质上跟前面的求出现次数超过一半的数是一样的,只不过是这里要求的是单词出现次数,而前面是整数出现次数。如果是整数的话,可以通过一个整型数组来完成求中位数的操作。但是这里是字符串了,我们想再用求中位数的方法,只能通过LinkedList这个集合累来完成。
首先我们遍历一次问价,将所有单词都保存到LinkedList当中去,然后求排在中间的那个单词。LinkedList中的元素使有序的,不像ArrayList中的元素那样是无序的。而且List中允许元素重复。
代码实现
View Code
import java.io.BufferedReader; import java.io.FileReader; import java.util.Iterator; import java.util.LinkedList; public class GetMoreThanHalfWords { public static void main(String args[]) { GetMoreThanHalfWords gmthw = new GetMoreThanHalfWords(); LinkedList<String> list =new LinkedList<String>(); gmthw.buildLinkedList(list); //两种输出LinkedList的方法 //gmthw.printLinkedList(list); //gmthw.printLinkedList2(list); System.out.println(gmthw.getMoreThanHalf(list)); /** * 测试String的compareTo方法 String a="apple"; String b="orange"; System.out.println(a.compareTo(b)); System.out.println(b.compareTo(a)); */ } // 第一步:创建LinkedList,将文件中的单词保存到LinkedList当中。 public void buildLinkedList(LinkedList<String> list) { try { FileReader reader = new FileReader("words2.txt"); BufferedReader br = new BufferedReader(reader); String s = null; while ((s = br.readLine()) != null) { list.add(s); } br.close(); reader.close(); } catch (Exception e) { e.printStackTrace(); } } public String getMoreThanHalf(LinkedList<String> list) { int len=list.size(); int mid=len/2; int start=0; int end=len-1; int index=partition(list,len,start,end); while(index!=mid)//index总会等于mid的 { if(index>mid) { end=index-1; index=partition(list,len,start,end); } else { start=index+1; index=partition(list,len,start,end); } } //到这里index=mid return list.get(index); } public int partition(LinkedList<String> list,int len,int start,int end) { String s=list.get(start); while(start<end) { //找出第一个 while(start<end&&s.compareTo(list.get(end))<=0) end--; list.set(start, list.get(end)); while(start<end&&s.compareTo(list.get(start))>=0) start++; list.set(end, list.get(start)); } list.set(start, s); return start; } // 打印LinkedList中的值 //方法1 public void printLinkedList(LinkedList<String> list) { Iterator it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } //方法2 public void printLinkedList2(LinkedList<String> list) { for(int i=0;i<list.size();i++) { System.out.println(list.get(i)); } } }