JS数据结构第六篇 --- 二叉树力扣练习题
Posted tandaxia
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS数据结构第六篇 --- 二叉树力扣练习题相关的知识,希望对你有一定的参考价值。
递归+迭代两种实现方式:
/** 反转二叉树 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * */ /** * @param TreeNode root * @return TreeNode * 第一种方式迭代 * 执行用时 :72 ms, 在所有 javascript 提交中击败了87.29%的用户 * 内存消耗 :33.8 M, 在所有 JavaScript 提交中击败了24.26%的用户 */ var invertTree = function(root) if (!root) return root; var arr = [root]; while(arr.length) var current = arr.shift(); //取出节点,交换左右子节点 var temp = current.right; current.right = current.left; current.left = temp; //将左右子节点push到数组中 if (current.right) arr.push(current.right); if (current.left) arr.push(current.left); return root; ; /** * 第二种方式递归 * @param root * @returns * * 执行用时 :64 ms, 在所有 JavaScript 提交中击败了98.02%的用户 * 内存消耗 :33.6 MB, 在所有 JavaScript 提交中击败了53.85%的用户 */ var invertTree2 = function(root) if (!root) return root; var temp = invertTree(root.left); root.left = invertTree(root.right); root.right = temp; return root; ;
初看这个题目描述,没怎么看懂,特别是控制台的输入输出
比如输入:[3, 9, 20, 15, 7, 88, 16, 2, 19, 13, 26, 11]
输出是:[3,9,15,2,19,7,13,26,20,88,11,16]
一时没弄明白,后面琢磨了一下,才发现力扣这里的输入是按照输入顺序来组成树的,而不是按输入的大小组成树。
即上面这个输入的数字列表,做成二叉树图为:
如果输入的数字列表中带有null, 则null所在的子树空不占位,
比如输入:[3, 9, null, 20, 15, 7, 88, 16, 2, 19, null, 13, 26, 11]
输出为:[3, 9, 20, 7, 19, 88, 13, 26, 15, 16, 11, 2]
输入数字的二叉树图为:
理解了力扣题目的输入输出逻辑,咱们再做题,二叉树的前序遍历递归+迭代方式code (先根节点,再左子节点,再右子节点):
/** 前序遍历规则:先根节点,再左子节点,再右子节点 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * */ /** 第一种方式:递归 * @param TreeNode root * @return number[] 执行用时 :72 ms, 在所有 JavaScript 提交中击败了86.38%的用户 内存消耗 :33.8 MB, 在所有 JavaScript 提交中击败了17.62%的用户 */ var preorderTraversal = function(root) var arr = []; recusion(root, arr); return arr; function recusion(root) if (!root) return; //前序遍历,先根节点,再左节点,再右节点 arr.push(root.val); recusion(root.left, arr); recusion(root.right, arr); ; // function TreeNode(val) // this.val = val; // this.left = this.right = null; // /** * 第二种方式:迭代 * 执行用时 :76 ms, 在所有 JavaScript 提交中击败70.96%的用户 * 内存消耗 :33.6 MB, 在所有 JavaScript 提交中击败了60.62%的用户 */ var preorderForeach = function(root) var res = []; if (!root) return res; var arr = [root]; while(arr.length) //借助于栈的特性:后进先出 var current = arr.pop(); res.push(current.val); //先将右节点压入栈底,因为右节点后取值 if (current.right) arr.push(current.right); //左节点先取值,压入栈顶 if (current.left) arr.push(current.left); return res; ;
二叉树中序遍历,先找到最左边的左子节点,从这里开始,然后左子节点, 再根节点,再右子节点:
/** 中序遍历:从小到大,从做左边的左子节点,最后一个是右边的右子节点 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * */ /** 中序遍历:按照从小到大排序,先找到最左边的子节点,也就是最小值,再依次往上走父节点,右节点 * @param TreeNode root * @return number[] * 第一种方式:递归 * 执行用时 :64 ms, 在所有 JavaScript 提交中击败了97.67%的用户 * 内存消耗 :33.8 MB, 在所有 JavaScript 提交中击败20.52%的用户 */ var inorderTraversal = function(root) const res = []; if (!root) return res; recusion(root); return res; function recusion(root) if (!root) return; recusion(root.left); res.push(root.val); recusion(root.right); ; /** * 第二种方式:迭代 * 执行用时 :68 ms, 在所有 JavaScript 提交中击败了94.67%的用户 * 内存消耗 33.7 MB, 在所有 JavaScript 提交中击败了30.60%的用户 */ var inorderTraversal2 = function(root) const res = []; if (!root) return res; const arr = []; while (root || arr.length) while(root) arr.push(root); root = root.left; root = arr.pop(); //最后一个左节点 res.push(root.val); root = root.right; return res; ;
4、第145题:二叉树的后序遍历
后序遍历的规则:先叶子节点,再根节点;即先左子节点,再右子节点,再根节点。
/** 后序遍历规则:先叶子节点,叶子节点先左后右,再根节点 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * */ /** 后序遍历:先叶子节点,再左子树,再右子树 * 第一种方式:递归 * @param TreeNode root * @return number[] * 执行用时 :76 ms, 在所有 JavaScript 提交中击败了68.85%的用户 * 内存消耗 :33.9 MB, 在所有 JavaScript 提交中击败了9.84%的用户 */ var postorderTraversal = function(root) var res = []; if (!root) return res; recusion(root); return res; function recusion(root) if (!root) return; recusion(root.left); recusion(root.right); res.push(root.val); ; /** * 第二种方式:迭代 * @param root * @returns Array * 执行用时 :80 ms, 在所有 JavaScript 提交中击败了48.15%的用户 * 内存消耗 :33.7 MB, 在所有 JavaScript 提交中击败25.41%的用户 */ var postorderTraversal = function(root) var res = []; if (!root) return res; var arr = [root]; while (arr.length) var current = arr.pop(); res.unshift(current.val); if (current.left) arr.push(current.left); if (current.right) arr.push(current.right); return res; ;
递归层级遍历和前序遍历差不多,迭代方式层级遍历有点绕
/** 层次遍历 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * * 给定二叉树: [3,9,20,null,null,15,7], 3 / 9 20 / 15 7 返回其层次遍历结果: [ [3], [9,20], [15,7] ] */ /** 层次遍历,第一种方式:递归, 和前序遍历差不多 * @param TreeNode root * @return number[][] * 执行用时 :84 ms, 在所有 JavaScript 提交中击败了55.19%的用户 * 内存消耗 :34.6 M, 在所有 JavaScript 提交中击败了53.23%的用户 */ var levelOrder = function(root) var res = []; if (!root) return res; recusion(root, 0); return res; function recusion(root, level) if (!root) return; if (res[level]) res[level].push(root.val); else res[level] = [root.val]; if (root.left) recusion(root.left, level+1); if (root.right) recusion(root.right, level+1); ; /** * 第二种层序遍历:迭代 * @param root * @returns Array * 执行用时 :80 ms, 在所有 JavaScript 提交中击败了73.64%的用户 * 内存消耗 :34.8 MB, 在所有 JavaScript 提交中击败了28.36%的用户 */ var levelOrder2 = function(root) var res = []; if (!root) return res; var queue = [root]; while(queue.length) //内循环把这一层级的所有节点都放入tempQueue队列中,每一个外循环则是每一层级重新开始 var arr = [], tempQueue = []; while(queue.length) var current = queue.shift(); arr.push(current.val); if (current.left) tempQueue.push(current.left); if (current.right) tempQueue.push(current.right); console.log("tempQueue.length: ", tempQueue.length, ", queue.length: ", queue.length); console.log("-----------") res.push(arr); queue = tempQueue; console.log(JSON.stringify(res)) console.log("***************************") return res; ; // function TreeNode(val) // this.val = val; // this.left = this.right = null; // // // var node = new TreeNode(23); // node.left = new TreeNode(16); // node.right = new TreeNode(45); // node.left.left = new TreeNode(3); // node.left.right = new TreeNode(22); // node.right = new TreeNode(45); // node.right.left = new TreeNode(37); // node.right.right = new TreeNode(99); // console.log(levelOrder2(node));
二叉树的最大深度和求二叉树的层级遍历差不多
/** 二叉树的最大深度 * 给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * */ /** * @param TreeNode root * @return number * 第一种方式:递归 * 执行用时 :96 ms, 在所有 JavaScript 提交中击败了51.18%的用户 * 内存消耗 :37.1 MB, 在所有 JavaScript 提交中击败了40.00%的用户 */ var maxDepth = function(root) if (!root) return 0; var maxLevel = 1; recusion(root, 1); return maxLevel; function recusion(root, level) if (level > maxLevel) maxLevel = level; if (root.left) recusion(root.left, level+1); if (root.right) recusion(root.right, level+1); ; /** * 第二种:迭代 * @param root * @returns number 执行用时 :88 ms, 在所有 JavaScript 提交中击败了81.91%的用户 内存消耗 :36.8 MB, 在所有 JavaScript 提交中击败了93.61%的用户 */ var maxDepth2 = function(root) if (!root) return 0; var level = 0, queue = [root]; while(queue.length) var tempQueue = []; //内循环,每次把整个层级节点遍历完, tempQueue存储每个层级的所有节点 while(queue.length) var current = queue.shift(); if (current.left) tempQueue.push(current.left); if (current.right) tempQueue.push(current.right) level++; queue = tempQueue; return level; ; // function TreeNode(val) // this.val = val; // this.left = this.right = null; // // // var node = new TreeNode(3); // node.left = new TreeNode(9); // node.right = new TreeNode(20); // node.right.left = new TreeNode(15); // node.right.right = new TreeNode(7); // console.log(maxDepth(node))
这个题和二叉树层级遍历/求最大深度类似,但比层级遍历要绕要麻烦点。
迭代方式:迭代遍历,用2个栈,一个用来存储每一层级的节点,另一个栈用来存储每个节点的编号。
对节点进行编号,规则:根节点编号从0开始,左子节点编号 = 父节点编号 * 2 + 1, 右子节点编号 = 父节点编号 * 2 + 2;
递归方式:规则同迭代方式,也是对节点进行编号
如图:
/** * 给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。 每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。 示例 1: 输入: 1 / 3 2 / \\ 5 3 9 输出: 4 解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。 示例 2: 输入: 1 / 3 / 5 3 输出: 2 解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。 示例 3: 输入: 1 / 3 2 / 5 输出: 2 解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。 示例 4: 输入: 1 / 3 2 / 5 9 / 6 7 输出: 8 解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。 * Definition for a binary tree node. * function TreeNode(val) * this.val = val; * this.left = this.right = null; * */ /** * @param TreeNode root * @return number * 第一种方式:递归 * 执行用时 :84 ms, 在所有 JavaScript 提交中击败了100.00%的用户 * 内存消耗 :36.7 MB, 在所有 JavaScript 提交中击败了37.50%的用户 */ var widthOfBinaryTree = function(root) if (!root) return 0; //queue存储节点,numArr存储节点对应的节点编号位置 var queue = [root], numArr = [0], maxWidth = 1; while (queue.length) //tempQueue存储每一层级所有的节点,tempNumArr存储对应节点的编号位置 var tempQueue = [], tempNumArr = []; while (queue.length) var node = queue.shift(), num = numArr.shift(); //取出栈底节点和编号 if (node.left) tempQueue.push(node.left); tempNumArr.push(num * 2 + 1); if (node.right) tempQueue.push(node.right); tempNumArr.push(num * 2 + 2); var tempWidth = 0; //计算tempNumArr中存储的这一层的宽度, 最后一位元素存储这一层级最大宽度的编号 if (tempNumArr.length) tempWidth = tempNumArr[tempNumArr.length - 1] - tempNumArr[0] + 1; if (tempWidth > maxWidth) maxWidth = tempWidth; //更新最大宽度 //开始下一个层级的宽度计算 queue = tempQueue; numArr = tempNumArr; return maxWidth; ; /** * 第二种递归方式: * @param root * @returns number * 执行用时 :84 ms, 在所有 JavaScript 提交中击败了100.00%的用户 * 内存消耗 :36 MB, 在所有 JavaScript 提交中击败了75.00%的用户 */ var widthOfBinaryTree2 = function(root) if (!root) return 0; var res = [], maxWidth = 1; recusion(root, 0, 0); return maxWidth; function recusion(root, level, num) if (res[level]) res[level].push(num); else res[level] = [num]; //计算最大宽度 var tempArr = res[level]; var tempWidth = tempArr[tempArr.length - 1] - tempArr[0] + 1; if (tempWidth > maxWidth) maxWidth = tempWidth; if (root.left) recusion(root.left, level + 1, num * 2 + 1); if (root.right) recusion(root.right, level + 1, num * 2 + 2); ; // function TreeNode(val) // this.val = val; // this.left = this.right = null; // // // //[1,1,1,1,null,null,1,1,null,null,1] // var root = new TreeNode(1); // root.left = new TreeNode(1); // root.right = new TreeNode(1); // root.left.left = new TreeNode(1); // root.left.right = new TreeNode(3); // root.right.right = new TreeNode(9); // console.log(widthOfBinaryTree(root));
以上是关于JS数据结构第六篇 --- 二叉树力扣练习题的主要内容,如果未能解决你的问题,请参考以下文章