啊哈,树

499 阅读26分钟

104. 二叉树的最大深度

题目描述

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

例子1

Input: 给定二叉树 [3,9,20,null,null,15,7],

output: 3

思考

1 比较简单,直接深度递归就可以了

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
const bfs = (root, count) => {
  if (!root) {
    return count;
  }
  let leftCount = count;
  let rightCount = count;
  if (root.left) {
    leftCount = bfs(root.left, count + 1);
  }
  if (root.right) {
    rightCount = bfs(root.right, count + 1);
  }
  return Math.max(leftCount, rightCount);
};
// Runtime: 104 ms, faster than 9.88% of JavaScript online submissions for Maximum Depth of Binary Tree.
// Memory Usage: 41.6 MB, less than 44.71% of JavaScript online submissions for Maximum Depth of Binary Tree.
export default (root) => {
  if (!root) {
    return 0;
  }
  return bfs(root, 1);
};

110. 平衡二叉树

题目描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

例子1

Input: root = [3,9,20,null,null,15,7]

output: true

思考

1 和108类似,也是求出子树的深度进行对比,不过这里如果发现子树不是平衡树,可以提前退出

参考实现1

2 简洁写法

参考实现2

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
const bfs = (root, count) => {
  if (!root) {
    return count;
  }
  let leftCount = count;
  let rightCount = count;
  if (root.left) {
    leftCount = bfs(root.left, count + 1);
  }
  if (root.right) {
    rightCount = bfs(root.right, count + 1);
  }
  return Math.max(leftCount, rightCount);
};
const balance = (root) => {
  if (!root) {
    return true;
  }
  let leftCount = 1;
  let rightCount = 1;
  if (root.left) {
    leftCount = bfs(root.left, 2);
  }
  if (root.right) {
    rightCount = bfs(root.right, 2);
  }
  return Math.abs(leftCount - rightCount) <= 1;
};
// Runtime: 148 ms, faster than 5.17% of JavaScript online submissions for Balanced Binary Tree.
// Memory Usage: 44.1 MB, less than 17.21% of JavaScript online submissions for Balanced Binary Tree.
export default (root) => {
  const stack = [root];
  while (stack.length > 0) {
    const root = stack.pop();
    if (!balance(root)) {
      return false;
    } else {
      if (root && root.left) {
        stack.push(root.left);
      }
      if (root && root.right) {
        stack.push(root.right);
      }
    }
  }

  return true;
};

实现2

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
const helper = (root) => {
  if (!root) {
    return 0;
  }
  let leftCount = 0;
  let rightCount = 0;
  if (root.left) {
    leftCount = helper(root.left);
  }
  if (root.right) {
    rightCount = helper(root.right);
  }
  if (leftCount === -1 || rightCount === -1 || Math.abs(leftCount - rightCount) > 1) {
    return -1;
  }
  return 1 + Math.max(leftCount, rightCount);
};

// Runtime: 148 ms, faster than 5.17% of JavaScript online submissions for Balanced Binary Tree.
// Memory Usage: 44.1 MB, less than 17.21% of JavaScript online submissions for Balanced Binary Tree.
export default (root) => {
  return helper(root) != -1;
};

543. 二叉树的直径

题目描述

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

例子1

Input:

          1
         / \
        2   3
       / \     
      4   5

output: 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。

思考

1 树的解法大多数是使用递归

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
const getLength = (root) => {
  if (!root.left && !root.right) {
    return 1;
  }
  let left = 0;
  if (root && root.left) {
    left = 1 + getLength(root.left);
  }
  let right = 0;
  if (root && root.right) {
    right = 1 + getLength(root.right);
  }
  return Math.max(left, right, 1);
};

const diameterOfBinaryTree1 = (root) => {
  if (!root) {
    return 0;
  }
  let count = 1;
  if (root && root.left) {
    count += getLength(root.left);
  }
  if (root && root.right) {
    count += getLength(root.right);
  }
  return Math.max(count, diameterOfBinaryTree1(root.left), diameterOfBinaryTree1(root.right));
};
// Runtime: 248 ms, faster than 5.20% of JavaScript online submissions for Diameter of Binary Tree.
// Memory Usage: 41.9 MB, less than 59.36% of JavaScript online submissions for Diameter of Binary Tree.
export default (root) => {
  const res = diameterOfBinaryTree1(root);
  return res >= 1 ? res - 1 : 0;
};

437. 路径总和 III

题目描述

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

例子1

   root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

返回 3。和等于 8 的路径有:

1.  5 -> 3
2.  5 -> 2 -> 1
3.  -3 -> 11



思考

1 题目比较简单,但是要注意一种情况就是如果从某个节点到它的子节点如果等于sum,此时还要继续向下边寻找,因为下边有可能他们的和是0

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} sum
 * @return {number}
 */
