遍历

前/中/后遍历

递归版本

  1. //中序
  2. var inorderTraversal = function(root) {
  3. let res = [];
  4. let inorder = (root) => {
  5. if (!root) return;
  6. inorder(root.left);
  7. res.push(root.val);//前中遍历仅需改变这行代码的顺序
  8. inorder(root.right);
  9. }
  10. inorder(root);
  11. return res;
  12. };

迭代版本

可以先写中序遍历,然后前中遍历仅需改变两行代码的顺序

  1. //中序遍历
  2. var inorderTraversal = function(root) {
  3. //左中右 在栈中右左中
  4. let queue = [];
  5. let result= []
  6. if (root) {
  7. queue.push(root);
  8. }
  9. while (queue.length>0) {
  10. const node = queue.pop();
  11. if (!node) {
  12. const value = queue.pop();
  13. result.push(value.val)
  14. } else {
  15. if (node.right) {
  16. queue.push(node.right);
  17. }
  18. queue.push(node);//
  19. queue.push(null);//前中遍历仅需改变这两行代码的顺序
  20. if (node.left) {
  21. queue.push(node.left);
  22. }
  23. }
  24. }
  25. return result;
  26. };

层序遍历

层序遍历就是广度优先搜索遍历
使用层序遍历可以解决

  • 111.二叉树的最小深度

    1. //层序遍历
    2. var levelOrder = function(root) {
    3. if(!root) return []
    4. let result=[];
    5. let queue=[root];
    6. while(queue.length>0){
    7. const length=queue.length;
    8. let nextLevel=[]
    9. for(let i =0;i<length;i++){
    10. const current=queue.shift();
    11. nextLevel.push(current.val)
    12. if(current.left) {
    13. queue.push(current.left);
    14. }
    15. if(current.right){
    16. queue.push(current.right);
    17. }
    18. }
    19. result.push(nextLevel)
    20. }
    21. return result;
    22. };

    翻转二叉树

    1. //自己写的
    2. //前序遍历
    3. var invertTree = function(root) {
    4. if(!root) return root;
    5. const left=root.left;
    6. root.left=root.right;
    7. root.right=left;
    8. invertTree(root.left)
    9. invertTree(root.right)
    10. return root;
    11. };
    1. //优化
    2. var invertTree = function(root) {
    3. if (!root) return null
    4. const tmp = root.left
    5. root.left = invertTree(root.right)
    6. root.right = invertTree(tmp)
    7. return root
    8. };

    平衡二叉树

    树的高度和深度的区别
    后序遍历

    1. var isBalanced = function(root) {
    2. function getTreeHeight(root){
    3. //后序遍历,从叶子节点开始计算高度,得到左右节点的高度差
    4. if(!root) return 0;
    5. const leftHeight=getTreeHeight(root.left);
    6. if(leftHeight===-1) return -1;
    7. const rightHeight=getTreeHeight(root.right);
    8. if(rightHeight===-1) return -1;
    9. if(Math.abs(leftHeight-rightHeight)>1) return -1;
    10. else return 1+Math.max(leftHeight,rightHeight);
    11. }
    12. return !(getTreeHeight(root) === -1);
    13. };

    二叉树的直径

    后序遍历

    1. var diameterOfBinaryTree = function(root) {
    2. let res=0;
    3. dfs(root);
    4. function dfs(node){
    5. if(!node) return 0;
    6. let left=dfs(node.left);
    7. let right=dfs(node.right);
    8. res=Math.max(res,left+right);
    9. return Math.max(left,right)+1;
    10. }
    11. return res;
    12. };

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

    用后序遍历 先遍历到两个子节点 再遍历到中间节点

    1. var lowestCommonAncestor = function(root, p, q) {
    2. if(root===p || root===q || root===null) return root; //节点唯一,所以不会有重复q/p
    3. const left = lowestCommonAncestor(root.left,p,q)
    4. const right = lowestCommonAncestor(root.right,p,q)
    5. if(left && right) return root;
    6. if(!left && right) return right;
    7. if(left && !right) return left;
    8. return null;
    9. };

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

    image.png

    1. var lowestCommonAncestor = function(root, p, q) {
    2. //二叉搜索树,公共祖先在[p,q]之间
    3. if(root.val>p.val && root.val >q.val){
    4. return lowestCommonAncestor(root.left,p,q)
    5. }else if(root.val<p.val && root.val <q.val){
    6. return lowestCommonAncestor(root.right,p,q)
    7. }else return root;
    8. };

    回溯

    二叉树的所有路径

    ```javascript

var binaryTreePaths = function(root) { let result=[]; let path=[] function backtracking(root){ path.push(root.val); if(!root.left && !root.right){ result.push(path.join(‘->’)) return; } if(root.left){ backtracking(root.left) path.pop(); } if(root.right){ backtracking(root.right) path.pop(); } } backtracking(root); return result; };

  1. <a name="CKTY9"></a>
  2. ## 112. 路径总和
  3. ```javascript
  4. var hasPathSum = function(root, targetSum) {
  5. //回溯
  6. if(!root) return false;
  7. //注意要到叶子节点
  8. if(!root.left && !root.right && targetSum===root.val) return true
  9. //利用targetSum-root.val后撤
  10. return hasPathSum(root.left,targetSum-root.val) || hasPathSum(root.right,targetSum-root.val)
  11. };

