数据结构专项-算法第四周(二叉树)(递归解法为主)

128 阅读3分钟
  1. 144. 二叉树的前序遍历
  2. 589. N 叉树的前序遍历
  3. 226. 翻转二叉树
  4. 102. 二叉树的层序遍历
  5. 107. 二叉树的层序遍历 II
  6. 103. 二叉树的锯齿形层序遍历
  7. 110. 平衡二叉树
  8. 112. 路径总和
  9. 105. 从前序与中序遍历序列构造二叉树
  10. 222. 完全二叉树的节点个数

144. 二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

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

题意理解: 前序遍历二叉树 根 左 右 使用递归处理

  1. 边界处理 root为空返回[]
  2. res 为返回值
  3. 递归写法 根的值push进去,左子树迭代,右子树迭代
/**
 * 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[]}
 */
var preorderTraversal = function(root) {
    if(!root) return [];
    let res=[];
    if(root) {
        res.push(root.val);
    }
    if(root?.left) {
        res=[...res,...preorderTraversal(root?.left)];
    }
    if(root?.right){
        res=[...res,...preorderTraversal(root?.right)];
    } 
    return res
};

589. N 叉树的前序遍历

给定一个 n 叉树的根节点  root ,返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。

提示: 节点总数在范围 [0, 104]内 0 <= Node.val <= 104 n 叉树的高度小于或等于 1000

题意理解: 与上一提类似,知识把左右字数换成多子树 先遍历根 左 中1 中2 中3 ... 右

  1. 边界处理 root为空返回[]
  2. res 为返回值
  3. 递归写法 根的值push进去,迭代左 中1 中2 中3 ... 右,用for循环遍历

递归

/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children;
 * };
 */

/**
 * @param {Node|null} root
 * @return {number[]}
 */
var preorder = function(root) {
    if(!root) return [];
    let res=[];
    if(root) res.push(root.val);
    if(root?.children){
        for(const item of root?.children){
            res=[...res,...preorder(item)]
        }
    }
    return res
};

利用栈(先进后出)

  1. 初始化栈stack,将根节点推入栈中,res存储返回值
  2. 遍历栈,执行出栈,将出栈数据出入res,如果出栈数据有children,则从右往左遍历执行入栈操作,一次类推
  3. 返回res
/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children;
 * };
 */

/**
 * @param {Node|null} root
 * @return {number[]}
 */
var preorder = function (root) {
    if (!root) return [];
    const res = [];
    const stack = [];
    stack.push(root)
    while (stack.length) {
        const currentNode = stack.pop();
        res.push(currentNode.val)
        for (let i = currentNode.children.length - 1; i >= 0; --i) {
            stack.push(currentNode.children[i])
        }
    }
    return res
};

迭代1

  1. 使用Map来存储每一层访问过的数据,从上到下,从左往右递推
/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children;
 * };
 */

/**
 * @param {Node|null} root
 * @return {number[]}
 */
var preorder = function (root) {
    if (!root) return [];
    const res = [];
    const stack = [];
    const currentMap = new Map();
    let currentNode = root;
    while (stack.length || currentNode) {
        while (currentNode) {
            res.push(currentNode.val);
            stack.push(currentNode)
            currentMap.set(currentNode, 1)
            currentNode = currentNode?.children?.[0] || null;
        }
        currentNode = stack[stack.length - 1]
        while (currentNode?.children?.length && currentMap.get(currentNode) < currentNode?.children?.length) {
            const currentIndex = currentMap.get(currentNode);
            currentMap.set(currentNode, currentIndex + 1)
            currentNode = currentNode?.children?.[currentIndex];
            while (currentNode) {
                res.push(currentNode.val);
                stack.push(currentNode)
                currentMap.set(currentNode, 1)
                currentNode = currentNode?.children?.[0] || null;
            }
        }
        stack.pop();
        currentNode = null;
    }

    return res
};

迭代2

/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children;
 * };
 */

/**
 * @param {Node|null} root
 * @return {number[]}
 */
