2018年6月前端面試經歷(中)

我母雞啊!發表於2018-07-03

前言

上一篇文章,寫了一些出去面試會考到的筆試題,不是很全(哈哈哈,基本上都是靠腦子記的,有些都忘記了~)

傳送門在這裡:2018年6月前端面試經歷(上)~~~

這篇我會寫出一些我碰到的演算法題,解法不統一,希望大家能多多的提供自己的想法和程式碼~

演算法題

1)快速排序

思路:
- 隨機選擇陣列中的一個數 A,以這個數為基準
- 其他數字跟這個數進行比較,比這個數小的放在其左邊,大的放到其右邊
- 經過一次迴圈之後,A 左邊為小於 A 的,右邊為大於 A 的
- 這時候將左邊和右邊的數再遞迴上面的過程

const Arr = [85, 24, 63, 45, 17, 31, 96, 50];
function quickSort(arr) {
    if (arr.length <= 1) {
        return arr;
    }
    let pivotIndex = Math.floor(arr.length / 2);
    let pivot = arr.splice(pivotIndex, 1)[0];
    let left = [];
    let right = [];
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < pivot) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    // 遞迴
    return quickSort(left).concat([pivot], quickSort(right));
}

console.log(quickSort(Arr));

ps:
這是阮老師的一個快排寫法,網上對於這個的爭論很多,第一說了阮老師不應該用splice去取值,應該用下標,還有就是不應該每次都從新開倆個新陣列。
其實我覺得演算法題重要的是思路,實現的方式有很多,不一定說誰對誰錯,效率越好的演算法的確是我們想要的,但是更多的理解一些不同的實現思路,我覺得也是可以的~。

複製程式碼

這裡是不同的聲音: 面試官:阮一峰版的快速排序完全是錯的

2)二分排序法

二分查詢法主要是解決「在一堆有序的數中找出指定的數」這類問題,不管這些數是一維陣列還是多維陣列,只要有序,就可以用二分查詢來優化。

二分查詢是一種「分治」思想的演算法,大概流程如下:

  • 陣列中排在中間的數字 A,與要找的數字比較大小
  • 因為陣列是有序的,所以: a) A 較大則說明要查詢的數字應該從前半部分查詢 b) A
  • 較小則說明應該從查詢數字的後半部分查詢
  • 這樣不斷查詢縮小數量級(扔掉一半資料),直到找完陣列為止
題目:在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。

function Find(target, array) {
    let i = 0;
    let j = array[i].length - 1;
    while (i < array.length && j >= 0) {
        if (array[i][j] < target) {
            i++;
        } else if (array[i][j] > target) {
            j--;
        } else {
            return true;
        }
    }
    return false;
}

//測試用例
console.log(Find(10, [
    [1, 2, 3, 4], 
    [5, 9, 10, 11], 
    [13, 20, 21, 23]
    ])
);
複製程式碼

3)解析url後的引數

function parseParam(url) {
  let obj = {};
  let arr = url.split("?");
  if (arr.length == 1) { //判斷沒有問號
    return "無引數"
  }
  let total = arr[1].split("&");
  for (let i = 0; i < total.length; i++) {
    let single = total[i].split("=");
    if (single[0] == '') { //判斷有?但是沒有引數
      return '無引數'
    }
    if (!single[1]) {
      obj[single[0]] = true;
    } else {
      if (obj[single[0]]) {
        let concat
        if (!Array.isArray(obj[single[0]])) { //判斷是否陣列
          concat = [obj[single[0]]]
        } else {
          concat = obj[single[0]];
        }
        concat.push(single[1]);
        concat = new Set(concat);
        concat = Array.from(concat) //陣列去重
        obj[single[0]] = concat
      } else {
        obj[single[0]] = decodeURI(single[1]) //進行轉碼
      }
    }
  }
  return obj
}

var url = 'http://www.baidu.com/?user=huixin&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';

var params = parseParam(url)

console.log(params)
複製程式碼

4)實現一個簡單的模版引擎:

列:我叫a,年齡b,性別c; let data = { name: '小明', age: 18, } 沒有定義的返回undefined


let template = '我是{name},年齡{age},性別{sex}';
    let data = {
        name: '小明',
        age: 18,
    }
    const  reg= /({([a-zA-Z]+)})/g;
    var r= '',regrounp={};
    while( r = reg.exec(template) ){
        Object.defineProperty(regrounp,r[2],{
            enumerable:true,
            value:r[2]
        })
    }

    var render = (template,regrounp)=>{
        var result='';
        for( key in regrounp){
            if(data[key] == undefined){
                result  = (result || template).replace(new RegExp(`{${regrounp[key]}}`,"g"),undefined);
            }else{		
                result  = (result || template).replace(new RegExp(`{${regrounp[key]}}`,"g"),data[key]);
            }
        }
        return result
    }
    let newtemple = render(template, regrounp);
    console.log(newtemple) // 結果: 我是小明,年齡18,性別undefined
  
複製程式碼

