java面试算法题(1)

引言

本篇博文中核心介绍的是一些java很精辟的运算符操作,包含一些独特的思维。在面试的过程中,也可能会遇到这些问题。笔者在阿里巴巴的电话面试过程中就遇到这样的一个问题。分享给大家。

题目

给出一组整形(int)数组,在这个数组中只有一个数字是单独的,其它的数字都出现了2次,或者更多次。当然出现的次数全部统一,要么全部出现2次,要么全部出现多次。

分析一

最简单的方法,也是最容易想到的就是先对这个数组进行排序,然后相邻的进行比较,从而找出只出现一次的数字。换句话说,也就是如果存在一个数字,它和前面的数不相同和后面的数不相同,那么它就只出现一次。
这种处理方法,重点就转向了如何进行排序,排序的时间复杂度直接影响最终的结果。在以往的博客中提到过,在排序中快速排序和堆排序是时间复杂度最低的排序方式,那么我们可以采用快速排序来解决这个问题。但是,这不是本篇博文的核心,需要的同学可以自行查找排序方式,或者去博主以往的博客中查找。

实现代码一


package com.brickworkers;

import java.util.Arrays;
/**
 * 
 * @author Brickworker
 * Date:2017年6月28日下午2:25:55 
 * 关于类Example.java的描述:用最简单的方法寻找数组中出现一次的值
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class Example {

    public static int getSingle(int[] a){
        //首先对数组进行排序
        //排序不是本篇博文核心,我们直接调用Arrays库来解决排序,但是真正处理的时候用快速排序或者堆排序
        Arrays.sort(a);
        for (int i = 1; i < a.length - 1; i++) {//直接从1开始,可以两头判断
            if(a[i] != a[i-1]){//把当前数字和它前面数字比较
                //如果不相等,那么在与它后面的数字比较
                if(a[i] != a[i+1]){
                    //那么可以断定已经找到了那个唯一的数字
                    return a[i];
                }
            }
        }
        return Integer.MIN_VALUE;//非法的时候抛出

    }

    public static void main(String[] args) {
        //除了唯一出现的其他出现2次
        int[] a = {1,2,3,4,5,6,7,8,9,1,2,4,5,6,7,8,9};
        System.out.println("唯一存在的值为" + getSingle(a));
        //除了唯一出现的,其他出现了3次
        int[] b = {1,2,3,4,5,6,7,8,9,1,2,4,5,6,7,8,9,1,2,4,5,6,7,8,9};
        System.out.println("唯一存在的值为" + getSingle(b));
    }


}
//运算结果:
//唯一存在的值为3
//唯一存在的值为3

这种算法的处理方式,直接受到了你选择的排序算法的性能所影响。但是它很通用,不论你是多大倍数出现次数的数组,都能很好解决。

分析二

或许有些小伙伴不知道异或(^)操作的功能,异或操作具满足交换律和结合律,也就是说异或操作满足以下等式:
X^X=0
0^X=X
也就是说,如果只要同一个数字出现偶次,那么它就会抵消。我们可以遍历数组,把他们都进行一次异或操作,那么最后的结果就是那个存在一次的数。但是,它有一个很致命的缺陷,就是只试用于数组只出现偶次的情况。但是优点不可置否,只需要遍历一次数组即可。

实现代码二

package com.brickworkers;

/**
 * 
 * @author Brickworker
 * Date:2017年6月28日下午2:25:55 
 * 关于类Example.java的描述:用异或运算寻找出现一次的数字,只试用于其他数字出现偶次的情况
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class Example {

    public static int getSingle(int[] a){
        //直接遍历数组
        int result = a[0];
        for (int i = 1; i < a.length; i++) {
            result ^= a[i];
        }
        return result;

    }

    public static void main(String[] args) {
        //除了唯一出现的其他出现2次
        int[] a = {1,2,3,4,5,6,7,8,9,1,2,4,5,6,7,8,9};
        System.out.println("唯一存在的值为" + getSingle(a));

    }


}
//运算结果:
//唯一存在的值为3
//

代码简单而精炼,是一种非常优秀的解决方式。

分析三

第二种方法虽然高明,但是只能查找出现偶次成为了硬伤。如果出现倍数为奇次又该如何解决呢?用第一种方法又会显得算法时间复杂度很高。其实也有更好、更通用的解决办法:

试想存在一个值,这个值出现了多次,那么它的二进制每个位数上的1相加,肯定能被次数整除,比如说:
{3,3,3,2,1,1,1}
转化成二进制就变成了:
11,11,11,10,01,01,01
可以发现除却2的二进制10以外,剩余的6个数字,低位和高位相加分别是3和6,都可以被3整除。那么要寻找这个2,可以通过下面这个算式计算:
低位:
(1+1+1+1+1+1)/3 = 0
高位:
(1+1+1+1)/3 = 1 PS:其中一个1是2的二进制中来的
那么这个出现一次的数字就是 高位+低位 = 10。

实现代码三

package com.brickworkers;

/**
 * 
 * @author Brickworker
 * Date:2017年6月28日下午2:25:55 
 * 关于类Example.java的描述:用位操作的规律实现通用的方法
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class Example {

    public static int getSingle(int[] a, int times) {
        // 定义一个数组,用于存放每一个位上出现次数
        int[] count = new int[32]; // int类型占4个字节32位
        // 遍历数组,计算每个位置上出现的次数
        for (int i = 0; i < a.length; i++) {
            // 计算每一个位上出现的次数
            for (int j = 0; j < 32; j++) {
                count[j] += ((a[i] >> j) & 1);//计算一个a[i]中的所以位上的出现情况。&操作:1&1=1;1&0=0
            }
        }
        int result=0;//定义一个返回值
        //遍历出现次数数组,如果存在某个位上的值不能被次数整除,那么唯一值就在这个位上

        for(int i = 0; i < 32; i++){
            if(count[i] % times !=0){
                result += (1<//恢复这个唯一的数,需要还原它原本1所在的位置
            }
        }
        return result;
    }

    public static void main(String[] args) {
        //除了唯一出现的其他出现3次
        int[] a = {1,2,3,4,5,6,7,8,9,1,2,4,5,6,7,8,9,1,2,4,5,6,7,8,9};
        System.out.println("唯一存在的值为" + getSingle(a, 3));

    }


}

//运行结果:
//唯一存在的值为3
//
//

总结

在面试的过程中,一般的人都只能想到第一种方法。之所以想不到后面两种,很多程度上是不知道存在: ①X^X = 0;0^X=X,②各个位数值相加肯定能被次数整除 这样的规律。大神除外,反正笔者当时是没想到。

希望对你有所帮助

你可能感兴趣的:(算法)