面試前的準備,筆試練手感

  現在出去面試,很多時候都會讓你先做一份筆試題,而題目一般是中等偏下的水平,不會很難。

  在面試前需要練練手感,以免在解題時沒有思路。

  練手感可以自己準備一些筆試題目,可根據自己的情況選擇合適的題目,而解法可以寫一種或多種。

  還搜集了一些筆試演算法總結,例如《一句話演算法》、《LeetCode》相關的內容。

一、數組

1)輸入多個”A”到”Z”、”a”到”z”、”0″到”9″字元,按ASCII碼值從小到大

  採用數組內置的sort()排序方法,接收一個比較函數,包含兩個參數:a和b。

  1. 當返回值大於0時,a會被移到b的後面。

  2. 當返回值等於0時,a和b的位置不改變。

  3. 當回返回值小於0時,a會被移到b的前面。

str.split('').sort().join('');

2)將一個整數逆序輸出

  採用數組內置的reverse()逆序方法。

str.split('').reverse().join('');

3)混淆方法比較

  1. slice(),開始位置(start)和結束位置(end),由提取元素組成的新數組,不包括結束位置的元素。

  2. splice(),開始位置(start)、要刪除的元素個數(deleteCount)和要插入的一個或多個值(value1,value2,…),由刪除元素組成的新數組

二、數學

1)求最小公倍數

  最小公倍數 = 兩整數的乘積 ÷ 最大公約數。

  求最大公約數的輾轉相除法:

  (1)a%b得餘數c。

  (2)若c=0,則b即為兩數的最大公約數。

  (4)若c≠0,則a=b,b=c,再回去執行(1)。

const divisor = gcd(left, right);
function gcd(left, right) {
  if (right == 0) return left;
  return gcd(right, parseInt(left % right));
}

2)求一個數字對應的二進位數字中1的最大連續數,例如3的二進位為00000011,最大連續2個1

  利用toString()將數字轉換成二進位,然後將0替換成空格。

str = parseInt(str).toString(2);
const arr = str.replace(/0+/g, " ").split(" "),
  lens = arr.map((value) => value.length);
const max = Math.max.apply(this, lens);

3)斐波那契數列

function fib(n) {
  const digits = [1, 1];
  if (n == 1 || n == 2) {
    return 1;
  }
  for (let i = 2; i < n; i++) {
    digits[i] = digits[i - 1] + digits[i - 2];
  }
  console.log(digits[n - 1]);
}

三、排序

  排序有冒泡、選擇、快速、歸併、堆等。此處我就記錄了快速排序的解法。

function quickSort(arr) {
  const length = arr.length;
  if(length <= 1) {
    return arr;
  }
  let left = [], right = [], base = arr[0];
  for(let i=1; i<length; i++) {
    base > arr[i] ? (left.push(arr[i])) : (right.push(arr[i]));
  }
  left = quickSort(left);
  right = quickSort(right);
  return left.concat([base], right);
}

  1. 冒泡排序:對於給定的n個記錄,從第一個記錄開始依次對相鄰的兩個記錄進行比較,當前面的記錄大於後面的記錄時,交換其位置,進行一輪比較和換位後,n個記錄中的最大記錄將位於第n位。

  2. 插入排序:對於給定的一組記錄,初始時假設第一個記錄自成一個有序序列,其餘的記錄為無序序列。接著從第二個記錄開始,按照記錄的大小依次將當前處理的記錄插入到其之前的有序序列中,直至最後一個記錄插入到有序序列中為止。

  3. 歸併排序:利用遞歸與分治技術將數據序列劃分成越來越小的半子表,再對半子表排序,最後用遞歸方法將排好序的半子表合併成為越來越大的有序序列。

  4. 快速排序:採用「分而治之」的思想,把大的拆分為小的,小的再拆分為更小的。將原序列分為兩部分,其中前一部分的所有記錄均比後一部分的所有記錄小,然後再依次對前後兩部分的記錄進行快速排序,遞歸該過程,直到序列中的所有記錄均有序為止。

  5. 選擇排序:經過第一輪比較後得到最小的記錄,然後將該記錄與第一個記錄的位置進行交換;接著對不包括第一個記錄以外的其它記錄進行第二輪比較,得到最小的記錄並與第二個記錄進行位置交換;重複該過程,直到進行比較的記錄只有一個時為止。

  6. 希爾排序:首先將待排序的數組元素分成多個子序列,使得每個子序列的元素個數相對較少,然後對各個子序列分別進行直接插入排序,等整個待排序列「基本有序」後,最終再對所有元素進行一次直接插入排序。

  7. 堆排序:把這些記錄看做一棵順序存儲的二叉樹,然後將其調整為一個大頂堆,再將堆的最後一個元素與堆頂元素(即二叉樹的根結點)進行交換後,堆的最後一個元素即為最大記錄。