這裡不試不知道,{}這樣宣告的物件,可以直接列舉,Object.defineProperty宣告出的物件,如果不定義enumerable:true的話,是不能用for-in 列舉的。

這裡有一片很好的文章 推薦 編寫一個簡單的JavaScript模板引擎

5)如何快速讓字串變成已千為精度的數字

function exchange(num) {
    num += ''; //轉成字串
    if (num.length <= 3) {
        return num;
    }

    num = num.replace(/\d{1,3}(?=(\d{3})+$)/g, (v) => {
        console.log(v)
        return v + ',';
    });
    return num;
}

console.log(exchange(1234567));

複製程式碼

6) 實現 JS 物件的深拷貝

深拷貝就是在拷貝資料的時候,將資料的所有引用結構都拷貝一份。簡單的說就是,在記憶體中存在兩個資料結構完全相同又相互獨立的資料,將引用型型別進行復制,而不是隻複製其引用關係。

分析下怎麼做 深拷貝

  • 首先假設深拷貝這個方法已經完成,為 deepClone
  • 要拷貝一個資料,我們肯定要去遍歷它的屬性,如果這個物件的屬性仍是物件,繼續使用這個方法,如此往復
function deepClone(o1, o2) {
    for (let k in o2) {
        if (typeof o2[k] === 'object') {
            o1[k] = {};
            deepClone(o1[k], o2[k]);
        } else {
            o1[k] = o2[k];
        }
    }
}
// 測試用例
let obj = {
    a: 1,
    b: [1, 2, 3],
    c: {}
};
let emptyObj = Object.create(null);
deepClone(emptyObj, obj);
console.log(emptyObj.a == obj.a);
console.log(emptyObj.b == obj.b);

複製程式碼

遞迴容易造成爆棧,尾部呼叫可以解決遞迴的這個問題,Chrome 的 V8 引擎做了尾部呼叫優化,我們在寫程式碼的時候也要注意尾部呼叫寫法。遞迴的爆棧問題可以通過將遞迴改寫成列舉的方式來解決,就是通過for或者while來代替遞迴。

7) 求斐波那契數列(兔子數列) 1,1,2,3,5,8,13,21,34,55,89...中的第 n 項

 下面的程式碼中count記錄遞迴的次數,我們看下兩種差異性的程式碼中的count的值:
 let count = 0;
 function fn(n) {
    let cache = {};
    function _fn(n) {
        if (cache[n]) {
            return cache[n];
        }
        count++;
        if (n == 1 || n == 2) {
            return 1;
        }
        let prev = _fn(n - 1);
        cache[n - 1] = prev;
        let next = _fn(n - 2);
        cache[n - 2] = next;
        return prev + next;
    }
    return _fn(n);
}

let count2 = 0;
function fn2(n) {
    count2++;
    if (n == 1 || n == 2) {
        return 1;
    }
    return fn2(n - 1) + fn2(n - 2);
}

console.log(fn(20), count); // 6765 20
console.log(fn2(20), count2); // 6765 13529
複製程式碼

8)演算法的效率

演算法的好壞可以通過演算法複雜度來衡量,演算法複雜度包括時間複雜度和空間複雜度兩個。時間複雜度由於好估算、好評估等特點,是面試中考查的重點。空間複雜度在面試中考查得不多。

常見的時間複雜度有:

  • 常數階 O(1)
  • 對數階 O(logN)
  • 線性階 O(n)
  • 線性對數階 O(nlogN)
  • 平方階 O(n^2)
  • 立方階 O(n^3)
  • !k次方階 O(n^k)
  • 指數階 O(2^n)

隨著問題規模 n 的不斷增大,上述時間複雜度不斷增大,演算法的執行效率越低。

一般做演算法複雜度分析的時候,遵循下面的技巧:

  • 看看有幾重迴圈,一般來說一重就是O(n),兩重就是 O(n^2),以此類推
  • 如果有二分,則為O(logN)
  • 保留最高項,去除常數項

題目:分析下面程式碼的演算法複雜度

let i =0; // 語句執行一次 
while (i < n) { // 語句執行 n 次 
  console.log(`Current i is ${i}`); //語句執行 n 次
  i++; // 語句執行 n 次
}
根據註釋可以得到,演算法複雜度為1 + n + n + n = 1 + 3n,去除常數項,為O(n)。

複製程式碼

演算法題其實還有很多,比如二叉樹的增刪改查等,推薦大家晚上空出時間都去看看~還是挺有意思的~

延伸資源:

在 JavaScript 中學習資料結構與演算法

我接觸過的前端資料結構與演算法


彩蛋

hr面試

1)你覺得你是什麼樣的人?

2)為什麼離職?

3)你最不滿意以前領導的什麼地方?

4)你平時週末都幹什麼?

5)你做的最喜歡的專案是什麼?為什麼?

6)喜歡什麼樣的團隊?


預告:跟新完了這篇,下一篇是面試官的面試了,由於我的技術棧是react,所以接下來的主題是圍繞著react

相關文章