High frequency algorithm (real interview question)

Posted by pcbytes on Fri, 14 Jan 2022 17:11:23 +0100

High frequency algorithm interview questions

★★★ bubble sorting

Compare two from the start position for n rounds

  • Basic Edition

    function bubbleSort (arr) {
    // Execute round i + 1
    for (let i = 0; i < arr.length; i++) {
      for (let j = 0; j < arr.length - 1; j++) {
        // Compare the former with the latter in pairs
        if (arr[j] > arr[j + 1]) {
          // Swap two variable values
          let tmp = arr[j]
          arr[j] = arr[j + 1]
          arr[j + 1] = tmp
        }
      }
    }
    }
    
  • Improved version

    function bubbleSort (arr) {
    // Bubbling processes a maximum / minimum value each time, and i represents the position of the maximum value each time
    for (let i = arr.length - 1; i > 0; i--) {
      for (let j = 0; j < i; j++) {
        if (arr[j] > arr[j + 1]) {
          // Swap two variable values
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
        }
      }
    }
    }
    

★★ Select Sorting

Select the maximum / minimum value for n rounds each time

  • Basic Edition

function selectSort (arr) {
  // Index of the minimum value of each
  for (let i = 0, len_i = arr.length; i < len_i; i++) {
    // Minimum index per round
    let index = i

    for (let j = i, len_j = arr.length; j < len_j; j++) {
      if (arr[index] > arr[j]) {
        // Minimum change
        index = j
      }
    }
    // Put the minimum value in the index of i
    [arr[i], arr[index]] = [arr[index], arr[i]]
  }
}
  • Improved version

function selectSort (arr) {
  // Discharge lenth - 1 minimum
  for (let i = 0, len_i = arr.length - 1; i < len_i; i++) {
    let index = i
    for (let j = i + 1, len_j = arr.length; j < len_j; j++) {
      if (arr[index] > arr[j]) {
        index = j
      }
    }
    // Determine whether the minimum index has changed
    if (index !== i) {
      [arr[i], arr[index]] = [arr[index], arr[i]]
    }
  }
}

★★ insert sort

By default, an ordered array is incrementally inserted into the array

  • Version one

function insertSort (arr) {
  // Index to insert
  for (let i = 1, len_i = arr.length; i < len_i; i++) {
    let cur = i
    // Insert into a sequenced sequence
    while (cur > 0 && arr[cur] < arr[cur - 1]) {
      [arr[cur], arr[cur - 1]] = [arr[cur - 1], arr[cur]]
      cur--
    }
  }
}
  • Version 2

function insertSort (arr) {
  // Index to insert
  for (let i = 1, len = arr.length; i < len; i++) {
    let tmp = arr[i]

    // Insert into a sequenced sequence
    for (let j = i - 1; j >= 0 && arr[j] > tmp; j--) {
      arr[j + 1] = arr[j]
    }
    arr[j + 1] = tmp
  }
}

★★ Hill sort

Incremental insertion sort} the normal insertion sort interval is 1. Hill sort sets the interval to be greater than 1 by default, and then decreases to 1

function shellSort (arr) {
  let len = arr.length
  let step = 1
  let dis = 3
  // Set maximum interval
  while (step < len / dis) {
    step = step * dis + 1
  }

  // Interval decrement
  for (; step > 0; step = Math.floor(step / dis)) {
    // Insertion sort of a single interval
    for (let i = step; i < len; i++) {
      let tmp = arr[i]
      let j = i - step

      while (j >= 0 && arr[j] > tmp) {
        arr[j + step] = arr[j]
        j -= step
      }
      arr[j + step] = tmp
    }
  }
}

★★ merge sort

Before sorting, split the array into two parts, and then merge and sort the two parts

function mergeSort (arr) {
  if (arr.length <= 1) return ;
  let left = 0,
  let right = arr.length - 1,
  let mid = parseInt((left + right) * 0.5)

  sort(arr, left, mid)
  sort(arr, mid + 1, right)
  merge(arr, left, mid, right)
}
function sort (arr, left, right) {
  // Decompose into one element
  if (left >= right) return ;
  let mid = parseInt((left + right) * 0.5)

  sort(arr, left, mid)
  sort(arr, mid + 1, right)
  merge(arr, left, mid, right)
}
function merge (arr, left, mid, right) {
  // The merged [left, mid] [mid + 1, right] parts have been sorted
  let i = left
  let j = mid + 1
  let tmp = []

  // Insert the elements of an array in sequence
  while (i <= mid && j <= right) {
    // The equality here ensures the stability of the sorting algorithm (that is, the position of two equal numbers will not change after sorting)
    if (arr[i] <= arr[j]) {
      tmp.push(arr[i])
      i++
    } else {
      tmp.push(arr[j++])
    }
  }
  // Process remaining left / right elements
  while (i <= mid) tmp.push(arr[i++])
  while (j <= right) tmp.push(arr[j++])
  for (let i = 0, len = tmp.length; i < len; i++) {
    arr[left + i] = tmp[i]
  }
}

