面试前的准备,笔试练手感

  现在出去面试,很多时候都会让你先做一份笔试题,而题目一般是中等偏下的水平,不会很难。

  在面试前需要练练手感,以免在解题时没有思路。

  练手感可以自己准备一些笔试题目,可根据自己的情况选择合适的题目,而解法可以写一种或多种。

  还搜集了一些笔试算法总结,例如《一句话算法》、《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); // 将右子树的根节点入队
      }
    }
  }
}