算法

152 阅读5分钟
1. 给定两个数组,编写一个函数来计算它们的交集。示例 1:输入: nums1 = [1,2,2,1], nums2 = [2,2]

输出: [2,2]

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
const intersect = function(nums1, nums2) {
    const result  = []
    for(let i = 0; i< nums1.length; i++) {
        const existIndex = nums2.findIndex(item => item===nums1[i])
        if (existIndex >= 0) {
            result.push(nums1[i])
            nums2.splice(existIndex, 1)
        }
    }
    return result
};
3. 解析url的query
function qsParse(str) {
  function deep_set(obj, key, value) {
    const path = key.split(/[\[\]]/).filter(x => x)
    // path = [name, name]
    let i = 0
    for (; i< path.length -1; i++) {
      if (obj[path[i]] === undefined) {
        // 没有属性
        if (path[i+1].match(/\d+/)) {
          obj[path[i]] = []
        } else {
          obj[path[i]] = {}
        }
      } 
      obj = obj[path[i]]
    }
    obj[path[i]] = decodeURIComponent(value)
  }
  return str.split('&').reduce((acc, item) => {
    const [key, value] = item.split('=')
    if (!value) {
      return acc
    }
    deep_set(acc, key, value)
    return acc
  }, {})
}
console.log(qsParse('name=xiaoxiao&age=18'))
console.log(qsParse('name&age'))
console.log(qsParse('name[name]=xiao xiao&age=18'))
console.log(qsParse('name[12]=xiaoxiao&age=18'))
console.log(qsParse('name=xiaoxiao'))
4. sumN([1,3, 8, 5, 2], 2, 11) 得出[3, 8]
// 递归

function sumN(arr, m, n) {
  let r = null
  const decision = []
  function inner(i = 0, m, n) { 
    console.log(decision, i, m, n)
    if (r) return
    if (n === 0) {
      r = decision.slice()
      return
    }
    if (n < 0 || m === 0 || i === arr.length) {
      return;
    }
    decision.push(arr[i])
    inner(i+1, m-1, n-arr[i])
    decision.pop(arr[i])
    inner(i+1, m, n)
  }
  inner(0, m, n)
  return r
}

sumN([1, 2, 3, 4, 5], 3, 10)
// 方便理解:答应出decision, i, m, n
    [] 0 3 10
    [ 1 ] 1 2 9
    [ 1, 2 ] 2 1 7
    [ 1, 2, 3 ] 3 0 4
    [ 1, 2 ] 3 1 7
    [ 1, 2, 4 ] 4 0 3
    [ 1, 2 ] 4 1 7
    [ 1, 2, 5 ] 5 0 2
    [ 1, 2 ] 5 1 7
    [ 1 ] 2 2 9
    [ 1, 3 ] 3 1 6
    [ 1, 3, 4 ] 4 0 2
    [ 1, 3 ] 4 1 6
    [ 1, 3, 5 ] 5 0 1
    [ 1, 3 ] 5 1 6
    [ 1 ] 3 2 9
    [ 1, 4 ] 4 1 5
    [ 1, 4, 5 ] 5 0 0
    [ 1, 4 ] 5 1 5
    [ 1 ] 4 2 9
    [] 1 3 10
5. 排序
5.1 冒泡排序
  • 外层循环n-1次
  • 内层循环n-1-i次
function bubbleSort(arr) {
    let count = arr.length
    while(count) {
        for(let i = 0; i < count - 1; i++) {
            if (arr[i] > arr[i+1]) {
                [arr[i], arr[i+1]] = [arr[i+1], arr[i]]
            }
        }
        count--
    }
    return arr
}
console.log(bubbleSort([1, 5, 4, 6, 0, 3])) //  [0, 1, 3, 4, 5, 6]
5.2 插入排序
  • 类似抓牌,先取一张,取的每一张和之前的牌做对比,大的放右,小的放左