var preorder = function (root) {
    if (!root) return [];
    const res = [];
    const stack = [];
    const currentMap = new Map();
    let currentNode = root;
    while (stack.length || currentNode) {
        while (currentNode) {
            res.push(currentNode.val);
            stack.push(currentNode)
            currentMap.set(currentNode, 1)
            currentNode = currentNode?.children?.[0] || null;
        }
        currentNode = stack[stack.length - 1]
        if (currentNode?.children?.length && currentMap.get(currentNode) < currentNode?.children?.length) {
            const currentIndex = currentMap.get(currentNode);
            currentMap.set(currentNode, currentIndex + 1)
            currentNode = currentNode?.children?.[currentIndex];
        }else{
            stack.pop();
            currentNode = null;
        }
    }

    return res
};

226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

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

题意理解: 将二叉树左右翻转

  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}
 */
var invertTree = function(root) {
    if(!root) return null;
    if(root.left){
        invertTree(root.left)
    }
    if(root.right){
        invertTree(root.right)
    }
    [root.left,root.right]=[root.right,root.left]
    return root
};

102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

提示: 树中节点数目在范围 [0, 2000] 内 -1000 <= Node.val <= 1000

题意理解: 按层遍历二叉树,及广度优先

  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[][]}
 */
var levelOrder = function(root) {
    if(!root) return [];
    const res=[];
    let stack=[];
    stack.push(root)
    while(stack.length){
        const cuurentLayer=[];
        const currentLayerValue=[];
        while(stack.length){
            const value=stack.shift();
            if(value.left) cuurentLayer.push(value.left)
            if(value.right) cuurentLayer.push(value.right)
            currentLayerValue.push(value.val)
        }
        stack=cuurentLayer
        res.push(currentLayerValue)
    }
    return res;
};

107. 二叉树的层序遍历 II

给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

提示: 树中节点数目在范围 [0, 2000] 内 -1000 <= Node.val <= 1000

题意理解: 与上题原理相同(102. 二叉树的层序遍历)

  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[][]}
 */
var levelOrderBottom = function(root) {
    if(!root) return [];
    const res=[];
    let stack=[];
    stack.push(root);
    while(stack.length){
        const currentLayer=[];
        const currentLayerValue=[];
        while(stack.length){
            const currentNode = stack.shift();
            currentLayerValue.push(currentNode.val)
            if(currentNode.left) currentLayer.push(currentNode.left);
            if(currentNode.right) currentLayer.push(currentNode.right);
        }
        stack=currentLayer;
        res.push(currentLayerValue)
    }
    return res.reverse();
};

103. 二叉树的锯齿形层序遍历

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

提示: 树中节点数目在范围 [0, 2000] 内 -100 <= Node.val <= 100

题意理解: 与题(二叉树的层序遍历)原理相同;

  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[][]}
 */
var zigzagLevelOrder = function(root) {
    if(!root) return [];
    const res=[];
    let stack=[];
    stack.push(root);
    let isSingle=true;
    while(stack.length){
        const currentLayer=[];
        const currentLayerValue=[];
        while(stack.length){
            const currentNode = stack.shift();
            currentLayerValue.push(currentNode.val)
            if(currentNode.left) currentLayer.push(currentNode.left);
            if(currentNode.right) currentLayer.push(currentNode.right);
        }
        stack=currentLayer;
        const value = isSingle?currentLayerValue:currentLayerValue.reverse();
        res.push(value)
        isSingle=!isSingle
    }
    return res;
};

110. 平衡二叉树

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

提示: 树中的节点数在范围 [0, 5000] 内 -104 <= Node.val <= 104

题意理解: 判断一颗二叉树是否是平衡二叉树

  1. 递归计算二叉树的左右子树的高度
  2. 如果左右子树相差大于1则返回false否则返回true
/**
 * 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}
 */
