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<<i);//恢復這個唯一的數,需要還原它原本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,②各個位數值相加肯定能被次數整除 這樣的規律。大神除外,反正筆者當時是沒想到。

希望對你有所幫助

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章