常见算法

链表:

1.链表反转

let reverseList = function(head) {
    //判断下变量边界问题
    if(!head || !head.next)
      return head;
    //初始设置为空,因为第一个节点反转后就是尾部,尾部节点指向null
    let pre = null,
        current = head,
        next;
    // 判断当前节点是否为空,不为空就获取当前节点的下一个节点
    // 然后把当前节点的next设为上一个节点,然后把current设为下一个节点,pre设为当前节点。
    while(current) {
      next = current.next;
      current.next = pre;
      pre = current;
      current = next
    }
    return pre;
  }

树:

1.二叉树的先序,中序,后序遍历(递归方式/非递归方式)

// 递归实现
  function TreeNode(val) {
    this.val = val;
    this.left = this.right = null;
  }
  var traversal = function(root) {
    if(root) {
      //先序
      console.log(root)
      traversal(root.left)
      // 中序
      // console.log(root)
      traversal(root.right)
      // 后序
      // console.log(root)
    }
  }
  // 对于递归的实现来说,只需要理解每个节点都会被访问三次就明白为什么这样实现了。

  // 非递归实现:使用了栈的结构,通过栈的先进后出模拟递归实现
  //先序遍历
  function pre(root) {
    if(root) {
      let stack = [];
      stack.push(root)
      while(stack.length > 0) {
        root  = stack.pop();
        console.log(root);
        if(root.right)
          stack.push(root.right)
        if(root.left)
          stack.push(root.left)
      }
    }
  }
  // 中序遍历
  function mid(root) {
    if(root) {
      let stack = [];
      // 中序遍历是先左再根最后右
      // 所以首先应该先把最左边节点遍历到底依次 push 进栈
      // 当左边没有节点时,就打印栈顶元素,然后寻找右节点
      // 对于最左边的叶节点来说,可以把它看成是两个 null 节点的父节点
      // 左边打印不出东西就把父节点拿出来打印,然后再看右节点
      while(stack.length > 0 || root) {
        if(root){
          stack.push(root)
          root = root.left;
        }else{
          root = stack.pop();
          console.log(root);
          root = root.right;
        }
      }
    }
  }
  // 后序遍历,使用两个栈来遍历
  function pos(root) {
    if(root){
      let stack1 = [], stack2 = [];
      stack1.push(root);
      while(stack1.length > 0) {
        root = stack1.pop();
        stack2.push(root)
        if(root.left) {
          stack1.push(root.left);
        }
        if(root.right){
          stack1.push(root.right)
        } 
      }
      while(stack2.lenght > 0) {
        console.log(s2.pop())
      }
    }
  }

2.中序遍历的前驱后继节点树的深度

// 中序遍历的前驱后继节点
  // 前驱节点
  function predecessor(node) {
    if(!node)
      return;
    if(node.left) {
      return getRight(node.left)
    }
    else {
      let parent = node.parent;
      while(parent && parent.right !== node) {
        node = parent;
        parent = node.parent
      }
      return parent;
    }
  }
  function getRight(node) {
    if(!node) {
      return;
    }
    node = node.right;
    while(node)
     node = node.right
    return node.parent; 
  }
  // 后继节点
  function successor(node) {
    if(!node) 
      return
    if(node.right) {
      return getLeft(node.getRight)
    }  
    else {
      let parent = node.parent;
      while(parent && parent.left !== node) {
        node = parent;
        parent  = node.parent;
      }
      return parent;
    }
  }
  function getLeft(node) {
    if(!node){
      return;
    }
    node = node.left;
    while(node) 
      node = node.left;
    return node.parent;
  }

3.树的深度

  // 树的深度
  var maxDepth = function(root) {
    if (!root) return 0
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1
  }

4.二叉搜索树 

