算法

207 阅读7分钟

binarySearch

function binarySearch(nums, target) {
    let low = 0;
    let high = nums.length - 1;
    while(low <= high) {
      let mid = parseInt((low + high) / 2);
      if(nums[mid] === target) {
        return mid;
      }
      if(nums[mid] > target) {
        high = mid - 1;
      }
      if(nums[mid] < target) {
        low = mid + 1;
      }
    }
    return -1;

  }
function binarySearch(nums, target) {
    let low = 0;
    let high = nums.length -1;
    const binaryWalker = (nums, low, high, target) => {
      if(low > high) return -1;
      const mid = parseInt((low + high) / 2);
      if(nums[mid] === target) return mid;
      if(nums[mid] > target) return binaryWalker(nums, low, mid - 1, target);
      if(nums[mid] < target) return binaryWalker(nums, low + 1, high, target);
    }
    return binaryWalker(nums, low, high, target);
  }

quickSort

function quickSort(arr) {
    if(arr.length <= 1) return arr;
    let left = [];
    let right = [];
    let pivot = arr[0];
    for(let i = 1; i < arr.length; i++) {
      if(arr[i] >= pivot) {
        right.push(arr[i]);
      } else {
        left.push(arr[i]);
      }
    }
    return [...quickSort(left), pivot, ...quickSort(right)];
  }

bubble

function bubbleSort(arr) {
    let i = arr.length - 1;
    while(i >= 0) {
      for(let j = 0; j < i; j++) {
        if(arr[j] > arr[j + 1]) {
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
      }
      i--;
    }
    return arr;
  }

归并

function merge(leftArr, rightArr){  
    var result = [];  
    while (leftArr.length > 0 && rightArr.length > 0){  
      if (leftArr[0] < rightArr[0])  
        result.push(leftArr.shift()); //把最小的最先取出,放到结果集中   
      else   
        result.push(rightArr.shift());  
    }   
    return result.concat(leftArr).concat(rightArr);  //剩下的就是合并,这样就排好序了  
}  

function mergeSort(array){  
    if (array.length == 1) return array;  
    var middle = Math.floor(array.length / 2);       //求出中点  
    var left = array.slice(0, middle);               //分割数组  
    var right = array.slice(middle);  
    return merge(mergeSort(left), mergeSort(right)); //递归合并与排序  
}  

deepTraversal

function deepTraversal(node,nodeList) {  
    if (node) {    
            nodeList.push(node);    
            var children = node.children;    
            for (var i = 0; i < children.length; i++) 
                deepTraversal(children[i],nodeList);    
        }    
    return nodeList;  
}  
function deepTraversal(node) {  
    var nodeList = [];  
    if (node) {  
        var stack = [];  
        stack.push(node);  
        while (stack.length != 0) {  
            // 取最后
            var childrenItem = stack.pop();  
            nodeList.push(childrenItem);  
            var childrenList = childrenItem.children;
            // 越前面放越后// 进入下次循环的时候永远都是第一个先pop出来
            for (var i = childrenList.length - 1; i >= 0; i--) {
                stack.push(childrenList[i]);  
                }
        }  
    }    
    return nodeList;  
}   

wideTraversal

function wideTraversal(node) {  
    var nodes = [];  
    if (node != null) {  
        var queue = [];  
        queue.unshift(node);  
        while (queue.length != 0) {
            // 去最前
            var item = queue.shift();  
            nodes.push(item);  
            var children = item.children;  
            // 正常队列
            for (var i = 0; i < children.length; i++)  {
                queue.push(children[i]);  
            }
        }  
    }  
    return nodes;  
}

flat

nomal

function flat(arr) {
  let arrResult = [];
  arr.forEach(item => {
    if (Array.isArray(item)) {
      arrResult = arrResult.concat(arguments.callee(item));   // 递归
      // 或者用扩展运算符
      // arrResult.push(...arguments.callee(item));
    } else {
      arrResult.push(item);
    }
  });
  return arrResult;
}

reduce

flat = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flat(cur) : cur);
  }, []);
};

function flat(arr) {
  const result = []; 
  const stack = [].concat(arr);  // 将数组元素拷贝至栈,直接赋值会改变原数组
  //如果栈不为空,则循环遍历
  while (stack.length !== 0) {
    const val = stack.pop(); 
    if (Array.isArray(val)) {
      stack.push(...val); //如果是数组再次入栈,并且展开了一层
    } else {
      result.unshift(val); //如果不是数组就将其取出来放入结果数组中
    }
  }
  return result;
}

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();
}

generate

// 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
// 也就是遍历器对象(Iterator Object)。所以我们要用一次扩展运算符得到结果
function* flat(arr, num) {
  if (num === undefined) num = 1;
  for (const item of arr) {
    if (Array.isArray(item) && num > 0) {   // num > 0
      yield* flat(item, num - 1);
    } else {
      yield item;
    }
  }
}