function insertSort(arr) {
    const result = []
    result.push(arr[0])
    for(let i = 1; i < arr.length -1; i++) {
        for(let j = result.length -1; j >= 0; j--) {
            if (arr[i] > result[j]) {
                result.splice(j+1, 0, arr[i])
                break
            }       
            if (j === 0) {
                result.unshift(arr[i])
            }
        }
    }
    return result
}
console.log(insertSort([1, 5, 4, 6, 0, 3])) //  [0, 1, 3, 4, 5, 6]
5.3 快速排序
  • 找中间的,大的在右,小的在左,递归
    function quickSort(arr) {
        if (arr.length <= 1) {
            return arr
        }
        const middle = Math.floor(arr.length/2)
        const [ middleItem ] = arr.splice(middle, 1)
        const leftArr = [], rightArr = [];
        for (let i = 0; i < arr.length; i++) {
            arr[i] > middleItem ? rightArr.push(arr[i]) : leftArr.push(arr[i])
        }
        return quickSort(leftArr).concat(middleItem, quickSort(rightArr))
    }
    console.log(quickSort([1, 5, 4, 6, 0, 3]))  // [0, 1, 3, 4, 5, 6]
6. 反转二叉树
function reverseBTree(node) {
  if (!node) {
    return
  }
  const temp = node.left
  node.left = node.right
  node.right = temp
  reverseBTree(node.left)
  reverseBTree(node.right)
}
7 求一个二叉树从左侧看的轮廓,如图,返回【1, 5, 4, 8】? 如果求每行的最大值怎么做
function outlineTree(node, d = 0, outline = []) {
  // 深度优先,先遍历左
  if (!node) {
    return outline
  }
  if (!outline[d]) {
    outline[d] = node.value
  }
  outlineTree(node.left, d + 1, outline)
  outlineTree(node.right, d + 1, outline)
  return outline;
}

// 求最大
function outMaxTree(node, d = 0, outline = []) {
  if (!node) return outline;
  outline[d] = Math.max(outline[d] || -1, node.value)
  outMaxTree(node.left, d + 1, outline)
  outMaxTree(node.right, d + 1, outline)
  return outline
}

在此复习一下二叉树的相关知识
  • 二叉事有且只有一个根结点
  • 每个节点做多有两个子节点
  • 左边的值大于右边的值

构造一颗二叉树

function Node(value) {
  this.value = value
  this.left = null
  this.right = null
}
let root = null
function insert(value) {
  const newNode = new Node(value)
  if (!root) {
    root = newNode
  } else {
    insertNode(root, newNode)
  }
  function insertNode(node, newNode) {
    if (node.value > newNode.value) {
      if (!node.left) {
        node.left = newNode
      } else {
        insertNode(node.left, newNode)
      }
    } else {
      if (!node.right) {
        node.right = newNode
      } else {
        insertNode(node.right, newNode)
      }
    }
  }
}$
![](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/5/14/17211504b6eb3cee~tplv-t2oaga2asx-image.image)
insert(8)
insert(4)
insert(9)
insert(1)
insert(0)
insert(6)

打印二叉树:

二叉树的遍历

    function travel(node, callback) {
      if (node === null) return
      callback(node.value)
      travel(node.left, callback)
      travel(node.right, callback)
    }
    
    travel(root, value => {
      console.log(value)
    })
    // 8 4 1 0 6 9 先序遍历
  • 先序遍历(根左右)
      callback(node.value)
      travel(node.left, callback)
      travel(node.right, callback) // 8 4 1 0 6 9 
  • 中序遍历(左根右)
    ...
      travel(node.left, callback)
      callback(node.value)
      travel(node.right, callback) // 0 1 4 6 8 9
  • 后序遍历(左右拫)
    ...
      travel(node.left, callback)
      travel(node.right, callback)
      callback(node.value) // 0 1 6 4 9 8
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
var findKthLargest = function(nums, k) {
    function bubbleSort(nums) {
        let count = nums.length
        while(count) {
            for(let i = 0; i < count -1; i++) {
                if (nums[i] < nums[i+1]) {
                    [nums[i], nums[i+1]] = [nums[i+1], nums[i]]
                }
            }
            count--
        }
        return nums
    }
    const sortArr = bubbleSort(nums)
    return sortArr[k -1]
};
数组的去重和排序
  • new Set()
// 'new Set()'
set [...new Set(arr)]
  • indexOf,或者includes,性能不好