const equalSum = (root, sum) => {
  let res = 0;
  if (root && root.val === sum) {
    res += 1;
  }

  if (root && root.left) {
    res += equalSum(root.left, sum - root.val);
  }
  if (root && root.right) {
    res += equalSum(root.right, sum - root.val);
  }
  return res;
};
var pathSum = function (root, sum) {
  const stack = [];
  if (root) {
    stack.push(root);
  }
  let res = 0;
  while (stack.length > 0) {
    const temp = stack.pop();

    res += equalSum(temp, sum);

    if (temp.right) {
      stack.push(temp.right);
    }
    if (temp.left) {
      stack.push(temp.left);
    }
  }
  return res;
};
export default pathSum;

101. 对称二叉树

题目描述

给定一个二叉树,检查它是否是镜像对称的。

例子1
下面的数是对称的

    1
   / \
  2   2
 / \ / \
3  4 4  3



例子2
下面的数是对称的

    1
   / \
  2   2
   \   \
   3    3



思考

1 判断一个数是否是对称,可以根据节点进行对比,如果是叶子节点肯定是对称的。

这里的关键是如何选择节点进行对比?

也就是如果在一棵树中找到相应的节点进行对比,相通这里之后,解法就很简单了

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
const isSymmetric1 = (left, right) => {
  if (!left && !right) {
    return true;
  }
  if (!left || !right) {
    return false;
  }
  if (left.val != right.val) {
    return false;
  }
  return isSymmetric1(left.left, right.right) && isSymmetric1(left.right, right.left);
};
// Runtime: 92 ms, faster than 58.10% of JavaScript online submissions for Symmetric Tree.
// Memory Usage: 40.5 MB, less than 66.28% of JavaScript online submissions for Symmetric Tree.
export default (root) => {
  return root ? isSymmetric1(root.left, root.right) : true;
};

1110. 删点成林

题目描述

给出二叉树的根节点 root,树上每个节点都有一个不同的值。

如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。

返回森林中的每棵树。你可以按任意顺序组织答案。


例子1
c4f41f077c8625908ce794090050663d.png


思考

1 数的类型基本上都可以使用递归

1.1 这里的难点是如何确定当前遍历的节点是否可以加入到结果数组中?

当我们遍历到一个节点的时候,根据什么确定应该加入到结果数组中呢?

1.2 第二点需要注意的是我们在遍历树的时候,如果删除了,此时需要更新被删除节点的父元素的指向?

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number[]} to_delete
 * @return {TreeNode[]}
 */
const deleteVal = (root, is_root, to_delete, res) => {
  if (!root) return null;
  const deleted = to_delete.includes(root.val);
  if (is_root && !deleted) {
    res.push(root);
  }
  root.left = helper(root.left, deleted, to_delete, res);
  root.right = helper(root.right, deleted, to_delete, res);
  return deleted ? null : root;
};
// Runtime: 120 ms, faster than 32.97% of JavaScript online submissions for Delete Nodes And Return Forest.
// Memory Usage: 47.8 MB, less than 5.41% of JavaScript online submissions for Delete Nodes And Return Forest.
export default (root, to_delete) => {
  const res = [];
  helper(root, true, to_delete, res);
  return res;
};

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

题目描述

根据一棵树的前序遍历与中序遍历构造二叉树。

你可以假设树中没有重复的元素。

例子1
510cb83ea180379e22bf8819786e3e26.png


思考

1 这个很好解决,一种耗时多

参考实现1
2 利用下标,防止重复复制元素

参考实现2

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
preorder = [3, 9, 20, 15, 7];
inorder = [9, 3, 15, 20, 7];
function TreeNode(val, left, right) {
  this.val = val === undefined ? 0 : val;
  this.left = left === undefined ? null : left;
  this.right = right === undefined ? null : right;
}
// Runtime: 4776 ms, faster than 5.07% of JavaScript online submissions for Construct Binary Tree from Preorder and Inorder Traversal.
// Memory Usage: 122.3 MB, less than 5.27% of JavaScript online submissions for Construct Binary Tree from Preorder and Inorder Traversal.
const buildTree = (preorder, inorder) => {
  if (preorder.length > 0) {
    const rootVal = preorder.shift();
    const inIndex = inorder.indexOf(rootVal);
    const leftInorder = inorder.slice(0, inIndex);
    const rightInorder = inorder.slice(inIndex);
    const root = new TreeNode(rootVal);

    const leftPreorder = [];
    const rightPreorder = [];
    for (let i = 0; i < preorder.length; i++) {
      if (leftInorder.includes(preorder[i]) && leftPreorder.length < leftInorder.length) {
        leftPreorder.push(preorder[i]);
      } else {
        rightPreorder.push(preorder[i]);
      }
    }
    root.left = buildTree(leftPreorder, leftInorder);
    root.right = buildTree(rightPreorder, rightInorder);
    return root;
  }
  return null;
};
export default buildTree;