指定深度

const flatten = (arr, depth = 1) =>
  arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) 
  ? flatten(v, depth - 1) : v), []);

去重

forEach

const unique = arr => {
    let obj = {}
    arr.forEach(value => {
        obj[value] = 0
    })
    return Object.keys(obj)
}

filter

const unique = arr => {
    return arr.filter((ele, index, array) => {
        return index === array.indexOf(ele)
    })
}

set

const unique = arr => {
    return [...new Set(arr)]
}

reduce

const unique = arr.reduce((map, item) => {
    map[item] = 0
    return map
}, {})

查找对象路径

function search(object, value) {
    for (var key in object) {
        if (object[key] == value) return [key];
        if (typeof(object[key]) == "object") {
            var temp = search(object[key], value);
            if (temp) return [key, temp].flat();
        }
    }
}

二叉树

计算二叉树最大深度

var maxDepth = function (root) {
    if (root == null) return 0
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right))
};

将二叉树以二维数组形式表现

var levelOrder = function(root) {
    let ans = []
    helper(root, ans, 0)
    return ans
};

function helper(node, ans, i){
    if (node == null) return
    if (i == ans.length) ans.push([])
    ans[i].push(node.val)

    helper(node.left, ans, i + 1)
    helper(node.right, ans, i + 1)
}

树数组互相转化

树转数组就是深度遍历

function array2Tree (data, pid) {
  let result = []
  let temp
  for (let i = 0; i < data.length; i++) {
    if (data[i].parentId === pid) {
      let obj = {id: data[i].id, name: data[i].name}
      temp = array2Tree(data, data[i].id)
      if (temp.length > 0) {
        obj.children = temp
      }
      result.push(obj)
    }
  }
  return result
}

优化版

function list_to_tree(list) {
  let map = {}, node, root = [], i;
  for(i = 0; i < list.length; i++) {
    map[list[i].id] = i
    list[i].children = []
  }
  for(i = 0; i < list.length; i++) {
    const node = list[i]
    if(list[i].pid !== 0) {
      list[map[node.pid]].children.push(node)
    }else {
      root.push(node)
    }
  }
  return root
}

可能性问题

给定几种面额的硬币和一个总额,使用最少的硬币凑成这个总额。

var coinChange = function (coins, amount) {
    let max = amount + 1
    let dp = new Array(amount + 1)
    dp.fill(max)
    dp[0] = 0

    for (let i = 1; i < max; i++) {
        for (let j = 0; j < coins.length; j++) {
            if (coins[j] <= i) {
                dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1)
            }
        }
    }
    return dp[amount] > amount ? -1 : dp[amount]
};

使用了动态规划(DP),将从 0 到目标额度所需的最小硬币数都列出来。

求出从矩阵左上角走到右下角,且只能向右向下移动,一共有多少种可能性。

var uniquePaths = function (m, n) {
    const pos = new Array(m)
    for (let i = 0; i < m; i++) {
        pos[i] = new Array(n)
    }
    for (let i = 0; i < n; i++) {
        pos[0][i] = 1
    }
    for (let i = 0; i < m; i++) {
        pos[i][0] = 1
    }
    for (let i = 1; i < m; i++) {
        for (let j = 1; j < n; j++) {
            pos[i][j] = pos[i - 1][j] + pos[i][j - 1]
        }
    }
    return pos[m - 1][n - 1]
};

这题就是使用了动态规划逐步列出每一格的可能性,最后返回右下角的可能性

查找 二维矩阵

var searchMatrix = function (matrix, target) {
    if (matrix.length == 0) return false
    let row = 0, col = matrix[0].length - 1
    while (true) {
        if (matrix[row][col] > target && col > 0) {
            col--
        } else if (matrix[row][col] < target && row < matrix.length - 1) {
            row++
        } else if (matrix[row][col] == target) {
            return true
        } else {
            break
        }
    }
    return false
};

回文

var longestPalindrome = function (s) {
    let maxLength = 0, left = 0, right = 0
    for (let i = 0; i < s.length; i++) {
        let singleCharLength = getPalLenByCenterChar(s, i, i)
        let doubleCharLength = getPalLenByCenterChar(s, i, i + 1)
        let max = Math.max(singleCharLength, doubleCharLength)
        if (max > maxLength) {
            maxLength = max
            left = i - parseInt((max - 1) / 2)
            right = i + parseInt(max / 2)
        }
    }
    return s.slice(left, right + 1)
};

