1. 程式人生 > >新鮮出爐的頭條面試演算法

新鮮出爐的頭條面試演算法

昨天下午實驗室有一個同學參加了今日頭條的面試,面試最終是以一個演算法題結束。

題目如下:

 

給你一個有序整數陣列,陣列中的數可以是正數、負數、零,請實現一個函式,這個函式返回一個整數:返回這個陣列所有數的平方值中有多少種不同的取值。舉例:

  1. nums = {-1,1,1,1},

    那麼你應該返回的是:1。因為這個陣列所有數的平方取值都是1,只有一種取值

  2. nums = {-1,0,1,2,3}

    你應該返回4,因為nums陣列所有元素的平方值一共4種取值:1,0,4,9

在往下看之前,請先進行思考,如果當時是你在面試,你會給出什麼樣的結題思路?下面會給出兩種解法,最優解:時間複雜度:O(n)、空間複雜度O(1)。無論有沒有思路,在往下看之前一定要有自己的思考

 

第一種也是最為直接、簡單的思路:把nums陣列中所有數的絕對值,全部計算完之後再統計有多少種不同的取值。

實現程式碼如下:

public int handle(int[] nums) {
    if(nums==null  || nums.length==0)
        return 0;
    HashSet<Integer> set = new HashSet<Integer>();
    for (int number : nums)
        set.add(Math.abs(number));
    return set.size();
}

上面的實現也很簡單,主要是利用HashSet的去重特性,最後直接返回set的size。

但是呢,僅僅給出這種解法是過不了面試的,昨天面試的同學在面試結束之後才想到更優的解法,所以....當然這個同學已經拿到了很好的offer—網易。舉這個案例只想和學弟學妹們在強調一遍:演算法的重要性。

 

上面直接使用set的解法沒有利用題目中有序條件,這也是優化的方向。

那如何利用好有序這個條件呢?

解思路法如下:

絕對值相等可能有哪些情況呢?

  1. 兩個同符號數的絕對值相等,這也意味著在有序陣列中這個兩個數是相鄰的,這時只需要移動指標跳過相鄰相等的數即可。

  2. 兩個異符號數的絕對值相等,所以我們需要維護兩個指標,一個指標從前往後移動,一個從後往前移。原因是前面的負數絕對值可能與後面整數的絕對值相等,我們需要比較前後兩個指標絕對值的大小。

綜上,我們需要維護兩個指標,i開始指向陣列第一個元素,i=0;j開始指向陣列最後一個元素,j = nums.length-1。i,j指標的另外含義是:陣列中索引小於i和大於j的元素都已經被處理了;i,j指向的是未處理元素中絕對值最大的兩個元素。當i>j的時候表明所有的元素都已經被處理了,迴圈結束。

那麼如何移動指標呢?

  1. 如果nums[i]與nums[j]的絕對值相等,此時執行i++,直到nums[i]的絕對值不等於nums[j]的絕對值(跳過相鄰重複元素);j也是做類似的移動,只不過j是向前移,j--;最後計數器加1。

  2. 如果nums[i]的絕對值大於nums[j]的絕對值,我們移動指標i,並且計數器加1。原因?因為i和j指向未處理元素中絕對值最大的那個,nums[i]的絕對值已經是未處理元素中絕對值最大的兩個數,也就是不可能存在一個未被處理的元素,它的絕對值與nums[i]的絕對值相等。所以這種情況下我們移動i指標。注意這裡所說的移動指的是:執行i++直到nums[i]的絕對值發生了改變(跳過相鄰相等的元素)。

  3. 如果nums[j]的絕對值大,那麼與上面類似,移動j指標。

具體程式碼實現如下:

public int handle (int[] nums) {

    if(nums==null  || nums.length==0)
        return 0;

    // result的縮寫,最後的返回值
    int res = 0;

    //i是前指標;j是後指標   
    int i = 0;
    int j = nums.length - 1;
    while (i <= j) {
        int num1=Math.abs(nums[i]);
        int num2=Math.abs(nums[j]);
        if (num1 > num2) {//移動i
            // 這兩個數的絕對值不相等
            res += 1;
            while(i<=j && Math.abs(nums[i])==num1)
                //過濾掉相鄰的絕對值相等的數
                i++;
        } else if (num1 < num2) {
            // 這兩個數的絕對值不相等    
            res += 1;
            while(i<=j && Math.abs(nums[j])==num2)
            //過濾掉相鄰的絕對值相等的數    
            j--;
        } else {
            res += 1;
            while(i<=j && Math.abs(nums[i])==num1)//去重
                i++;
            while(i<=j && Math.abs(nums[j])==num2)//去重
                j--;
        }
    }
    return res;
}

對本題有疑問的同學,歡迎在評論區留言探討~

 

總結:

面試中最常考的演算法題主要是:陣列、二叉樹。期中呢,HashMap、HashSet等輔助資料結構使用比較多,“雙指標法”在有序陣列相關演算法題中使用較多;無序陣列,使用HashMap和HashSet較多;二叉樹大多使用遞迴、dfs來解題。

另外,在實際面試中,演算法也是這樣一步步優化:最先提出的方案沒有那麼“優”也沒有關係,如果面試官不滿意,繼續在此基礎上進行優化,大多時候面試官會提示優化的方向。當然能直接給出優化方案最好~

 

公眾號後臺回覆“資料”即可獲得2T的學習資料(長期更新ing)以及博主整理好的精品資料一份。2T資料涵蓋各個求職方向,並且每一個方向都有對應的經典專案,可寫入簡歷的大型專案