★★★ quick sort

Before sorting, split the array into two parts according to the specified base point. One part is less than or equal to the base point and the other part is greater than the base point

  • Version one

function quickSort (arr) {
  if (arr.length <= 1) return ;
  let left = 0
  let right = arr.length - 1

  sort(arr, left, right)
}
function sort (arr, left, right) {
  if (left >= right) return ;
  let baseValue = arr[left]
  let start = left
  let end = right
  while (start < end) {
    // Find the index of the value on the right that is less than the base point
    while (start < end && arr[end] >= baseValue) end--
    // Find the index of the value greater than the base point on the left
    while (start < end && arr[start] <= baseValue) start++
    if (start < end) {
      [arr[start], arr[end]] = [arr[end], arr[start]]
    }
  }
  [arr[left], arr[start]] = [arr[start], arr[left]]
  sort(arr, left, start - 1)
  sort(arr, start + 1, right)
}
  • Version 2

function quickSort(arr) {
  if (arr.length <= 1) return ;
  let left = 0
  let right = arr.length - 1

  sort(arr, left, right)
}
function sort (arr, left, right) {
  if (left >= right) return ;
  let baseValue = arr[left]
  let leftArr = []
  let rightArr = []

  for (let i = left; i <= right; i++) {
    if (arr[i] <= baseValue) {
      leftArr.push(arr[i])
    } else {
      rightArr.push(arr[i])
    }
  }
  let mid = left + leftArr.length - 1
  for (let i = 0, len = leftArr.length; i < len; i++) {
    arr[left + i] = leftArr[i]
  }
  for (let i = 0, len = rightArr.length; i < len; i++) {
    arr[mid + i + 1] = rightArr[i]
  }
  [arr[left], arr[mid]] = [arr[mid], arr[left]]
  sort(arr, left, mid - 1)
  sort(arr, mid + 1, right)
}

★★ heap sorting

Treat the array as a heap structure, where index = 0 is the root node

The child nodes of the node pointed to by index are 2 * index + 1, 2 * index + 20 - > 1, 2, 1 - > 3, 4, 2 - > 5, 6

The actual operation is to adjust the heap. You can learn about the data structure of the heap

Reference https://www.cnblogs.com/chengxiao/p/6129630.html

// Heap sort
function heapSort (arr) {
  // Start from the last non leaf node to build a large top heap, that is, find the maximum number each time
  for (let i = Math.floor(arr.length * 0.5 - 1); i >= 0; i--) {
    adjustHeap(arr, i, arr.length)
  }
  // Putting the largest node at the end is equal to arranging the order, and building a large top heap for the remaining elements
  for (j = arr.length - 1; j > 0; j--) {
    [arr[0], arr[j]] = [arr[j], arr[0]]
    // Most of them do not need to be adjusted, so start directly from the root node
    adjustHeap(arr, 0, j)
  }
}
// Adjustment reactor
function adjustHeap (arr, i, len) {
  // k = 2 * k + 1 means that when two nodes are exchanged, the original heap structure will be confused, and the child nodes need to be readjusted
  let cnt = 0
  for (let k = 2 * i + 1; k < len; k = 2 * k + 1) {
    // Find the largest node between the left child node and the right child node
    if (k + 1 < len && arr[k] < arr[k + 1]) k++
    // Determine whether to exchange with child nodes
    if (arr[k] > arr[i]) {
      [arr[k], arr[i]] = [arr[i], arr[k]]
      // After the exchange is completed, the next level node shall be readjusted
      i = k
    } else {
      break ;
    }
  }
}

★★★ Fibonacci sequence

Fibonacci sequence 0 1 1 2 3 5 8 13 Item n is the sum of items n-1 and n-2. The first item is 0 and the second item is 1

  • Use cycle

function fibonacci (n) {
  if (n <= 2) return n - 1
  let n1 = 0, n2 = 1

  for (let i = 2; i < n; i++) {
    [n1, n2] = [n2, n1 + n2]
  }
  return n2
}
  • Use recursion

Large memory consumption