function getPalLenByCenterChar(s, left, right) {
    // 中间值为两个字符,确保两个字符相等
    if (s[left] != s[right]){
        return right - left
    }
    while (left > 0 && right < s.length - 1) {
        left--
        right++
        if (s[left] != s[right]){
            return right - left - 1
        }
    }
    return right - left + 1
}

路径题

路径题可以使用深度优先(DFS)和广度优先(BFS)算法来做。我比较常用的是使用 DFS 来做。通过递归将走过的路径进行标记来不断往前找到目标路径。如:

通过给定单词在二维字母数组中查找是否能使用邻近字母组成这个单词(212题)

let hasWord = false

var findWords = function (board, words) {
    var ans = []
    for (let word of words) {
        for (let j = 0; j < board.length; j++) {
            for (let i = 0; i < board[0].length; i++) {
                if (board[j][i] == word[0]) {
                    hasWord = false
                    DFS(word, board, 0, j, i, "")
                    if (hasWord) {
                        if (!ans.includes(word))
                            ans.push(word)
                    }
                }
            }
        }
    }
    return ans
};

function DFS(word, board, index, j, i, subStr) {
    if (word[index] == board[j][i]) {
        subStr += board[j][i]
        board[j][i] = "*"
        if (j < board.length - 1)
            DFS(word, board, index + 1, j + 1, i, subStr)
        if (j > 0)
            DFS(word, board, index + 1, j - 1, i, subStr)
        if (i < board[0].length - 1)
            DFS(word, board, index + 1, j, i + 1, subStr)
        if (i > 0)
            DFS(word, board, index + 1, j, i - 1, subStr)
        board[j][i] = word[index]
    }
    if (index >= word.length || subStr == word) {
        hasWord = true
    }
}

由于 DFS 是一条路走到黑,如果每个元素都去使用 DFS 来找会出现超时的情况。如果条件允许(如查找递增数组)可以通过设置缓存来优化 DFS 查找超时问题。

链表

链表从 JS 的角度来说就是一串对象使用指针连接的数据结构。合理使用 next 指针改变指向来完成对链表的一系列操作。如:

链表的排序:

var sortList = function (head) {
    if (head == null || head.next == null) return head

    let prev = null, slow = head, fast = head
    while (fast != null && fast.next != null) {
        prev = slow
        slow = slow.next
        fast = fast.next.next
    }

    prev.next = null;

    let l1 = sortList(head)
    let l2 = sortList(slow)

    return merge(l1, l2)
};

function merge(l1, l2) {
    let l = new ListNode(0), p = l;

    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            p.next = l1;
            l1 = l1.next;
        } else {
            p.next = l2;
            l2 = l2.next;
        }
        p = p.next;
    }

    if (l1 != null)
        p.next = l1;

    if (l2 != null)
        p.next = l2;

    return l.next;
}

使用了自上而下的归并排序方法对链表进行了排序。使用 slow.next 和 fast.next.next 两种速度获取链表节点,从而获取中间值。

求数组中第K大的值

var findKthLargest = function (nums, k) {
    for (let i = 0; i <= k; i++) {
        let max = i
        for (let j = i; j < nums.length; j++) {
            if (nums[j] > nums[max]) max = j
        }
        swap(nums, i, max)
    }
    return nums[k - 1]
};

function swap(arr, a, b) {
    let tmp = arr[a]
    arr[a] = arr[b]
    arr[b] = tmp
}

递增子序列

function lis(seq) {
  const valueToMax = {}
  let len = seq.length
  for (let i = 0; i < len; i++) {
    valueToMax[seq[i]] = 1
  }

  let i = len - 1
  let last = seq[i]
  let prev = seq[i - 1]
  while (typeof prev !== 'undefined') {
    let j = i
    while (j < len) {
      last = seq[j]
      if (prev < last) {
        const currentMax = valueToMax[last] + 1
        valueToMax[prev] =
          valueToMax[prev] !== 1
            ? valueToMax[prev] > currentMax
              ? valueToMax[prev]
              : currentMax
            : currentMax
      }
      j++
    }
    i--
    last = seq[i]
    prev = seq[i - 1]
  }

  const lis = []
  i = 1
  while (--len >= 0) {
    const n = seq[len]
    if (valueToMax[n] === i) {
      i++
      lis.unshift(len)
    }
  }

  return lis
}

动态规划


var arr = [2,3,5,10,7,20,100,0];
let dp =[];
for(let i=0;i<arr.length;i++){
    dp[i]=1;
}
let res = 0;
for(let i=1;i<arr.length;i++){
    for(let j=0;j<i;j++){
        if(arr[j]<arr[i]){
            dp[i] = Math.max(dp[i],dp[j]+1)  // dp[i]代表第i个位置上升子序列中元素的长度
        }
    }
    res = Math.max(res,dp[i])
}