头图

join us!

Mountain" , to provide front-end developers with technical information and a series of basic articles. For a better user experience, please move to our official website novices (1612c57cbe7bd0 https://xhs-rookies.com/ ) to learn and get the latest articles in time.

"Code tailor" , if you are interested in our article or want to make some suggestions, follow "Novices of Xiaohe Mountain" public account, contact us, you can also watch it on WeChat Our article. Every suggestion or approval is a great encouragement to us!

Interview series are updated from time to time, please stay tuned

Preface

This column focuses on explaining the interview handwritten procedural questions/algorithmic questions in the interview.

Note: This column will only involve key content and will not be expanded. For some topics that need to expand knowledge points, we will place the expanded content and overall detailed information at the top of each topic, and you can check it by yourself.

Handwritten program questions/algorithm questions

Handwritten program questions/algorithm questions
Program output topic: the attribute problem between the constructor and the instance object
Program programming questions: flat, flatten the array, realize the effect of flattening the array by yourself
Programming problem: Realize promise all by yourself
Program programming problem: implement reducer by yourself
Programming problem: URL parsing into objects
Programming problem: use setTimeout to write a setInterval
Algorithmic question: the problem of the largest substring without repeated characters
Algorithmic problem: traversing the front, middle and back of the binary tree
Algorithm problem: maze problem
Algorithm question: handwritten bubble sort
Algorithmic problem: the inversion of an incomplete binary tree

Problem analysis

Attribute problem between constructor and instance object

What does the following code output?

function Otaku() {
  this.b = 1
  this.c = 2
}
var person = new Otaku()
Otaku.prototype.b = 4
person.c = 5
console.log('1:', person.b)
console.log('2:', person.c)
person.__proto__.b = 10
console.log('3:', person.b)
console.log('4:', Otaku.prototype.b)

This topic involves the knowledge points between the constructor and the instance object. It also involves the problem of prototype and prototype chain. For details, see JavaScript in-depth from prototype to prototype chain .

Let's reveal the answer first:

1: 1
2: 5
3: 1
4: 10

Did you answer all of them correctly? Let's take a look at this problem together. This question first gave a constructor Otaku , this constructor has two attributes b and c , and then use new to create its instance object person , if you know the prototype, then you must know that the instance object person has obtained the constructor Properties in.

function Otaku() {
  this.b = 1
  this.c = 2
}
var person = new Otaku() // person.b = 1; person.c = 2

Seeing this you will find that the constructor Otaku has an attribute prototype . The prototype attribute of this constructor points to an object, which is instance created by calling the constructor, which is the prototype of this example of person . That is to say, Otaku.prototype.b = 4; the statement b actually points to the b of the prototype.

function Otaku() {
  this.b = 1
  this.c = 2
}
var person = new Otaku()
Otaku.prototype.b = 4 // 修改的是 person 的原型的属性 b
person.c = 5 // 修改的是 person 的 c
console.log('1:', person.b) // person.b = 1
console.log('2:', person.c) // person.c = 5

See here, we also found person property __proto__ , this property also point person prototype, so this sentence b property is also pointing to the prototype b

function Otaku() {
  this.b = 1
  this.c = 2
}
var person = new Otaku() // person.b = 1; person.c = 2
Otaku.prototype.b = 4 // 修改的是 person 的原型的属性 b
person.c = 5 // 修改的是 person 的 c
console.log('1:', person.b) // person.b = 1
console.log('2:', person.c) // person.c = 5
person.__proto__.b = 10 // 修改的事 preson 的原型的属性b
console.log('3:', person.b) // person.b = 1
console.log('4:', Otaku.prototype.b) // Otaku.prototype.b = 10

This is the result. Regarding the knowledge points involved in this question, the focus is still on the prototype and the prototype chain.

Programming questions

1. Flat, flatten the array, realize the effect of flattening the array by yourself