// 'indexOf或者includes'
+ 在原有基础上操作
方法一:
let arr = [12, 14,13, 16, 28, 99, 13, 12]
// 拿当前的和后边的找
function uniqArr(arr) {
  for(let i = 0; i < arr.length; i++) {
    let otherArr = arr.slice(i + 1)
    if (otherArr.indexOf(arr[i]) > -1) { // 或者includes
      arr.splice(i, 1)
      i-- // 存在数组塌陷的问题
    }
  }
  return arr
}
console.log(uniqArr(arr))
方法二:
+ '将存在的项变为null,再使用filter'
let arr1 = [12, 14,13, 16, 28, 99, 13, 12, ]
function uniqArr(arr) {
  for(let i = 0; i < arr.length; i++) {
    let otherArr = arr.slice(i + 1)
    if (otherArr.indexOf(arr[i]) > -1) { // 或者includes
      arr[i] = null
    }
  }
  return arr.filter(x => x)
}
console.log(uniqArr(arr1))
方法三:
// '开辟一个新的堆内存存放数组'
function uniqArr(arr) {
  let newArr = []
  for(let i = 0; i < arr.length; i++) {
    if(arr.slice(i+1).indexOf(arr[i]) === -1) {
      newArr.push(arr[i])
    }
  }
  return newArr
}
console.log(uniqArr(arr))

方法四:
// '如果存在用最后一项替代当前项,并删除最后一项,i--'
function uniqArr(arr) {
  for(let i = 0; i < arr.length; i++) {
    let otherArr = arr.slice(i + 1)
    if (otherArr.indexOf(arr[i]) > -1) { // 或者includes
      arr[i] = arr.pop()
      i--
    }
  }
  return arr
}
console.log(uniqArr(arr))
  • 对象键值对
function uniqArr(arr) {
  let obj = {}
  for(let i = 0; i < arr.length; i++) {
    let item = arr[i]
    if (typeof obj[item] !== 'undefined') {
      arr[i] = arr[arr.length -1]
      arr.length--
      i--
      continue
    }
    obj[item] = item
  }
  return arr
}
数组扁平化
  • flatten扁平化
  • 数组转成字符串
// 方法1
let arr = [1, [2, 3, [4, 5, [6, 7]]]]
function flat(arr) {
  return arr.toString().split(',').map(item => parseInt(item))
}
console.log(flat(arr))

// 方法二
let arr = [1, [2, 3, [4, 5, [6, 7]]]]
function flat(arr) {
  return JSON.stringify(arr).replace(/[\[\]]/g, '').split(',').map(x => parseInt(x))
}
console.log(flat(arr))
  • 循环验证每一项是不是数组
let arr = [1, [2, 3, [4, 5, [6, 7]]]]
// 方法一:
function flat(arr) {
  while(arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr
}
console.log(flat(arr))
// 方法二:
  • 递归
let arr = [1, [2, 3, [4, 5, [6, 7]]]]
function flat(arr) {
  let newArr = []
  const fn = (arr) => {
    for(let i = 0; i< arr.length; i++) {
      if (Array.isArray(arr[i])) {
        fn(arr[i])
      } else {
        newArr.push(arr[i])
      }
    }
  }
  fn(arr)
  return newArr
}
console.log(flat(arr))
斐波那契数列
  • 前两项是1,之后每一项是之前两项的和
function fibe(index) {
  function fn(index, first = 1, second = 1) {
    if (index <= 1) {
      return 1
    }
    return fibe(index -1) + fibe(index-2)
  }

  return fn(index)
}
console.log(fibe(4))  //5
输出和为n的所有正数序列
  • 连续整数的和: n + (m+n-1)* m /2
function fn(count) {
  const createArr = (i, len) => {
    let arr = new Array(len).fill(i)
    arr = arr.map((x, index) => {
      return x+index
    })
    return arr
  } 
  let middle = Math.ceil(count/2) // 取中间的数字
  let arr = []
  for(let i = 1; i <= middle; i++) {
    for(let j = 2;;j++) {
      const total = (i+(i+j-1))*(j/2)
      if (total > count) {
        break
      } else if(total === count){
        arr.push(createArr(i, j))
        break
      }
    }
  }
  return arr
}
console.log(fn(15))