实现2

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
function TreeNode(val, left, right) {
  this.val = val === undefined ? 0 : val;
  this.left = left === undefined ? null : left;
  this.right = right === undefined ? null : right;
}
// Runtime: 96 ms, faster than 87.67% of JavaScript online submissions for Construct Binary Tree from Preorder and Inorder Traversal.
// Memory Usage: 41.7 MB, less than 93.64% of JavaScript online submissions for Construct Binary Tree from Preorder and Inorder Traversal.
const helper = (preStart, inStart, inEnd, preorder, inorder) => {
  if (preStart > preorder.length - 1 || inStart > inEnd) {
    return null;
  }
  const rootVal = preorder[preStart];
  const root = new TreeNode(rootVal);
  const index = inorder.indexOf(rootVal);
  // index - inStart + 1;
  root.left = helper(preStart + 1, inStart, index - 1, preorder, inorder);
  root.right = helper(preStart + index - inStart + 1, index + 1, inEnd, preorder, inorder);
  return root;
};
const buildTree = (preorder, inorder) => {
  return helper(0, 0, inorder.length - 1, preorder, inorder);
};
export default buildTree;

144. 二叉树的前序遍历

题目描述

根据一棵树的前序遍历与中序遍历构造二叉树。

例子1
输入:root = [1,null,2,3]
输出:[1,2,3]

例子2
输入:root = []
输出:[]

思考

1 这个很好解决,一种是使用递归

参考实现1
2 如果不使用递归,可以使用栈正序遍历,也就是先遍历左子树,一直到底,然后再右子树。

2.1 这里需要考虑下什么时候退栈,什么时候入栈,什么时候该退出?

参考实现2

3 还有一种也是使用栈,但是本质上和递归有点类似,利用栈的先入后出的特性

参考实现3

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
// Runtime: 80 ms, faster than 57.41% of JavaScript online submissions for Binary Tree Preorder Traversal.
// Memory Usage: 38.9 MB, less than 11.26% of JavaScript online submissions for Binary Tree Preorder Traversal.
const travel = (root, res) => {
  if (!root) {
    return;
  }

  res.push(root.val);
  travel(root.left, res);
  travel(root.right, res);
};
export default (root) => {
  const res = [];
  travel(root, res);
  return res;
};

实现2

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
// Runtime: 80 ms, faster than 57.41% of JavaScript online submissions for Binary Tree Preorder Traversal.
// Memory Usage: 38.8 MB, less than 41.85% of JavaScript online submissions for Binary Tree Preorder Traversal.
export default (root) => {
  let res = [];
  let stack = [];
  if (root) {
    stack.push({
      node: root,
      hasVisitedLeft: false,
      hasVisitedRight: false,
    });
  } else {
    return res;
  }

  while (stack.length > 0) {
    const temp = stack[stack.length - 1];
    if (!temp.hasVisitedLeft) {
      res.push(temp.node.val);
    }

    if (temp.node.left && !temp.hasVisitedLeft) {
      temp.hasVisitedLeft = true;
      stack.push({
        node: temp.node.left,
        hasVisitedLeft: false,
        hasVisitedRight: false,
      });
    } else if (temp.node.right && temp.hasVisitedLeft && !temp.hasVisitedRight) {
      temp.hasVisitedRight = true;
      stack.push({
        node: temp.node.right,
        hasVisitedLeft: false,
        hasVisitedRight: false,
      });
    } else if (!temp.node.left && !temp.hasVisitedLeft) {
      temp.hasVisitedLeft = true;
    } else if (!temp.node.right && !temp.hasVisitedRight) {
      temp.hasVisitedRight = true;
    } else {
      stack.pop();
    }
  }
  return res;
};

实现3

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
// Runtime: 80 ms, faster than 57.41% of JavaScript online submissions for Binary Tree Preorder Traversal.
// Memory Usage: 38.8 MB, less than 41.85% of JavaScript online submissions for Binary Tree Preorder Traversal.
export default (root) => {
  let res = [];
  let stack = [];
  if (root) {
    stack.push(root);
  } else {
    return res;
  }

  while (stack.length > 0) {
    const temp = stack.pop();
    res.push(temp.val);
    if (temp.right) {
      stack.push(temp.right);
    }
    if (temp.left) {
      stack.push(temp.left);
    }
  }
  return res;
};

实现一颗二叉查找树

题目描述

实现一颗二叉查找树

思考

1 使用递归解决

参考实现1

实现1

class Node {
  constructor(val, left, right) {
    this.val = val === undefined ? 0 : val;
    this.left = left === undefined ? null : left;
    this.right = right === undefined ? null : right;
  }
}
class BinarySearchTree {
  constructor(val) {
    this.root = new Node(val);
  }
  insert(val) {
    this.insertNode(this.root, new CreateNode(val));
  }
  insertNode(node, newNode) {
    // 如果节点大于新节点的值
    if (node.val > newNode.val) {
      if (node.left) {
        this.insertNode(node.left, newNode);
      } else {
        node.left = newNode;
      }
    } else if (node.val < newNode.val) {
      if (node.right) {
        this.insertNode(node.right, newNode);
      } else {
        node.right = newNode;
      }
    }
  }
  // 中序遍历
  inOrderTraverce() {
    const res = [];
    this.inOrderTraverceNodes(this.root, res);
    return res;
  }
  inOrderTraverceNodes(node, res) {
    if (node) {
      this.inOrderTraverceNodes(node.left, res);
      res.push(node.val);
      this.inOrderTraverceNodes(node.right, res);
    }
  }
  // 前序排列
  preOrderTraverce() {
    const res = [];
    this.preOrderTraverceNodes(this.root, res);
    return res;
  }
  preOrderTraverceNodes(node, res) {
    if (node) {
      res.push(node.val);
      this.preOrderTraverceNodes(node.left, res);
      this.preOrderTraverceNodes(node.right, res);
    }
  }