Here are only two relatively simple implementations, and later will involve the use of reduce and stacks, the use of Generator , the prototype chain, etc. For details, see: Interviewer's series of questions: Array flattening (flattening) flat method implementation-Zhihu (zhihu.com) ")

  1. The simplest traversal implementation
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, 'string', { name: '弹铁蛋同学' }]
// concat + 递归
function flat(arr) {
  let arrResult = []
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      arrResult = arrResult.concat(flat(item)) // 递归
      // 或者用扩展运算符
      // arrResult.push(...arguments.callee(item));
    } else {
      arrResult.push(item)
    }
  })
  return arrResult
}
flat(arr)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];
  1. Pass in the array to control the number of recursive layers
// reduce + 递归
function flat(arr, num = 1) {
  return num > 0
    ? arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? flat(cur, num - 1) : cur), [])
    : arr.slice()
}
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]], 5, 'string', { name: '弹铁蛋同学' }]
flat(arr, Infinity)
// [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, "string", { name: "弹铁蛋同学" }];

2. Realize promise all by yourself

Promise.newAll = function (promiseArr) {
  let results = []

  return new Promise((reslove, reject) => {
    promiseArr.forEach((item_promise) => {
      item_promise.then((res) => results.push(res)).catch((err) => reject(err))
    })
    return reslove(results)
  })
}

3. Implement the reducer yourself

Note: Arrow functions cannot be used. The absence of this in the arrow function will cause sourcearr to be an empty object
Array.prototype.fakereduce = function (fn, initnumber = 0) {
  let sum_increase = initnumber
  let sourcearr = this
  for (let i = 0; i < sourcearr.length; i++) {
    sum_increase = fn(sum_increase, sourcearr[i])
  }
  return sum_increase
}

This is only the most basic, only contains the first two parameters, and does not check whether it is a function. (Add the judgment below)

// 判断调用对象是否为数组
if (Object.prototype.toString.call([]) !== '[object Array]') {
  throw new TypeError('not a array')
}
// 判断调用数组是否为空数组
const sourceArray = this
if (sourceArray.length === 0) {
  throw new TypeError('empty array')
}
// 判断传入的第一个参数是否为函数
if (typeof fn !== 'function') {
  throw new TypeError(`${fn} is not a function`)
}

4. URL parsing to objects

Convert the URL entered below into an object:

http://www.baidu.com/s?wd=春节&name=justin

{
    wd: '春节',
    name: 'justin'
}

The focus of this question is segmentation. ? and place them in the object. Different key-value pairs are segmented & The specific code is as follows (only one solution is given here):

let urlToJson = (url = window.location.href) => {
  // 箭头函数默认传值为当前页面url
  url = url.encodeURIComponent()
  let obj = {},
    index = url.indexOf('?'),
    params = url.substr(index + 1)

  if (index != -1) {
    let parr = params.split('&')
    for (let i of parr) {
      let arr = i.split('=')
      obj[arr[0]] = arr[1]
    }
  }
  return obj
}

5. Use setTimeout to write a setInterval

extension: uses setInterval implement a setTimeout

const mySetInterval = (callback, time) => {
  ;(function inner() {
    const timer = setTimeout(() => {
      callback()
      clearTimeout(timer)
      inner()
    }, time)
  })()
}

Algorithm question

1. The problem of the largest substring without repeated characters

For details, please see: 3. The longest substring without repeated characters-LeetCode (leetcode-cn.com) ")

There are roughly two methods: brute force traversal, sliding window

The sliding window code is given here. For detailed explanation, please see the analysis of leetcode

var lengthOfLongestSubstring = function (s) {
  // 哈希集合,记录每个字符是否出现过
  const occ = new Set()
  const n = s.length
  // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
  let rk = -1,
    ans = 0
  for (let i = 0; i < n; ++i) {
    if (i != 0) {
      // 左指针向右移动一格,移除一个字符
      occ.delete(s.charAt(i - 1))
    }
    while (rk + 1 < n && !occ.has(s.charAt(rk + 1))) {
      // 不断地移动右指针
      occ.add(s.charAt(rk + 1))
      ++rk
    }
    // 第 i 到 rk 个字符是一个极长的无重复字符子串
    ans = Math.max(ans, rk - i + 1)
  }
  return ans
}

