原始题目:给定一个整形数组arr,打印出其中出现次数大于一半的数,如果没有这样的数,打印提示信息。
进阶题目:给定一个整形数组arr,再给定一个整数k,打印所有出现次数大于N/K的数,如果没有这样的数,打印提示信息。
要求:原始问题要求时间复杂度为O(N),额外空间复杂度为O(1)。进阶问题要求时间复杂度为O(N*K),额外空间复杂度为O(K)。
原始问题:设定一个候选值cands,以及记录次数的变量times,当前值如果等于候选值时times加一,否则times减一,即将两个不同的值同时消除,这样的话最终剩下的值即为大于一半的数,但是也应该进行最后的校验,因为剩下的值不一定是大于一半的数,但是大于一半的数一定会被剩下。具体代码实现如下所示:
public class PrintHalfMajor { public void printHalfMajor(int[] arr){ int cand=0; int times=0; for (int i=0;i!=arr.length;i++){ if (times==0){ cand=arr[i]; times=1; }else if (arr[i]==cand){ times++; }else{ times--; } } times=0; for (int i=0;i!=arr.length;i++){ if (arr[i]==cand){ times++; } } if (times>arr.length/2){ System.out.println(cand); }else{ System.out.println("没有这种数!"); } } public static void main(String[] args) { int[] arr=new int[]{1,5,2,2,2}; PrintHalfMajor printHalfMajor=new PrintHalfMajor(); printHalfMajor.printHalfMajor(arr); } }
进阶题目:可以采用原始题目的思路,因为找到大于一半的数时选择的是一个候选值,因此可以想到在找大于N/K的数时,应该设置k-1个候选值,然后同样比较候选值与当前值是否相等,若相等则对应的次数加一,否则减一,当然要判断候选值是否为k-1,若没有达,则直接将当前值作为候选值,如果到达k-1了,且当前值不等于候选值中的数值,此时应该将候选值中的数的次数都减一,这样便能找到要求的数值。具体代码实现如下所示:
import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public class PrintHalfMajor2 { public void printHalfMajor2(int[] arr,int k){ if (k<2){ System.out.println("不合规矩!"); return ; } HashMap<Integer,Integer> cands=new HashMap<>(); for (int i=0;i!=arr.length;i++){ if (cands.containsKey(arr[i])){ cands.put(arr[i],cands.get(arr[i])+1); }else{ if (cands.size()==k-1){ allCandsMinusone(cands); }else{ cands.put(arr[i],1); } } } HashMap<Integer,Integer> reals=getReals(arr,cands); boolean hasPrint=false; for (Map.Entry<Integer,Integer> set:cands.entrySet()){ Integer key=set.getKey(); if (reals.get(key)>arr.length/k){ hasPrint=true; System.out.println(key+" "); } } System.out.println(hasPrint?" ":"无该数值!"); } /*当候选值达到k-1时更新候选值的次数*/ private void allCandsMinusone(HashMap<Integer,Integer> map) { List<Integer> removeList=new LinkedList<>(); for (Map.Entry<Integer,Integer> set:map.entrySet()){ Integer key=set.getKey(); Integer value=set.getValue(); if (value==1){ removeList.add(key); } map.put(key,value-1); } for (Integer removeKey:removeList){ map.remove(removeKey); } } /*获取候选值在原数组中出现的总次数*/ private HashMap<Integer,Integer> getReals(int[] arr, HashMap<Integer,Integer> cands) { HashMap<Integer,Integer> reals=new HashMap<>(); for (int i=0;i!=arr.length;i++){ int curNum=arr[i]; if (cands.containsKey(curNum)){ if (reals.containsKey(curNum)){ reals.put(curNum,reals.get(curNum)+1); }else{ reals.put(curNum,1); } } } return reals; } public static void main(String[] args) { int[] arr=new int[]{1,5,2,2,2,1,1}; PrintHalfMajor2 printHalfMajor2=new PrintHalfMajor2(); printHalfMajor2.printHalfMajor2(arr,3); } }