中等

98.验证二叉搜索树

方法一:递归

// n为二叉树的节点数量
// way1:递归
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈内存开销为O(n)
var isValidBST = function(root) {
  var dfs = (root, min, max) => {
    if (root === null) {
      return true;
    }
    if (root.val <= min || root.val >= max) {
      return false;
    }
    return dfs(root.left, min, root.val) && dfs(root.right, root.val, max);
  }
  return dfs(root, -Infinity, Infinity)
};

方法二:中序遍历

// n为二叉树的节点数量
// way2:中序遍历
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈内存开销为O(n)
var isValidBST = function (root) {
  var min = -Infinity;
  var inOrder = (root) => {
    if (root === null) {
      return true;
    }
    if (!inOrder(root.left)) {
      return false;
    }
    if (root.val <= min) {
      return false;
    }
    min = root.val;
    return inOrder(root.right);
  }
  return inOrder(root);
}

102.二叉树的层序遍历

// n为二叉树节点的数量
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下是一个链表,队列的内存开销为o(n)
var levelOrder = function (root) {
  const list = [];
  const queue = [];
  if (root !== null) {
    queue.push(root);
  }
  while (queue.length > 0) {
    const arr = [];
    for (let i = 0, len = queue.length; i < len; i++) {
      const node = queue.shift();
      arr.push(node.val);
      if (node.left !== null) {
        queue.push(node.left);
      }
      if (node.right !== null) {
        queue.push(node.right);
      }
    }
    list.push(arr);
  }
  return list;
};

105.从前序与中序遍历序列构造二叉树

方法一:递归 + 哈希表

// n为二叉树节点的数量
// way1: 递归 + 哈希表
// 时间复杂度:O(n),需要遍历一遍每个节点
// 空间复杂度:O(n),哈希表存储每个节点的内存开销
var buildTree = function (preorder, inorder) {
  const map = new Map();
  for (let i = 0; i < inorder.length; i++) {
    map.set(inorder[i], i);
  }
  const buildHelper = (pStart, pEnd, iStart, iEnd) => {
    if (pStart > pEnd) {
      return null;
    }
    const rootVal = preorder[pStart];
    const root = new TreeNode(rootVal);
    const rootIndex = map.get(rootVal);
    const leftNum = rootIndex - iStart;
    root.left = buildHelper(pStart + 1, pStart + leftNum, iStart, rootIndex - 1);
    root.right = buildHelper(pStart + 1 + leftNum, pEnd, rootIndex + 1, iEnd);
    return root;
  }
  return buildHelper(0, preorder.length - 1, 0, inorder.length - 1);
};

方法二:递归(无哈希表)

// n为二叉树节点的数量
// way2: 递归(无哈希表)
// 时间复杂度:O(n),需要遍历一遍每个节点
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈的开销为O(n)
var buildTree = function (preorder, inorder) {
  let p = 0;
  let i = 0;
  const buildHelper = (stop) => {
    if (inorder[i] !== stop) {
      const root = new TreeNode(preorder[p++]);
      root.left = buildHelper(root.val);
      i++;
      root.right = buildHelper(stop);
      return root;
    }
    return null;
  }
  return buildHelper();
}

114.二叉树展开为链表

方法一:前序遍历 + 存储节点

// way1: 前序遍历 + 存储节点
// n为二叉树节点的数量
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈的内存开销为O(n)
var preOrder = (root, list) => {
  if (root === null) {
    return;
  }
  list.push(root);
  preOrder(root.left, list);
  preOrder(root.right, list);
}
var flatten = function (root) {
  const list = [];
  preOrder(root, list);

  for (let i = 1, len = list.length; i < len; i++) {
    const prev = list[i - 1];
    const curr = list[i];
    prev.left = null;
    prev.right = curr;
  }
};

方法二:前序遍历 + 栈

// way2: 前序遍历 + 栈
// n为二叉树节点的数量
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,栈内存开销为O(n)
var flatten = function (root) {
  if (root === null) {
    return null;
  }
  const stack = [root];
  let prev = null;
  while (stack.length > 0) {
    const node = stack.pop();
    if (prev !== null) {
      prev.left = null;
      prev.right = node;
    }
    if (node.right !== null) {
      stack.push(node.right);
    }
    if (node.left !== null) {
      stack.push(node.left);
    }
    prev = node;
  }
}

199.二叉树的右视图

方法一:广度优先遍历(BFS)

// way1: 广度优先遍历(BFS) + 存储每层最后一个元素
// n为二叉树节点的数量
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,队列内存开销为O(n)
var rightSideView = function (root) {
  const list = [];
  if (root === null) {
    return list;
  }
  const queue = [root];
  while (queue.length > 0) {
    const items = [];
    for (let i = 0, len = queue.length; i < len; i++) {
      const node = queue.shift();
      items.push(node.val);
      if (node.left !== null) {
        queue.push(node.left);
      }
      if (node.right !== null) {
        queue.push(node.right);
      }
    }
    list.push(items.pop());
  }
  return list;
};

方法二:深度优先遍历

// way2: 深度优先遍历 + 记录节点深度
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈的内存开销为O(n)
var rightSideView = function (root) {
  const result = [];
  if (root === null) {
    return result;
  }
  const dfs = (root, depth) => {
    if (root === null) {
      return;
    }
    if (depth === result.length) {
      result.push(root.val);
    }
    depth++;
    dfs(root.right, depth);
    dfs(root.left, depth);
  }
  dfs(root, 0);
  return result;
}

230.二叉搜索树中第K小的元素

方法一:中序遍历(二叉搜索树中序遍历为升序数组)

// way1: 中序遍历(二叉搜索树中序遍历为升序数组)
// 时间复杂度:O(n),需要遍历每一个节点
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈堆内存开销为O(n)
var kthSmallest = function(root, k) {
  let res;
  var inOrder = (root) => {
    if (root === null) {
      return;
    }
    inOrder(root.left);
    if (k === 0) {
      return;
    }
    if (--k === 0) {
      res = root.val;
    }
    inOrder(root.right);
  }
  inOrder(root);
  return res
};

236.二叉树的最近公共祖先

方法一:深度优先遍历

// way1: 深度优先遍历
// n为二叉树的节点数量
// 时间复杂度:O(n)
// 空间复杂度:O(n),最坏情况下为一个链表,递归调用栈的内存开销为O(n)
var lowestCommonAncestor = function (root, p, q) {
  if (root === null || root === p || root === q) {
    return root;
  }
  const left = lowestCommonAncestor(root.left, p, q);
  const right = lowestCommonAncestor(root.right, p, q);
  if (left === null && right === null) {
    return null;
  }
  if (left === null) {
    return right;
  }
  if (right === null) {
    return left;
  }
  return root;
};

437.路径总和 III

方法一:深度优先遍历(以每个节点为root,探索所有的路径和)

// way1: 深度优先遍历(以每个节点为root,探索所有的路径和)
// n为二叉树节点的数量
// 时间复杂度:O(n²),对任意节点,遍历子树O(n),求和O(n)
// 空间复杂度:O(n),递归调用栈的内存开销
var rootSum = function (root, targetSum) {
  let total = 0;
  if (root === null) {
    return 0;
  }
  if (root.val === targetSum) {
    total++;
  }
  total += rootSum(root.left, targetSum - root.val);
  total += rootSum(root.right, targetSum - root.val);
  return total;
}
var pathSum = function (root, targetSum) {
  if (root === null) {
    return 0;
  }
  let total = rootSum(root, targetSum);
  total += pathSum(root.left, targetSum);
  total += pathSum(root.right, targetSum);
  return total;
};
最后更新时间:
贡献者: wangtunan