var isBalanced = function(root) {
    if(!root) return true;
    function height(root){
        if(!root) return 0;
        const leftHeight=height(root.left);
        const rightHeight=height(root.right);
        if(leftHeight<0 || rightHeight<0 || Math.abs(leftHeight-rightHeight)>1){
            return -1
        }else{
            return Math.max(leftHeight,rightHeight)+1;
        }
    }
    return height(root)>=0
};

112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。 叶子节点 是指没有子节点的节点。

提示: 树中节点的数目在范围 [0, 5000] 内 -1000 <= Node.val <= 1000 -1000 <= targetSum <= 1000

题意理解: 判断给定的树是否存在一条路径,是否存在从根节点到叶子节点的路径之和目标值相同的路径

  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} targetSum
 * @return {boolean}
 */
var hasPathSum = function (root, targetSum) {
    if (!root) return false;
    if (!root.left && !root.right) {
        return targetSum === root.val;
    }
    return hasPathSum(root.left,targetSum-root.val) || hasPathSum(root.right,targetSum-root.val);
};

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

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

提示: 1 <= preorder.length <= 3000 inorder.length == preorder.length -3000 <= preorder[i], inorder[i] <= 3000 preorder 和 inorder 均 无重复 元素 inorder 均出现在 preorder preorder 保证 为二叉树的前序遍历序列 inorder 保证 为二叉树的中序遍历序列

题意理解: 先序遍历: 根 左 右 中序遍历: 左 根 右

  1. 可以确定节点值为preorder[0],借助栈来进行数据操作
  2. startIndex表示中序遍历已确定的数的下标
  3. 遍历preorder,i=1开始stack[stack.length - 1].val !== inorder[startIndex]表示是当前节点的左节点,否则为有节点
  4. 右节点需要执行出栈操作,确定中序遍历的为左节点的位置用while循环判断
/**
 * 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}
 */
var buildTree = function (preorder, inorder) {
    const root = new TreeNode(preorder[0])
    const stack = [];
    stack.push(root);
    let startIndex = 0;
    for (let i = 1; i < preorder.length; i++) {
        let currentNode = stack[stack.length - 1]
        if (stack[stack.length - 1].val !== inorder[startIndex]) {
            currentNode.left = new TreeNode(preorder[i])
            stack.push(currentNode.left)
        } else {
            while (stack.length && stack[stack.length - 1].val === inorder[startIndex]) {
                currentNode = stack.pop()
                startIndex++;
            }
            currentNode.right = new TreeNode(preorder[i])
            stack.push(currentNode.right)
        }
    }
    return root;
};

222. 完全二叉树的节点个数

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。 完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

提示: 树中节点的数目范围是[0, 5 * 104] 0 <= Node.val <= 5 * 104 题目数据保证输入的树是 完全二叉树

题意理解: 统计完全二叉树中节点个数

  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}
 */
var countNodes = function (root) {
    if (!root) return 0;
    const l = countNodes(root.left);
    const r = countNodes(root.right);
    return 1 + l + r;
};

利用完全二叉树的特性 递归的另一种写法

/**
 * 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}
 */
var countNodes = function (root) {
    if (!root) return 0;
    let leftLevel = 0;
    let rightLevel = 0;
    let currentNode = root.left;
    while (currentNode) {
        leftLevel++;
        currentNode = currentNode.left;
    }
    currentNode = root.right
    while (currentNode) {
        rightLevel++;
        currentNode = currentNode.left;
    }
    if(leftLevel<=rightLevel) return countNodes(root.right)+(1<<leftLevel);
    else return countNodes(root.left)+(1<<rightLevel)
};
  1. 左子树节点深度大于右节点深度,则左边不完全
  2. 否则右边不完全
  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)
 * }
 */

function height(root) {
    let currentNode = root;
    let level = 0;
    while (currentNode) {
        level++;
        currentNode = currentNode.left;
    }
    return level;
}
/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function (root) {
    if (!root) return 0;
    const l = height(root.left);
    const r = height(root.right);
    if (l > r) return countNodes(root.left)+(1<<r);
    else return countNodes(root.right)+(1<<l)
};