  //后序遍历
  afterOrderTraverce() {
    const res = [];
    this.afterOrderTraverceNodes(this.root, res);
    return res;
  }
  afterOrderTraverceNodes(node, res) {
    if (node) {
      this.afterOrderTraverceNodes(node.left, res);
      this.afterOrderTraverceNodes(node.right, res);
      res.push(node.val);
    }
  }

  //层次遍历
  levelOrderTraverce() {
    const res = [];
    const quene = [this.root];
    let node = null;
    while (quene.length > 0) {
      node = quene.shift();
      res.push(node.val);
      if (node && node.left) {
        quene.push(node.left);
      }
      if (node && node.right) {
        quene.push(node.right);
      }
    }
    return res;
  }

  //查询最小值:找到最左边的节点
  findMin() {
    return this.min(this.root);
  }
  min(node) {
    if (node) {
      while (node.left) {
        node = node.left;
      }
      return node.val;
    } else {
      return null;
    }
  }

  //查询最大值:找到最左边的节点
  findMax() {
    return this.max(this.root);
  }
  max(node) {
    if (node) {
      while (node.right) {
        node = node.right;
      }
      return node.val;
    } else {
      return null;
    }
  }

  //查找特定值
  find(val) {
    return this.findVal(this.root, val);
  }
  findVal(node, val) {
    if (!node) {
      return false;
    }
    if (node.val < val) {
      return this.findVal(node.right, val);
    } else if (node.val > val) {
      return this.findVal(node.left, val);
    } else {
      return true;
    }
  }

  remove(val) {
    this.removeNode(this.root, val);
  }
  // 找到最小值的节点
  findMinNode(node) {
    if (node) {
      while (node.left) {
        node = node.left;
      }
      return node;
    } else {
      return null;
    }
  }
  // 删除节点的方法
  removeNode(node, val) {
    if (!node) {
      return null;
    }
    if (node.val > val) {
      node.left = this.removeNode(node.left, val);
      return node;
    } else if (node.val < val) {
      node.right = this.removeNode(node.right, val);
      return node;
    } else {
      // 找到需要删除的节点后
      // 第一种,如果是叶子节点,那么可以直接删除
      if (!node.left && !node.right) {
        node = null;
        return null;
        // 如果有左节点,没有右节点,直接把node删除,然后
      } else if (node.left && !node.right) {
        node = node.left;
        return node;
        // 如果有右节点,没有左节点,
      } else if (!node.left && node.right) {
        //删除只有右节点的节点 => 将右节点替换需删除节点
        node = node.right;
        return node;
        // 同时存在左右节点
      } else {
        // 在右子树中找到最小值节点
        const minNode = this.findMinNode(node.right);
        // 最小值节点的值 赋给 被删除点的值,即完成替换
        node.val = minNode.val;
        // 替换后删除最小值节点
        node.right = this.removeNode(node.right, minNode.val);
        return node;
      }
    }
  }
}

99. 恢复二叉搜索树

题目描述

给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。

进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗?

例子1
输入:root = [1,3,null,null,2]
输出:[3,1,null,null,2]
解释:3 不能是 1 左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。

例子2
输入:root = [3,1,4,null,null,2]
输出:[2,1,4,null,null,3]
解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。

思考

1 中序遍历二叉查找树的时候,可以按照从小到打排列,但是如果想要使用O(1)的空间复杂度的时候来解决此问题,直接看答案就可以

如果想要使用O(1),可以查找下莫里斯查找算法,该算法实现了使用常量空间

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {void} Do not return anything, modify root in-place instead.
 */
// Runtime: 396 ms, faster than 5.73% of JavaScript online submissions for Recover Binary Search Tree.
// Memory Usage: 53.3 MB, less than 6.37% of JavaScript online submissions for Recover Binary Search Tree.
export default (root) => {
  if (!root) {
    return;
  }
  // 错误的节点
  const errorNodes = [];
  let current = root;
  let preNode;
  // 当前遍历到节点的前一个节点
  let preCurrentNode;
  let first;
  let second;

  while (current) {
    if (!current.left) {
      if (preCurrentNode && preCurrentNode.val > current.val) {
        if (!first) {
          first = preCurrentNode;
        }
        second = current;
      }
      preCurrentNode = current;
      current = current.right;
    } else {
      preNode = current.left;
      while (preNode.right && preNode.right.val !== current.val) {
        preNode = preNode.right;
      }
      if (!preNode.right) {
        preNode.right = current;
        current = current.left;
      } else {
        if (preCurrentNode && preCurrentNode.val > current.val) {
          if (!first) {
            first = preCurrentNode;
          }
          second = current;
        }
        preNode.right = null;
        preCurrentNode = current;
        current = current.right;
      }
    }
  }

  // 交换
  const temp = first.val;
  first.val = second.val;
  second.val = temp;
};

669. 修剪二叉搜索树