四、字元串

1)統計大寫字母的個數

  利用數組的filter()方法過濾不符合條件的字母。

str.split('').filter(value => value >= 'A' && value <= 'Z').length;

2)找出字元串中第一個只出現一次的字元

  利用indexOf()和lastIndexOf()方法,如果兩者相同,就說明只出現了一次。

function first(str) {
  for (let letter of str) {
    if (str.indexOf(letter) == str.lastIndexOf(letter)) {
      console.log(letter);
      return;
    }
  }
  console.log(-1);
}

3)輸入一行字元,分別統計出包含英文字母、空格、數字和其它字元的個數

  利用正則統計出各類字元的個數。

let matches = str.match(/[a-zA-Z]/g);
const num1 = matches && matches.length;

matches = str.match(/\s/g);
const num2 = matches && matches.length;

matches = str.match(/\d/g);
const num3 = matches && matches.length;

matches = str.match(/[^a-zA-Z0-9\s]/g);
const num4 = matches && matches.length;

4)混淆方法比較

  1. 字元串的原型方法charCodeAt()可以讀取到BMP中的字元的碼位。

  2. String對象的靜態方法fromCharCode()可將碼位轉換成字元。

  1. substring(),開始位置(start),結束位置(end)

  2. substr(),開始位置(start),提取的字元數(length)

五、遞歸

1)迴文

function palindrome(str) {
  if (str.length <= 1) return true;
  //首字元和末字元做匹配
  if (str[0] != str[str.length - 1]) return false;
  //將去除首尾字元的字元串傳入函數自身中
  return palindrome(str.substr(1, str.length - 2));
}

六、鏈表

  創建Node類和List類,addLink()方法用於插入節點。

class Node {
  constructor(key) {
    this.next = null;
    this.key = key;
  }
}
class List {
  constructor(key) {
    this.header = new Node(key);
  }
  addLink(node) {
    var current = this.header;
    while (current.next != null) {
      current = current.next;
    }
    current.next = node;
  }
  getLinkList() {
    var current = this.header;
    while (current) {
      console.log(current.key);
      current = current.next;
    }
  }
}

七、二叉樹

  創建Node類和Tree類,insert()方法用於插入節點,根據數值的大小放置合適位置,preOrder、midOrder、backOrder和levelOrder分別是前序、中序、後序和層次遍歷。

class Node {
  constructor(data) {
    this.data = data;
    this.left = null;
    this.right = null;
  }
}
class Tree {
  constructor(datas) {
    this.root = null;
    datas.forEach((value) => {
      const node = new Node(value);
      if (this.root == null) {
        this.root = node;
        return;
      }
      this.insert(this.root, node);
    });
  }
  insert(parent, child) {
    if (parent.data > child.data) {
      parent.left === null
        ? (parent.left = child)
        : this.insert(parent.left, child);
      return;
    }
    parent.right === null
      ? (parent.right = child)
      : this.insert(parent.right, child);
  }
  preOrder(root) {
    if (!root) {
      return;
    }
    console.log(root.data);
    this.preOrder(root.left);
    this.preOrder(root.right);
  }
  midOrder(root) {
    if (!root) {
      return;
    }
    this.midOrder(root.left);
    console.log(root.data);
    this.midOrder(root.right);
  }
  backOrder(root) {
    if (!root) {
      return;
    }
    this.backOrder(root.left);
    this.backOrder(root.right);
    console.log(root.data);
  }
  levelOrder(node = this.root) {
    let queue = [];
    queue.push(node); // 根節點入隊
    while (queue.length) {
      node = queue.shift(); // 出隊
      console.log(node.data); // 訪問該節點
      if (node.left) {
        // 如果它的右子樹不為空
        queue.push(node.left); // 將左子樹的根節點入隊
      }

      if (node.right) {
        // 如果它的右子樹不為空
        queue.push(node.right); // 將右子樹的根節點入隊
      }
    }
  }
}