2. The front, middle and back traversal of the binary tree

The binary tree structure is as follows:

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**

There are many traversal methods, and we use the recursive method here.

preorder traversal:

function preOrderReducer(head) {
  if (head === null) {
    return
  }
  console.log(head.val)
  preOrderReducer(head.left)
  preOrderReducer(head.right)
}

In-order traversal

function preOrderReducer(head) {
  if (head === null) {
    return
  }
  preOrderReducer(head.left)
  console.log(head.val)
  preOrderReducer(head.right)
}

post-order traversal

function preOrderReducer(head) {
  if (head === null) {
    return
  }
  preOrderReducer(head.left)
  preOrderReducer(head.right)
  console.log(head.val)
}

3. Maze problem

Title Description:

In a n * m , the starting point is [0, 0] and the ending point is [n -1, m -1]. Now you need to judge whether the maze has at least one path. If there is a path, it returns true , otherwise it returns false .

n * m There may be a wall in the maze. If the current position is 1, it means that the current position is a wall and you cannot walk.

input: n, m, maze (array)

output: true / false

For example:

input: 3 3 [ [0,1,1],[0,0,0],[0,1,0] ]
output: true

title analysis:

Generally speaking, DFS BFS , 0612c57cbe86be or dynamic programming can be solved, and the solutions are very diverse. There are many variants of this problem, such as output paths and so on.

Sample code:

// BFS
const findMazeWay = (n, m, maze) => {
  if (maze[(0, 0)] === 1) {
    //如果起点为墙壁则直接无解
    return false
  } else {
    return dfsSearch(0, 0, maze)
  }
  function dfsSearch(index_n, index_m, maze) {
    maze[index_n][index_m] = -1 //走过的路判定为-1
    if (index_n === n - 1 && index_m === m - 1) {
      return true
    }
    if (index_m + 1 <= m - 1 && maze[index_n][index_m + 1] === 0)
      dfsSearch(index_n, index_m + 1, maze)
    if (index_n + 1 <= n - 1 && maze[index_n + 1][index_m] === 0)
      dfsSearch(index_n + 1, index_m, maze)
    if (index_m - 1 >= 0 && maze[index_n][index_m - 1] === 0) dfsSearch(index_n, index_m - 1, maze)
    if (index_n - 1 >= 0 && maze[index_n - 1][index_m] === 0) dfsSearch(index_n - 1, index_m, maze)
  }
}

console.log(
  findMazeWay(3, 3, [
    [0, 1, 1],
    [0, 0, 1],
    [1, 0, 0],
  ]),
)
Problem expansion: If successful, please output at least one successful path. (Or output all successful paths)

4. Handwritten bubble sort

sample code:

function mySort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = i; j < arr.length; j++) {
      if (arr[i] > arr[j]) {
        let temp = arr[j]
        arr[j] = arr[i]
        arr[i] = temp
      }
    }
  }
}

5. The inversion of an incomplete binary tree

Title description:

Assuming there is a binary tree of trees, we need to transform all its left subtrees into right subtrees. (For each child node, conversion is required)

The tree structure is as follows:

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**

title analysis:

The point of this question is to traverse the tree, and in the case of traversal, two subtrees need to be exchanged, so the depth traversal of DFS There are many traversal methods, and the solution given here is one of them.

sample code:

const treeReBuild(tree: TreeNode){
    if(tree === null){
        return
    }
    let temp = tree.right
    tree.right = tree.left
    tree.left = temp

    treeReBuild(tree.left)
    treeReBuild(tree.right)
}

小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。