题目描述

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构(即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

例子1
输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]
解释:

例子2
输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]
解释:

思考

1 很明显使用递归解决就可以了,比较简单

参考实现1

2 递归的时候,返回节点

参考实现2

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
function TreeNode(val, left, right) {
  this.val = val === undefined ? 0 : val;
  this.left = left === undefined ? null : left;
  this.right = right === undefined ? null : right;
}
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {TreeNode}
 */
const inorder = (parent, root, low, high, dir) => {
  if (root && root.val < low) {
    if (dir === "left") {
      parent.left = root.right;
      root.right = null;
      inorder(parent, parent.left, low, high, "left");
    } else {
      parent.right = root.right;
      root.right = null;
      inorder(parent, parent.right, low, high, "right");
    }
  } else if (root && root.val > high) {
    if (dir === "left") {
      parent.left = root.left;
      root.right = null;
      inorder(parent, parent.left, low, high, "left");
    } else {
      parent.right = root.left;
      root.right = null;
      inorder(parent, parent.right, low, high, "right");
    }
  } else {
    if (root && root.left) {
      inorder(root, root.left, low, high, "left");
    }
    if (root && root.right) {
      inorder(root, root.right, low, high, "right");
    }
  }
};

// Runtime: 116 ms, faster than 34.98% of JavaScript online submissions for Trim a Binary Search Tree.
// Memory Usage: 44 MB, less than 91.36% of JavaScript online submissions for Trim a Binary Search Tree.
export default (root, low, high) => {
  const parent = new TreeNode();
  parent.left = root;
  inorder(parent, root, low, high, "left");
  return parent.left;
};

实现2

// Runtime: 96 ms, faster than 69.97% of JavaScript online submissions for Trim a Binary Search Tree.
// Memory Usage: 44.5 MB, less than 44.36% of JavaScript online submissions for Trim a Binary Search Tree.
const trimBST = (root, low, high) => {
  if (!root) return null;

  if (root.val < low) return trimBST(root.right, low, high);
  if (root.val > high) return trimBST(root.left, low, high);
  root.left = trimBST(root.left, low, high);
  root.right = trimBST(root.right, low, high);

  return root;
};
export default trimBST;

208. 实现 Trie (前缀树)

题目描述

实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

例子1

Trie trie = new Trie();

trie.insert("apple");
trie.search("apple");   // 返回 true
trie.search("app");     // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");   
trie.search("app");     // 返回 true

说明:

你可以假设所有的输入都是由小写字母 a-z 构成的。
保证所有输入均为非空字符串。

思考

1 就是一颗多叉树,很好实现

参考实现1

实现1

var TrirNode = function (ch) {
  this.ch = ch || -1;
  this.children = new Map();
  this.isEnd = false;
};

/**
 * Initialize your data structure here.
 */

// Runtime: 320 ms, faster than 13.70% of JavaScript online submissions for Implement Trie (Prefix Tree).
// Memory Usage: 69.2 MB, less than 5.20% of JavaScript online submissions for Implement Trie (Prefix Tree).
var Trie = function () {
  this.root = new TrirNode();
};

/**
 * Inserts a word into the trie.
 * @param {string} word
 * @return {void}
 */
Trie.prototype.insert = function (word) {
  let currentRoot = this.root;
  for (let ch of word) {
    let node = currentRoot.children.get(ch);

    if (!node) {
      node = new TrirNode(ch);
      currentRoot.children.set(ch, node);
    }
    currentRoot = node;
  }
  currentRoot.isEnd = true;
};

/**
 * Returns if the word is in the trie.
 * @param {string} word
 * @return {boolean}
 */
Trie.prototype.search = function (word) {
  let currentRoot = this.root;
  for (let ch of word) {
    if (currentRoot.children.get(ch)) {
      currentRoot = currentRoot.children.get(ch);
    } else {
      return false;
    }
  }
  return currentRoot.isEnd === true;
};

/**
 * Returns if there is any word in the trie that starts with the given prefix.
 * @param {string} prefix
 * @return {boolean}
 */
Trie.prototype.startsWith = function (prefix) {
  let currentRoot = this.root;
  for (let ch of prefix) {
    if (currentRoot.children.get(ch)) {
      currentRoot = currentRoot.children.get(ch);
    } else {
      return false;
    }
  }
  return true;
};

/**
 * Your Trie object will be instantiated and called as such:
 * var obj = new Trie()
 * obj.insert(word)
 * var param_2 = obj.search(word)
 * var param_3 = obj.startsWith(prefix)
 */

226. 翻转二叉树

题目描述

翻转一棵二叉树。

例子1

     4
   /   \
  2     7
 / \   / \
1   3 6   9

反转后

     4
   /   \
  7     2
 / \   / \
9   6 3   1

思考

1 刚开始想只是交换两个节点的值,但是发现在有一侧为null的情况下有问题,比如[1,2]的时候,所以直接交换节点

参考实现1

实现1

/**
 * Definition for a binary tree node.

 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */

