前端學習演算法3:一道來自頭條的面試題

Ace7523發表於2019-02-14

前端學習演算法3:一道來自頭條的面試題

朋友去面試頭條的前端,二面,第一個題目,就是下面這張紙,面試官也會給一張紙要求手寫答案。如果你看到就立刻知道了怎麼解答,那可以直接忽略本文了,或者有更好的寫法,直接評論區留言吧,謝謝~

前端學習演算法3:一道來自頭條的面試題

我的學習風格總是想著循序漸進,下面從幾個小題開始。

1 簡單題目熱身(簡單場景遞迴一下)

題目:給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們組成的陣列。 你不能重複利用這個陣列中同樣的元素。

其實就是一個陣列,找出兩個元素,他們的和是指定的值,但是不能一直就一個元素,也就是說加陣列只有4的話,不能返回結果是[4,4]這樣

var nums = [8, 9, 2, 15, 7, 1]
var target = 9
var twoSum = function(nums, target) {
    var result = []
    for(var i=0; i<nums.length;i++){
        for(var j=i+1; j<nums.length;j++){
            if(nums[i]+nums[j] === target){
                result.push([nums[i],nums[j]])
            }
         }
    }
    return result
}
console.log( twoSum(nums, target) )
複製程式碼

執行結果如下

前端學習演算法3:一道來自頭條的面試題

程式碼很簡單,也不用什麼解釋吧。不過要引出來的是下面一種解法,遞迴。

var nums = [8, 9, 2, 15, 7, 1]
var target = 9

var twoSum = function(nums, target) {
	var result = []

	var _sum = function(nums, target){
	    if(nums.length === 0){
		return false
	    }
            for(var i = 1; i < nums.length; i++){
       		    if((nums[0] + nums[i]) === target){
	    		    result.push([nums[0] , nums[i]])
	    	    }
       	    }
       	    nums.splice(0,1) // 或者使用 nums.shift(),刪除陣列第一個元素
       	    return _sum(nums, target)
	 }
	_sum(nums, target)
	return result
}
console.log( twoSum(nums, target) )
複製程式碼

執行結果如下,當然,和上述結果是一樣的

前端學習演算法3:一道來自頭條的面試題

小結 程式碼稍微多了一點,其實思想是一樣的,不過想更好的解釋遞迴,那麼需要再把題目簡化一下

題目:還是上面的題目,加上一個限定條件:你可以假設每種輸入只會對應一個答案。也就是說對應這樣的題幹陣列[11, 7, 10, 2] 只有 [7,2]這樣一個結果。對於只有一個結果這樣的情況,遞迴的寫法程式碼會簡練的多。

var twoSum = function(nums, target) {
    // 1
    var result = []
    if(nums.length === 0){
        // 2
        return false
    }
    for(var i = 1; i < nums.length; i++){
        if((nums[0] + nums[i]) === target){
    	    result.push([nums[0], nums[i]])
    	    // 3
    	    return result
    	}
    }
    // 4
    nums.shift()
    // 5
    return twoSum(nums, target)
}
console.log( twoSum(nums, target) )
複製程式碼

小結 :註釋1 中,會把result置為[], 那豈不是每次遞迴呼叫這個方法時候,開始都是重置?答,因為前提條件已經改了,我們返回的結果只有一個 註釋4 ,修改遞迴執行函式的引數,畢竟要每次不一樣嘛要不然怎麼進行下去。

提問,真的清楚每個 return 的作用嗎?比如 註釋5 那裡的return不寫的話,有什麼區別?這個一定要清楚。

2 題目進階(for迴圈?遞迴?)

前端學習演算法3:一道來自頭條的面試題

根據上面例子,很容易想到3重迴圈,確實也是可以解決的,只是那個判定條件,有些low 並且假如是尋找長度為4子陣列呢?那要for迴圈4次?顯然不可取

前端學習演算法3:一道來自頭條的面試題

3 迴歸主題(重點理解遞迴時候的return)

提問 ,如果用遞迴,這個問題怎麼解決?

var candidates = [2, 3, 8, 4, 10, 15]
var target = 9

var combinationSum2 = function(candidates, target) {
	const buffer = [];
	const result = [];

    const backTrace = (index, target) => {
        if(target == 0) {
            return result.push(buffer.slice());
        }

        if(target < 0) {
            return;
        }

        if(index === candidates.length) return;

        buffer.push(candidates[index]);
        backTrace(index + 1, target - candidates[index]);
        buffer.pop();

        backTrace(index + 1, target);
    }
    backTrace(0, target);

    return result.filter(item => item.length === 3)
};

console.log(combinationSum2(candidates, target))
複製程式碼

執行結果如下

前端學習演算法3:一道來自頭條的面試題

解析程式碼

  • 第一次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為0 target為9
  2. backTrace方法中,有三個終止條件,在第一次執行時候,這三個return沒有一個會執行
  3. 此時 buffer 為 [2]
  • 第二次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為1 target為7
  2. 三個return沒有執行
  3. 此時 buffer 為 [2,3]
  • 第三次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為2 target為4
  2. 三個return沒有執行
  3. 此時 buffer 為 [2,3,8]
  • 第四次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為3 target為 -4
  2. 因為 target < 0 所以執行第二個return
  3. 在這次執行過程中 後面程式碼沒有執行,buffer沒變
  • 返回到第三次執行 backTrace 的過程中
  1. 執行 buffer.pop()後 buffer為 [2,3]
  2. 執行backTrace(index + 1, target) 看上面第三次執行步驟,index為2,target為4
  • 第五次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為3 target為 4
  2. 三個return沒執行
  3. 此時buffer為[2,3,4]
  • 第六次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為4 target為 0
  2. 執行第一個return , result為 [[2,3,4]]
  • 返回到第五次執行 backTrace 的過程中
  1. 執行 buffer.pop()後 buffer為 [2,3]
  2. 執行backTrace(index + 1, target) 看上面第五次執行步驟,index為3,target為4
  • 第七次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為4 target為 4
  2. 三個return沒執行
  3. 此時buffer為[2,3,10]
  • 第八次執行 backTrace
  1. 執行 backTrace(index, target) 方法index為5 target為 -6
  2. ...
  3. ...

看到這裡,相信已經明白了這個程式碼所做的事情,其實這就是回溯演算法,不斷試錯,錯誤的話,回到上個狀態,換引數繼續試錯下去。

其實,最最開始的題目,答案已經有了。因為我們上面的程式碼,最後返回的result中,含有多個和為目標值的陣列,那麼我們只需要再遍歷一下這個result的每一項,篩選出長度符合要求的即可。

相關文章