美团2020春招笔试题java代码
题目表述: 第一行输入数字个数,第二行输入一组数字(非负)。如果这组数字中的数字的二进制表示的相反数(即两个数字的二进制每一位都相反)存在于这组数字中,输出对应位置为-1,否则为1。
例如 :
输入
4
3 5 6 1
输出:1 1 -1 -1
6的二进制表示:110 , 1的二进制表示:001。 每一位都相反。
思路:将输入的数字和序号作为<key,value>存入hashMap中,计算每个数字的二进制相反数,如果map中存在,则结果数组该位置置为-1,否则为1。需要注意的是相反数不至只有一个,根据数字的二进制表示的位数不同
例如:1的二进制表示为:1,01,001,0001 则二进制相反数有:0,10,110,1110。虽然int类型有32位,但是不需要全部枚举, 只要相反数不大于数组中的最大值即可,所以计算出第一个相反数,高位补1,去map中查找即可。相反数大于最大值则停止搜索。
代码如下:
public static void main(String[] args) {
Scanner sc= new Scanner(System.in);
int n = sc.nextInt();
int[] ans = new int[n];
HashMap<Integer,Integer> map = new HashMap<>();
int max = -1;
for(int i = 0;i<n;i++){
int v =sc.nextInt();
if(v>max) max = v; //记录最大值
map.put(v,i);
}
for(Integer key:map.keySet())
{
if(getRevers(key,max,map)){ ans[map.get(key)]=-1;}//相反数存在
else{ ans[map.get(key)]=1;}
}
System.out.println(Arrays.toString(ans));
}
private static boolean getRevers(int key,int max,HashMap<Integer,Integer> map ) {
int revs; int len;
String s = Integer.toBinaryString(max);//将十进制转化为二进制
len = s.length();//记录最大值二进制表示长度
s = Integer.toBinaryString(key);//将key十进制转化为二进制
StringBuilder strBuilder = new StringBuilder(s);
for(int i=0; i<s.length();i++){
int a = (int)s.charAt(i)^1;
strBuilder.setCharAt(i, (char)a); //二进制取反
}
s = strBuilder.toString();
len = len-s.length();
revs = Integer.parseInt(s,2);
if(map.containsKey(revs)) return true;
while(len-->0){ //相反数二进制长度 < max的二进制长度
s = "1"+s;
revs = Integer.parseInt(s,2);
if(map.containsKey(revs)) return true;
}
return false;
}
因为笔试时用数组,双循环按位与进行比较计算。通过率只有55%。原因出在时间复杂度超出限制,数字大小在0~10^6范围。所以循环比较容易超时。所以又思考了一下直接计算二进制相反数以及借助hashMap容器可以大幅度缩短比较时间。值得注意的是相反数的枚举(不唯一)和需要用最大值进行限制。