const invert1 = (root, leftRoot, rightRoot) => {
  if (!root) {
    return;
  }
  root.right = leftRoot;
  root.left = rightRoot;
  if (rightRoot) {
    invert1(rightRoot, rightRoot.left, rightRoot.right);
  }
  if (leftRoot) {
    invert1(leftRoot, leftRoot.left, leftRoot.right);
  }
};
// Runtime: 80 ms, faster than 62.85% of JavaScript online submissions for Invert Binary Tree.
// Memory Usage: 39.2 MB, less than 8.63% of JavaScript online submissions for Invert Binary Tree.
export default (root) => {
  if (!root) return null;
  invert1(root, root.left, root.right);
  return root;
};

617. 合并二叉树

题目描述

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

例子1

   Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7    



合并后

         3
	    / \
	   4   5
	  / \   \ 
	 5   4   7

思考

1 递归解决就可以了

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} t1
 * @param {TreeNode} t2
 * @return {TreeNode}
 */

// Runtime: 120 ms, faster than 48.51% of JavaScript online submissions for Merge Two Binary Trees.
// Memory Usage: 46.5 MB, less than 22.07% of JavaScript online submissions for Merge Two Binary Trees.
const merge = (left, right) => {
  if (!left) {
    return right;
  } else if (!right) {
    return left;
  } else {
    left.val += right.val;
    left.left = merge(left.left, right.left);
    left.right = merge(left.right, right.right);
    return left;
  }
};
export default (t1, t2) => {
  if (!t1) return t2;
  if (!t2) return t1;
  return merge(t1, t2);
};

572. 另一个树的子树

题目描述

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

例子1

   Tree s                                     
     3
    / \
   4   5
  / \
 1   2  
 
 Tree t
 
    4 
  / \
 1   2
  


返回true

例子2

   Tree s                                     
     3
    / \
   4   5
  / \
 1   2
    /
   0
 Tree t
 
    4 
  / \
 1   2
  


返回false

思考

1 递归解决就可以了

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} s
 * @param {TreeNode} t
 * @return {boolean}
 */
const isEqual = (s, t) => {
  if (!s && !t) {
    return true;
  }
  if (s && !t) {
    return false;
  }
  if (!s && t) {
    return false;
  }
  if (s.val != t.val) return false;
  return isEqual(s.left, t.left) && isEqual(s.right, t.right);
};
// Runtime: 108 ms, faster than 60.11% of JavaScript online submissions for Subtree of Another Tree.
// Memory Usage: 46 MB, less than 5.88% of JavaScript online submissions for Subtree of Another Tree.
const isSubtree = (s, t) => {
  if (!s) return false;
  if (isEqual(s, t)) return true;

  return isSubtree(s.left, t) || isSubtree(s.right, t);
};

export default

404. 左叶子之和

题目描述

计算给定二叉树的所有左叶子之和。

例子1

     3
   / \
  9  20
    /  \
   15   7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

思考

1 这里的难点是如何确定是左叶子节点

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
const getLeftLeaves = (root, dir) => {
  if (!root) {
    return 0;
  }
  let sum = 0;
  if (root && !root.left && !root.right && dir === "left") {
    return root.val;
  } else {
    if (root && root.left) {
      sum += getLeftLeaves(root.left, "left");
    }
    if (root && root.right) {
      sum += getLeftLeaves(root.right, "right");
    }
  }
  return sum;
};
// Runtime: 76 ms, faster than 97.22% of JavaScript online submissions for Sum of Left Leaves.
// Memory Usage: 39.9 MB, less than 88.86% of JavaScript online submissions for Sum of Left Leaves.
const sumOfLeftLeaves = (root) => {
  if (!root) return 0;
  if (root && !root.left && !root.right) return 0;
  return getLeftLeaves(root, "left");
};
export default sumOfLeftLeaves;

513. 找树左下角的值

题目描述

给定一个二叉树,在树的最后一行找到最左边的值。

例子1

   输入:

    2
   / \
  1   3

输出:
1

例子2

   输入:

        1
       / \
      2   3
     /   / \
    4   5   6
       /
      7

输出:
7


提示:

注意: 您可以假设树(即给定的根节点)不为 NULL。

思考

1 树的解法无非就是三种递归,深度遍历,广度遍历,中序遍历等的变种

这里使用广度遍历

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
export default (root) => {
  const levels = [];
  let stack = [root];
  let level = 0;
  while (stack.length) {
    levels.push([]);
    const newStack = [];
    while (stack.length) {
      const curr = stack.shift();
      levels[level].push(curr);
      if (curr.left) newStack.push(curr.left);
      if (curr.right) newStack.push(curr.right);
    }

    stack = newStack;
    level++;
  }
  return levels[levels.length - 1][0].val;
};

538. 把二叉搜索树转换为累加树

题目描述

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。


例子1
890327555f22703bbcd0ff3bbdcfeb2b.png

  输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]


例子2

  输入:root = [0,null,1]
输出:[1,null,1]

提示:

树中的节点数介于 0 和 104 之间。
每个节点的值介于 -104 和 104 之间。
树中的所有值 互不相同 。
给定的树为二叉搜索树。

思考

1 树的解法无非就是三种递归,深度遍历,广度遍历,中序遍历等的变种