113.路径总和 II

  1. var pathSum = function(root, targetSum) {
  2. let pathResult=[],pathArray=[] //需要保存路径
  3. function getTargetPath(root, targetSum){
  4. if(!root) return;
  5. pathArray.push(root.val)
  6. if(!root.left && !root.right && targetSum===root.val){
  7. pathResult.push([...pathArray]);
  8. return
  9. }
  10. if(root.left){
  11. getTargetPath(root.left,targetSum-root.val)
  12. pathArray.pop() //后撤
  13. }
  14. if(root.right){
  15. getTargetPath(root.right,targetSum-root.val)
  16. pathArray.pop()
  17. }
  18. }
  19. getTargetPath(root, targetSum)
  20. return pathResult;
  21. };

搜索二叉树

二叉搜索树是一个有序树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树

二叉搜索树 中序遍历是一个递增序列,很重要的一点

98. 验证二叉搜索树

  • 陷阱1

不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。
我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。

  1. //方法一:因为二叉搜索树 中序遍历是一个递增序列。所以额外空间一个数组记录中序遍历结果
  2. var isValidBST = function(root) {
  3. let path=[]
  4. function tranverse(root){//中序遍历
  5. if(!root) return;
  6. tranverse(root.left)
  7. path.push(root.val);
  8. tranverse(root.right)
  9. }
  10. tranverse(root)
  11. for(let i=1;i<path.length;i++){
  12. if(path[i]<=path[i-1]) return false;
  13. }
  14. return true;
  15. };
  16. //方法二:也可以不用额外空间,递归遍历时利用pre记录上一个数字进行比较
  17. var isValidBST = function(root) {
  18. let pre=-Infinity
  19. function tranverse(root){
  20. if(!root) return true;
  21. const left=tranverse(root.left)
  22. if(root.val>pre) pre=root.val
  23. else return false;
  24. const right=tranverse(root.right)
  25. return left && right
  26. }
  27. return tranverse(root)
  28. };
  29. //方法三: 不用递归,用迭代
  30. var isValidBST = function (root) {
  31. let path = [];
  32. let pre = -Infinity;
  33. path.push(root);
  34. path.push(null);
  35. while (path.length > 0) {
  36. let node = path.pop();
  37. if (node) {
  38. if (node.val > pre) pre = node.val;
  39. else return false;
  40. } else {
  41. node = path.pop();
  42. if (node.right) {
  43. path.push(node.right);
  44. path.push(null);
  45. }
  46. path.push(node);
  47. if (node.left) {
  48. if (node.left) path.push(node.left);
  49. path.push(null);
  50. }
  51. }
  52. }
  53. return true;
  54. };

108. 将有序数组转换为二叉搜索树

因为是有序数组,所以选取哪个为根节点都可以形成二叉搜索树,不过要求是高度平衡,所以要选取中间的为根节点。

  1. var sortedArrayToBST = function(nums) {
  2. function traversal(nums,left,right){
  3. if (left > right) return null
  4. let mid=left + Math.ceil(((right - left) / 2));
  5. //也可以是let mid=left + Math.floor(((right - left) / 2)); 基准不同,但必须在中间
  6. let root=new TreeNode(nums[mid])
  7. root.left = traversal(nums, left, mid - 1);
  8. root.right = traversal(nums, mid + 1, right);
  9. return root
  10. }
  11. let root=traversal(nums, 0, nums.length - 1);
  12. return root;
  13. };

106. 从中序与后序遍历序列构造二叉树