function fibonacci (n) {
  return n <= 2 ? n - 1 : Fibonacci(n - 1) + Fibonacci(n - 2)
}

// Set cache
let cache = {}
function fibonacci (n) {
  if (n <= 0) return 0
  if (n <= 2) return n - 1
  if (cache[n]) return cache[n] 

  return cache[n] = Fibonacci(n - 1) + Fibonacci(n - 2)
}

★★ Hanoi Tower problem

Hanoi Tower rule reference https://baike.baidu.com/item/%E6%B1%89%E8%AF%BA%E5%A1%94/3468295

  • Recursive solution

// The three columns are a, B, C, and a is the initial position
function hanoi (n, current = 'A', temp = 'B', target = 'C') {
  if (n <= 0) return 0
  let sum = 1

  sum += hanoi(n - 1, current, target, temp)
  console.log(current + ' ---> ' + target)
  sum += hanoi(n - 1, temp, target, current)
  return sum
}

★★ merge two ordered arrays

Merge method of direct reference merge sort

function mergeArr (arr1, arr2) {
  let result = []
  // p1 and p2 are the indexes of arr1 and arr2, respectively
  let p1 = p2 = 0
  let len1 = arr1.length
  let len2 = arr2.length

  while (p1 < len1 && p2 < len2) {
    arr1[p1] <= arr2[p2] ? result.push(arr1[p1++]) : result.push(arr2[p2++])
  }
  while (p1 < len1) result.push(arr1[p1++])
  while (p2 < len2) result.push(arr2[p2++])
  return result
}
  • API version

function mergeArr (arr1, arr2) {
  // sort custom collation
  return (arr1.concat(arr2)).sort((a, b) => a - b)
}

★★ duplicate numbers in the array

Array de duplication can be referenced https://segmentfault.com/a/1190000016418021

  • Simple version

function noRepeat (arr) {
  return [...new Set(arr)]
}
  • Circular version

function noRepeat (arr) {
  let result = []

  // n+1 non repeating element
  for (let i = 0, len_i = arr.length; i < len_i; i++) {
    let isNoRepeat = true
    for (let j = 0, len_j = result.length; j < len_j; j++) {
      // If there are only basic data types in the array, you can directly use = = =
      isNoRepeat = !isEqual(arr[i], result[j])
      if (!isNoRepeat) break;
    }
    if (isNoRepeat) result.push(arr[i])
  }
  return result;
}

// Methods used
function isEqual (obj1, obj2) {
  // Judge whether the types are the same
  let type = getType(obj1)
  if (type !== getType(obj2)) return false;
  // Judge basic data type value
  if (typeof obj1 !== 'object') return obj1 === obj2;
  // Judge address
  if (obj1 === obj2) return true;

  // Judge whether the values are the same
  switch (type) {
    // object
    case '[object Object]':
      // Get traversable property
      let keys1 = Object.keys(obj1)
      let keys2 = Object.keys(obj2)
      if (keys1.length !== keys.length) return false
      // Equal length
      for (let i = 0, len = keys1.length; i < len; i++) {
        let key = keys1[i]
        if (!isEqual(obj1[key], obj2[key])) return false;
      }
      break;
    // Map
    case '[object Map]':
    // Set
    case '[object Set]':
      // Convert to array
      obj1 = Array.from(obj1)
      obj2 = Array.from(obj2)
    // array
    case '[object Array]':
      if (obj1.length !== obj2.length) return false;
      for (let i = 0, len = obj1.length; i < len; i++) {
        if (!isEqual(obj1[i], obj2[i])) return false;
      }
      break;
    // ...
    default: break;
  }
  return true;
}
function getType (obj) {
  // You can determine the data type of array Map Set
  return Object.prototype.toString.call(obj)
}
  • Marked version

function noRepeat (arr) {
  let result = []
  // Multiple data types can be determined using tags and maps
  // Here, you can also use the WeakMap weak reference to check the data js WeakMap type yourself
  const flag = new Map()

  for (let i = 0, len = arr.length; i < len; i++) {
    // Preliminary judgment
    if (!flag.has(arr[i])) {
      let isNoRepeat = true
      // Basic data type direct insertion
      // Reference data type
      if (typeof arr[i] === 'object') {
        for (let j = 0, len_j = result.length; j < len_j; j++) {
          isNoRepeat = !isEqual(arr[i], result[j])
          if (!isNoRepeat) break;
        }
      }
      // No repetition
      if (isNoRepeat) {
        result.push(arr[i])
        flag.set(arr[i], true)
      }
    }
  }
  return result
}