这里使用先遍历右边,再中间,再左边的方法

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
const convert = (cur, sum) => {
  if (!cur) return sum;
  sum = convert(cur.right, sum);
  cur.val += sum;
  sum = cur.val;
  sum = convert(cur.left, sum);
  return sum;
};
// Runtime: 104 ms, faster than 95.00% of JavaScript online submissions for Convert BST to Greater Tree.
// Memory Usage: 47.4 MB, less than 85.00% of JavaScript online submissions for Convert BST to Greater Tree.
export default (root) => {
  convert(root, 0);
  return root;
};

235. 二叉搜索树的最近公共祖先

题目描述

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例子1
764fdec670db06a85206ee9f33a3efa4.png

 输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6

例子2

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。


提示:

所有节点的值都是唯一的。

p、q 为不同节点且均存在于给定的二叉搜索树中。

思考

1 直接使用二叉搜索的性质解决就可以

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */

/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
// Runtime: 96 ms, faster than 88.11% of JavaScript online submissions for Lowest Common Ancestor of a Binary Search Tree.
// Memory Usage: 48.6 MB, less than 70.92% of JavaScript online submissions for Lowest Common Ancestor of a Binary Search Tree.
const lowestCommonAncestor = (root, p, q) => {
  if (!root) return null;
  if (p > q) {
    const temp = p;
    p = q;
    q = temp;
  }
  if (root.val === p.val || root.val === q.val) {
    return root;
  }

  if (root.val < p.val) {
    if (root && root.left) {
      return lowestCommonAncestor(root.left, p, q);
    } else {
      return null;
    }
  } else if (root.val > q.val) {
    if (root && root.right) {
      return lowestCommonAncestor(root.right, p, q);
    } else {
      return;
    }
  } else {
    return root;
  }
};
export default lowestCommonAncestor;

530. 二叉搜索树的最小绝对差

题目描述

给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。

例子1

输入:

   1
    \
     3
    /
   2

输出:
1

解释:
最小绝对差为 1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。

提示:

树中至少有 2 个节点。

思考

1 直接使用二叉搜索的性质解决就可以

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
const inorder = (root, res) => {
  if (!root) return res;
  inorder(root.left, res);
  res.push(root.val);
  inorder(root.right, res);
};
// Runtime: 100 ms, faster than 51.72% of JavaScript online submissions for Minimum Absolute Difference in BST.
// Memory Usage: 44.2 MB, less than 77.59% of JavaScript online submissions for Minimum Absolute Difference in BST.
export default (root) => {
  const res = [];
  inorder(root, res);
  let min = -1;
  for (let i = 1; i < res.length; i++) {
    if (min < 0) {
      min = res[i] - res[i - 1];
    } else {
      min = Math.min(res[i] - res[i - 1], min);
    }
  }
  return min;
};

889. 根据前序和后序遍历构造二叉树

题目描述

返回与给定的前序和后序遍历匹配的任何二叉树。

pre 和 post 遍历中的值是不同的正整数。

例子1

输入:pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]

提示:

1 <= pre.length == post.length <= 30
pre[] 和 post[] 都是 1, 2, ..., pre.length 的排列 每个输入保证至少有一个答案。如果有多个答案,可以返回其中一个。

思考

1 直接按照前序和后序性质就可以

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} pre
 * @param {number[]} post
 * @return {TreeNode}
 */
// Runtime: 100 ms, faster than 58.06% of JavaScript online submissions for Construct Binary Tree from Preorder and Postorder Traversal.
// Memory Usage: 42.8 MB, less than 8.60% of JavaScript online submissions for Construct Binary Tree from Preorder and Postorder Traversal.
const constructFromPrePost = (pre, post) => {
  if (pre.length > 0) {
    const root = new TreeNode(pre.shift());
    post.pop();
    const rootVal = post[post.length - 1];
    const index = pre.indexOf(rootVal);
    const pre1 = pre.slice(0, index);
    const pre2 = pre.slice(index);
    const index1 = post.indexOf(pre1[0]);
    const post1 = post.slice(0, index);
    const post2 = post.slice(index);
    console.log(pre1, post1);
    console.log(pre2, post2);
    root.left = constructFromPrePost(pre1, post1);
    root.right = constructFromPrePost(pre2, post2);
    return root;
  } else {
    return null;
  }
};
export default constructFromPrePost;

94. 二叉树的中序遍历

题目描述

给定一个二叉树的根节点 root ,返回它的 中序 遍历。

例子1

输入:root = [1,null,2,3]<br/>
输出:[1,3,2]

例子2

输入:root = []
输出:[]

提示:

1 树中节点数目在范围 [0, 100] 内
2 -100 <= Node.val <= 100

思考

1 这里的主要难点思考下如何什么时候需要入栈,什么时候出栈

参考实现1

相似题目145

实现1

if (!root) return [];
  const stack = [root];
  const res = [];
  let visited = false;
  while (stack.length>0) {
    let pre = stack[stack.length - 1];
    while (pre && !visited) {
      pre = pre.left;
      if (pre) {
        stack.push(pre);
      }
    }
    const node = stack.pop();
    res.push(node.val);
    if (node.right) {
      stack.push(node.right);
      visited = false;
    } else {
      visited = true;
    }
  }
  return res;

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

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例子1

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3