// 二叉搜索树 BST
  class Node {
    constructor(value) {
      this.value = value
      this.left = null;
      this.right = null
    }
  }
  class BST { 
    constructor() {
      this.root = null;
      this.size = 0
    }
    getSize() {
      return this.size;
    }
    isEmpty() {
      return this.size === 0;
    }
    addNode(v)  {
      this.root = this._addChild(this.root, v)
    }
    _addChild(node, v) {
      if(!node) {
        this.size++;
        return new Node(v)
      }
      if(node.value > v) {
        node.left = this._addChild(node.left, v);
      }
      else if(node.value < v) {
        node.right = this._addChild(node.right, v)
      }
      return node;
    }
    // 深度遍历
    preTraversal() {
      this._pre(this.root)
    }
    _pre(node) {
      if (node) {
        console.log(node.value)
        this._pre(node.left)
        this._pre(node.right)
      }
    }
    // 中序遍历可用于排序
    // 对于 BST 来说,中序遍历可以实现一次遍历就
    // 得到有序的值
    // 中序遍历表示先访问左节点,然后访问根节点,最后访问右节点。
    midTraversal() {
      this._mid(this.root)
    }
    _mid(node) {
      if (node) {
        this._mid(node.left)
        console.log(node.value)
        this._mid(node.right)
      }
    }
    // 后序遍历可用于先操作子节点
    // 再操作父节点的场景
    // 后序遍历表示先访问左节点,然后访问右节点,最后访问根节点。
    backTraversal() {
      this._back(this.root)
    }
    _back(node) {
      if (node) {
        this._back(node.left)
        this._back(node.right)
        console.log(node.value)
      }
    }
    // 广度遍历
    breadthTraversal() {
      if(this.root) 
        return null;
      let q = new Queue();
      q.enQueue(this.root)
      while(!q.isEmpty()) {
        let n = q.deQueue();
        console.log(n.value);
        if(n.left) q.enQueue(n.left)
        if(n.right) q.enQueue(n.right)
      } 
    }
    //寻找最大值或最小值
    getMin() {
      return this._getMin(this.root).value;
    }
    _getMin(node) {
      if(!node.left)
        return node;
      return this._getMin(node.left)
    }
    getMax() {
      return this._getMax(this.root).value
    }
    _getMax(node) {
      if(!node.right) 
        return node;
      return this._getMax(node.right);
    }
    floor(v) {
      let node = this._floor(this.root, v);
      return node ? node.value : null 
    }
    _floor(node, v) {
      if(!node)
        return null;
      if(node.value === v)
        return v;
      if(node.value > v) {
        return this._floor(node.left, v);
      }
      let right = this._floor(node.right, v)
      if(right)
        return right;
      return node;
    }
  }

动态规划:

动态规划背后的基本思想非常简单,就是将一个问题拆分为子问题,一般来说这些子问题就是非常相似的,

那么我们可以通过只解决一次每个子问题来达到减少计算量的目的。一旦得出每个子问题的解,就存储该结果以便下次使用。

1.斐波那契数列

 // 1. 存在很严重的性能问题,当n越大的时候,需要的时间是指数增加的
  function fib(n) {
    if(n < 2 && n >= 0){
      return n;
    }
    return fib(n-1) + fib(n-2);
  }
  console.log(fib(10))
  // 动态规划 1.1字底向上分解子问题 1.2 通过变量存储已经计算过的解
  function fib(n) {
    let array = new Array(n + 1).fill(null)
        array[0] = 0
        array[1] = 1
    for (let i = 2; i <= n; i++) {
      array[i] = array[i - 1] + array[i - 2];
    }
    return array[n]
  }
  console.log(fib(10))

2.0 - 1 背包问题

 // 0 - 1 背包问题
    /**
   * @param {*} w 物品重量
   * @param {*} v 物品价值
   * @param {*} C 总容量
   * @returns
   */
  function knapsack(w, v, C) {
    let length = w.length;
    if(length === 0) return 0;
    let array = new Array(length).fill(new Array(C + 1).fill(null));
    for(let i = 0; i <= C; i++) {
      array[0][i] = i >= w[0] ? v[0] : 0;
    }
    // 自底向上开始解决子问题,从物品2开始
    for (let i = 1; i < length; i++) {
      for(let j = 0; j <= C; j++) {
        array[i][j] = array[i-1][j];
        if(j >= w[i]) {
          array[i][j] = Math.max(array[i][j], v[i] + array[i-1][j-1])
        }
      }
    }
    return array[length - 1][C];
  }
  let w = [1, 2, 3], v = [3, 7, 12], C = 5;
  console.log('0 - 1背包问题', knapsack(w, v, C))

3.最长递增子序列

//最长递增子序列
  function lis(n) {
    if(n.length === 0) 
      return 0;
    let array = new Array(n.length).fill(1);
    for(let i = 1; i < n.length; i++) {
      for(let j = 0; j < i; j++) {
        if(n[i] > n[j]) {
          array[i] = Math.max(array[i], 1 + array[j])
        }
      }
    }
    let res = 1;
    for(let i = 0; i < array.length; i++) {
      res = Math.max(res, array[i])
    }
    return res;
  }
  console.log('mzry', lis([0, 3, 4, 17, 2, 8, 6, 10]))

4.约瑟夫环

function ysfh(N, M) {
  if (N < 1 || M < 1) {
    return;
  }
  const source = Array(...Array(N)).map((_, i) => i + 1);
  let index = 0;
  while (source.length) {
    index = (index + M - 1) % source.length;
    console.log(source[index]);
    source.splice(index, 1);
  }
}

排序: 详见https://blog.csdn.net/mzrybbd/article/details/82941406 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值