★★ intersection of two arrays

Intersection is the same part, not two sets, so the final intersection needs to retain the same elements with different indexes

  • Basic Edition

function intersection (arr1, arr2) {
  let flag = {}
  let result = []

  for (let i = 0, len_i = arr1.length; i < len_i; i++) {
    let item = arr1[i]
    // It's OK to search whether indexOf or includes is used
    for (let j = 0, len_j = arr2.length; j < len_j; j++) {
      // To determine equality, it is recommended to use the previously encapsulated isEqual method
      // isEqual(arr1[i], arr2[i])
      if (item === arr2[j] && !flag[j]) {
        result.push(item)
        flag[j] = true
        break;
      }
    }
  }
  return result
}
  • Map markup

// Defect if the element value has a reference data type, an error may occur
function intersection (arr1, arr2) {
  let map = new Map()
  let result = []

  for (let i = 0, len = arr1.length; i < len; i++) {
    let item = arr1[i]
    if (map.has(item)) map.set(item, map.get(item) + 1)
    else map.set(item, 1)
  }
  for (let i = 0, len = arr2.length; i < len ;i++) {
    let item = arr2[i]
    if (map.has(item)) {
      result.push(item)
      map.set(item, map.get(item) - 1)
    }
  }
  return result
}
  • API version

// It is more suitable to find the intersection of two sets
function intersection (arr1, arr2) {
  // return noRepeat(arr1).filter(v => arr2.includes(v))
  return [...new Set(arr1)].filter(v => arr2.includes(v))
}

★★ rotate array

In reverse order? Or rotate the array to the right by k steps, and change the original array directly without returning a new array?

  • Reverse order

function reverse (arr) {
  let len = arr.length

  for (let i = 0, mid = parseInt(len * 0.5); i < mid; i++) {
    [arr[i], arr[len - i - 1]] = [arr[len - i - 1], arr[i]]
  }
  return arr
}
  • Rotate k steps

function rotateArr (arr, k) {
  let len = arr.length
  let tmp = [...arr]

  if (k < 0) k = (k % len) + len
  for (let i = 0; i < len; i++) {
    let index = (i + k) % len
    arr[index] = tmp[i]
  }
  return arr
}

★★ sum of two numbers

Given an integer array nums # and a target value target, please find the two integers with sum as the target value in the array and return their array subscripts.

You can assume that each input will correspond to only one answer. However, you cannot reuse the same elements in this array.

Example nums = [2, 7, 11, 15] target = 9, then the result returns [0, 1], which is the subscript of 2 and 7

function twoSum (arr, target) {
  let map = new Map()

  for (let i = 0, len = arr.length; i < len; i++) {
    let diff = target - arr[i]
    if (map.has(diff)) {
      return [map.get(diff), i]
    }
    map.set(arr[i], i)
  }
  return 'not found'
}

★★ realization idea of climbing steps

Suppose you are climbing stairs. You need n steps to reach the roof.

You can climb one or two steps at a time. How many different ways can you climb to the roof?

Note: given n is a positive integer.

Example n = 2 -- > 2 two methods 1st order + 1st order and 2nd order

  • Realization idea

// First of all, the title requires Mei to list all the steps to climb the stairs, and you can climb only one step at a time (no matter how you climb, you can only climb one step to the roof)
// This can give a basic idea to climb once (1 or 2) - > climb the remaining steps. The termination condition is that the number of remaining steps is 0
function climb (n) {
  if (n === 0) return 1  // One return here represents a way to climb to the roof
  climb(n - 1)  // Climb the remaining steps after climbing step 1
  if (n - 2 >= 0) {
      climb(n - 2)  // After climbing 2 steps, climb the remaining steps. At this time, judge whether the number of remaining steps is greater than or equal to 2      
  }
}

// Improve the termination condition to n < = 2
function climb (n) {
  if (n <= 0) return 0    // Direct termination
  if (n <= 2) return n    // There is one solution for the remaining first order and two solutions for the second order

  return climb(n - 1) + climb(n - 2)
}

// At this point, you can find that climb(n - 1) calls climb(n - 2) internally, and climb(n) also calls climb(n - 2)
// You might as well save the calculation results
let cache = {}
function climb (n) {
  if (n <= 0) return 0    // Direct termination
  if (n <= 2) return n    // There is one solution for the remaining first order and two solutions for the second order
  if (cache[n]) return cache[n]

  return cache[n] = climb(n - 1) + climb(n - 2)
}
// be accomplished

Topics: Javascript Front-end Algorithm Interview