例子2

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

提示:

1 树中节点数目在范围 [2, 105] 内。
2 -10^9 <= Node.val <= 10^9
3 所有 Node.val 互不相同 。
4 p != q
5 p 和 q 均存在于给定的二叉树中。

思考

1 树一般都是通过递归,这里可以思考一下,如果p或者 q存在于左子树,如果p和q存在于右子树,怎么处理?

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
// Runtime: 92 ms, faster than 91.34% of JavaScript online submissions for Lowest Common Ancestor of a Binary Tree.
// Memory Usage: 48.2 MB, less than 27.33% of JavaScript online submissions for Lowest Common Ancestor of a Binary Tree.
export default (root, p, q) => {
  if (!root || root === p || root === q) return root;
  const left = lowestCommonAncestor(root.left, p, q);
  const right = lowestCommonAncestor(root.right, p, q);
  // 如果p或者q存在于left
  if (left) {
    // 如果p或者q存在于right
    if (right) {
      // 此时最近的肯定是root
      return root;
    } else {
      // 如果不存在于右边,那说明p或者q都存在于left
      return left;
    }
  } else {
    // 如果right存在,说明p和q都存在于right
    if (right) {
      return right;
    } else {
      // 如果不存在于左子树也不存在右子树
      return null;
    }
  }
};

109. 有序链表转换二叉搜索树

题目描述

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

例子1

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

思考

1 很简单,就是不断的利用快速指针找出中间的节点就可以了

参考实现1

实现1

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {ListNode} head
 * @return {TreeNode}
 */
function TreeNode(val, left, right) {
  this.val = val === undefined ? 0 : val;
  this.left = left === undefined ? null : left;
  this.right = right === undefined ? null : right;
}
const toBST = (head, end) => {
  let low = head;
  let high = head;
  if (head === end) return null;
  while (high !== end && high.next !== end) {
    low = low.next;
    high = high.next.next;
  }
  let root = new TreeNode(low.val);
  root.left = toBST(head, low);
  root.right = toBST(low.next, end);
  return root;
};
export default (head) => {
  if (!head) return head;
  return toBST(head, null);
};

897. 递增顺序查找树

题目描述

给你一个树,请你 按中序遍历 重新排列树,使树中最左边的结点现在是树的根,并且每个结点没有左子结点,只有一个右子结点。

例子1

输入:[5,3,6,2,4,null,8,1,null,null,null,7,9]

       5
      / \
    3    6
   / \    \
  2   4    8
 /        / \ 
1        7   9

输出:[1,null,2,null,3,null,4,null,5,null,6,null,7,null,8,null,9]

 1
  \
   2
    \
     3
      \
       4
        \
         5
          \
           6
            \
             7
              \
               8
                \
                 9  


思考

1 中序遍历就可以

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
//  Runtime: 84 ms, faster than 38.73% of JavaScript online submissions for Increasing Order Search Tree.
//  Memory Usage: 39 MB, less than 69.47% of JavaScript online submissions for Increasing Order Search Tree.
export default (root) => {
  if (!root) return root;
  let cur = root;
  const stack = [];
  let rootNode;
  let curNode;
  while (cur || stack.length) {
    while (cur) {
      stack.push(cur);
      cur = cur.left;
    }
    const node = stack.pop();
    if (!rootNode) {
      rootNode = new TreeNode(node.va);
      curNode = rootNode;
    } else {
      curNode.left = null;
      curNode.right = new TreeNode(node.va);
      curNode = curNode.right;
    }
    cur = node.right;
  }
  return rootNode;
};

50. 删除二叉搜索树中的节点

题目描述

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。

说明: 要求算法时间复杂度为 O(h),h 为树的高度。

例子1

root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7

给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。

一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。

    5
   / \
  4   6
 /     \
2       7

另一个正确答案是 [5,2,6,null,4,null,7]。

    5
   / \
  2   6
   \   \
    4   7


思考

1 递归就可以,不过需要需要叶子节点和有一颗左子树和有一颗右子树的时候或者同时有两颗子树的情况

参考实现1

实现1

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} key
 * @return {TreeNode}
 */
const findMin = (root) => {
  while (root.left) {
    root = root.left;
  }
  return root;
};
// Runtime: 112 ms, faster than 75.00% of JavaScript online submissions for Delete Node in a BST.
// Memory Usage: 47.2 MB, less than 65.49% of JavaScript online submissions for Delete Node in a BST.
const deleteNode = (root, key) => {
  if (!root) return root;
  if (key < root.val) {
    root.left = deleteNode(root.left, key);
  } else if (key > root.val) {
    root.right = deleteNode(root.right, key);
  } else {
    // 只有一个右节点
    if (!root.left) {
      return root.right;
      // 只有一个左节点
    } else if (!root.right) {
      return root.left;
    }
    // 同时有两个节点
    const minNode = findMin(root.right);
    root.val = minNode.val;
    root.right = deleteNode(root.right, root.val);
  }
  return root;
};

export default deleteNode;