二叉树算法题

匿名 (未验证) 提交于 2019-12-03 00:05:01
  • 二叉树层次遍历

//思路  特殊情况 ,根节点为null,深度为0,return 0 借助队列,队列储存每一层的所有节点, 先保存数组长度,用于控制依次遍历循环――遍历每一个节点保存值,该节点的所有子结点从队尾入队,访问过的节点从头出队 注意――需要先把width存下来,用于for循环的结束标志,因为for循环里面直接操作了queue,不能去都不敢动态获取 队列初始值为root,对每一层都要进行上述遍历――while循环控制,队列空代表叶节点这一层遍历完成,此时遍历结束,退出循环 每次循环开始前初始化一个curNodes储存该层所有节点,每次循环结束,将curNodes压入result  var levelOrder = function(root) {   if (root === null) return []; //空树   var result = [],     queue = [root];   while (queue.length) {     let width = queue.length; //需要先把width存下来,用于for循环,for循环里面直接操作了数组     let curNodes = [];     for (let i = 0; i < width; i++) {       let node = queue.shift();       curNodes.push(node.val);       node.left ? queue.push(node.left) : "";       node.right ? queue.push(node.right) : "";     }     result.push(curNodes);   }   return result; };
  • 二叉树反向层次遍历

var levelOrderBottom = function(root) {   if (root === null) return []; //空树   var result = [],     queue = [root];   while (queue.length) {     let width = queue.length; //需要先把width存下来,用于for循环,for循环里面直接操作了数组     let curNodes = [];     for (let i = 0; i < width; i++) {       let node = queue.shift();       curNodes.push(node.val);       node.left ? queue.push(node.left) : "";       node.right ? queue.push(node.right) : "";     }     result.unshift(curNodes);//从头部插入,先插入顶部的,后插入底部的额   }   return result; };
  • 先序遍历

 
  • 中序遍历

//思路 和前序其他都相似,左节点要先访问根节点要后访问,所以在根节点出栈的时候加入result数组  特殊情况:根节点为空,返回空数组 利用栈来存放访问过的根节点――定义一个指针指向要访问的节点,先访问根节点。,结点存入栈中。 stack存放遍历过的根,左节点,以便回溯访问右节点 然后指针一直向下访问左节点 当根节点没有左节点时,将根节点退栈,将遍历到的结点值存入result数组,然后指针指向右节点,访问右节点 直到栈为空而且指针为空,所有节点遍历完成,退出循环,返回结果  var inorderTraversal = function(root) {   if (root === null) return []; //空树   var result = [],     stack = []; //result储存结果,stack存放遍历过的根,左节点,以便回溯访问右节点   var p = root; //p指向当前遍历的节点   //当有未访问的右节点(stack不空)||还有左节点没有访问(p不空)时进入循环   while (stack.length != 0 || p != null) {     //还有左节点没有访问(p不空)     if (p != null) {       stack.push(p); //把遍历过的节点放入stack保管       p = p.left; //访问左节点     }     //左节点访问完     else {         let node = stack.pop();//栈顶节点退栈,       result.push(node.val); //遍历左节点-根节点       p = node.right; //访问右节点     }   }   return result; };
  • 后序遍历

//思路 特殊情况:根节点为空,返回空数组 利用栈来存放访问过的根节点――定义一个指针指向要访问的节点,先访问根节点。,结点存入栈中。 stack存放遍历过的根,右节点,以便回溯访问左节点 然后指针一直向下访问右节点 当根节点没有右节点时,将根节点退栈,将遍历到的结点值存入result数组,然后指针指向右节点,访问右节点 直到栈为空而且指针为空,所有节点遍历完成,退出循环,返回结果  var postorderTraversal = function(root) {   if (root === null) return []; //空树   var result = [],     stack = []; //result储存结果,stack存放遍历过的根,左节点,以便回溯访问右节点   var p = root; //p指向当前遍历的节点   //当有未访问的左节点(stack不空)||还有右节点没有访问(p不空)时进入循环   while (stack.length != 0 || p != null) {     //还有左节点没有访问(p不空)     if (p != null) {       stack.push(p); //把遍历过的节点放入stack保管       result.unshift(p.val); //从result数组头部插入根节点-右节点-左节点       //最后result顺序为左节点-右节点-根节点       p = p.right; //访问右节点     }     //右节点访问完     else {       p = stack.pop().left; //栈顶节点退栈,访问左节点     }   }   return result; };
  • 重建二叉树

//思路:二叉树前序遍历第一个点为根节点,中序遍历顺序为先左子树然后根节点最后右子树。所以先通过前序遍历找出根节点,然后将中序遍历分为左右子树两组,最后对于每个子树依次递归调用。  function reConstructBinaryTree(pre, vin) {  if (pre.length === 0 || vin.length === 0) return null; // 前序/中序又一个为空,就返回空值   let root = new TreeNode(pre[0]); //新建节点,作为根节点   if (pre.length === 1) return root; //是叶节点直接返回root,不需要计算子树   //不是叶节点,先递归得到左右节点   let rootIdx = vin.indexOf(pre[0]); //根节点在中序的位置   root.left = reConstructBinaryTree(     // 递归调用得到左子树的根节点     pre.slice(1, rootIdx + 1),     vin.slice(0, rootIdx)   );   root.right = reConstructBinaryTree(     // 递归调用得到右子树的根节点     pre.slice(rootIdx + 1),     vin.slice(rootIdx + 1)   );   return root; }
  • 二叉树镜像

//思路:先将根的左右节点互换,然后就是递归调用,对左右子树进行分别处理  function Mirror(root) {     // write code here     if(root==null) return null;     //首先先将左右节点互换     var  tmp = root.left;     root.left=root.right;     root.right=tmp;     //递归     Mirror(root.left);     Mirror(root.right);  }
  • 平衡二叉树

//在这里,由于我们是以递归的形式,所以,我们会先遍历左节点,再遍历右节点,最后再遍历根节点(后序遍历)  同时,我们判断左右树的高度差是否超过 1,如果是,则进行中断,返回 false;否则,继续递归。  最后,我们将遍历的结果与 -1 进行比较,返回 true 或者 false  let root = {   val: 3,   left: { val: 9, left: null, right: null },   right: {     val: 20,     left: { val: 3, left: null, right: null },     right: {       val: 7,       left: null,       right: { val: 2, left: null, right: null },     },   }, }  var isBalanced = function(root) {   let ergodic = function(root) {     if (!root) {       return 0;     }     let left = ergodic(root.left);     if (left === -1) {       return -1;     }     let right = ergodic(root.right);     if (right === -1) {       return -1;     }     return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;   }   return ergodic(root) != -1; };  console.log(isBalanced(root));
  • 二叉树深度

//题目: 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。  //思路 特殊情况 ,根节点为null,深度为0,return 0 递归法,递归法获取左右子树的深度, root的深度是左右子树的深度最大值+1,返回深度  /* function TreeNode(x) {     this.val = x;     this.left = null;     this.right = null; } */ function TreeDepth(pRoot) {   // write code here   if (!pRoot) return 0; //根节点为空,深度为0   var left = TreeDepth(pRoot.left); //左深度等于左子树深度+1   var right = TreeDepth(pRoot.right); //右深度等于右子树深度+1   return 1 + Math.max(left, right); //该节点深度为左右深度中的max,返回 }
  • 二叉树最大深度

//思路 我们判断 root 是否还存在 left 和 right:如果不存在,那么树到了最底层,终止递归;如果存在,那么 depth 深度 + 1,并且遍历它的左树和右树。  同时,我们在树存在 left 和 right 的时候,将 depth 和 longest 比较,把最深的树给记录下来。  返回最长深度 longest。  var maxDepth = function(root) {   return root === null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; };
  • 二叉树最小深度

//思路 假设我是一只蜘蛛,我在一颗大树最底下(根节点),开始往上爬。  每经过 1 米(1 个 val 节点),我就留下一个分身。  当我爬到最顶的时候,我就进行最后标记,并告诉分身,前面凉凉了,开始报数!  于是从我为 1 开始,一直到根节点的长度,就是这个分支的高度。  const root = {   val: 3,   left: { val: 9, left: null, right: null },   right: {     val: 20,     left: { val: 15, left: null, right: null },     right: { val: 7, left: null, right: null },   }, } var minDepth = function(root) {   if (!root) {     return 0;   }   if (!root.left) {     return minDepth(root.right) + 1;   }   if (!root.right) {     return minDepth(root.left) + 1;   }   return Math.min(minDepth(root.left), minDepth(root.right)) + 1; }; minDepth(root);
  • 翻转二叉树

 
  • 二叉树下一个节点

 
  • 最大二叉树

//题目: 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下: 二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的根节点。  //思路 递归法实现构建二叉树 递归出口,数组长度为0,return null,返回空节点 根节点为传入数组的max值, 左节点为递归调用,传入以max为分割点的左边数组的返回值, 右节点为递归调用,传入以max为分割点的右边数组的返回值, slice(left,right)分割数组,索引为left的元素划分进去,索引为right的元素不划分进去。  var constructMaximumBinaryTree = function(nums) {   if (nums.length <= 0) return null;   let max = Math.max(...nums);   let i = nums.indexOf(max);   let root = new TreeNode(max);   root.left = constructMaximumBinaryTree(nums.slice(0, i));   root.right = constructMaximumBinaryTree(nums.slice(i + 1));   return root; };
  • 序列化二叉树

//题目描述 请实现两个函数,分别用来序列化和反序列化二叉树  //思路:  序列化,将节点值存入数组中,空节点则使用特殊标记存入数组中。 反序列化,从数组中获取元素,为number类型则生成节点,为特殊标记,则为空节点  var arr=[]; function Serialize(pRoot) {     // write code here     if(pRoot==null){         arr.push('#')         return;     }      arr.push(pRoot.val);     Serialize(pRoot.left)     Serialize(pRoot.right) } function Deserialize(s) {     // write code here     if(arr==null){         return null;     }      if(arr.length<1){         return null;     }     var root=null;     var temp=arr.shift();     if(typeof temp=='number'){         root=new TreeNode(temp);         root.left=Deserialize(arr);         root.right=Deserialize(arr);     }     return root; }
  • 二叉树中和为某一值的路径

//思路  因为要先访问最长的路径,所以考虑` 递归深度遍历:对根节点调用递归函数 curPath保存当前路径,sum保存当前和,每访问一个节点,更新路径和和, 更新之后,判断是否满足叶节点+和为指定值,如果满足,压入结果数组 注意:因为 curPath是一个引用类型,它的值一直在变化,所以压入数组时需要先进行深拷贝,如果不拷贝会导致结果输出为最后的curPath空数组。 左右节点存在时,对左右节点递归遍历 当前节点和所有子节点遍历完成,当前节点退出路径   function FindPath(root, expectNumber) {   // write code here   if (!root) return [];   var res = [], //所有满足条件路径     curPath = [], //当前路径     p = root, //访问指针     sum = 0; //当前路径和   dfs(p, curPath, sum, res, (exp = expectNumber));   return res; } function dfs(p, curPath, sum, res, exp) {     if(!p&&curPath.length===0) return;//所有节点访问完毕,退出递归   curPath.push(p.val); //节点值加入路径   sum += p.val; //更新和   if (!p.left && !p.right && sum === exp) {     //栈顶节点是叶节点且路径满足条件     res.push(curPath.slice(0)); //路径加入结果   }   p.left ? dfs(p.left, curPath, sum, res, exp) : "";   p.right ? dfs(p.right, curPath, sum, res, exp) : "";   curPath.pop(); //节点和所有子树访问完毕,退出路径 }
  • 求根到叶子节点数字之和

//题目: 给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。例如,从根到叶子节点路径 1->2->3 代表数字 123。计算从根到叶子节点生成的所有数字之和。  //方法一:递归法 //思路  递归输入当前节点和上一层的和,初始为root和0 如果节点空返回0 更新sum,是叶节点的话直接返回这个值 不是叶节点再加上左右节点的递归返回值  var sumNumbers = function(root) {     return dfs(root,0); }; var dfs = function(p,sum) {     if(!p) return 0;     sum=sum*10+p.val;     if(!p.left&&!p.right) return sum;     return dfs(p.left,sum)+dfs(p.right,sum); };
  • 二叉树所有路径

//思路: 使用递归方法来解决此问题。对于根节点,如果根节点为空,则返回空数组[],如果为叶子节点,则返回包含此节点的值的数组(这个数组只有一个元素)。若不为空也不是叶子节点,则对左右子树递归调用,将两个结果数组拼接起来,最后使用 map 函数来对数组的每个元素进行字符串的操作。  /**  * Definition for a binary tree node.  * function TreeNode(val) {  *     this.val = val;  *     this.left = this.right = null;  * }  */  var binaryTreePaths = function(root) {   if (root === null) return [];   if (root.left === null && root.right === null) {     return [root.val.toString()];   }   var left = binaryTreePaths(root.left),       right = binaryTreePaths(root.right);   return left.concat(right).map(x => root.val + '->' + x); };
  • 树的子结构

 
  • 二叉搜索树最近公共祖先

//思路: 首先确保p的值小于q,若不是,则互换。这样有助于判断root、p、q三者的位置。  三种情况  root最小,说明p和q的最近公共祖先一个在root的右边,使用root.right递归调用 root在中间,说明最近公共祖先只能是root,返回root root最大,说明p和q的最近公共祖先一个在root的左边,使用root.left递归调用  /**  * Definition for a binary tree node.  * function TreeNode(val) {  *     this.val = val;  *     this.left = this.right = null;  * }  */  var lowestCommonAncestor = function(root, p, q) {   if (p.val > q.val) [p, q] = [q, p];                           // 让p小于q,方便判断   if (root.val >= p.val && root.val <= q.val) {     return root;   } else if (root.val <= p.val && root.val <= q.val) {     return lowestCommonAncestor(root.right, p, q);   } else if (root.val >= p.val && root.val >= q.val) {     return lowestCommonAncestor(root.left, p, q);   } };

  • 二叉搜索树的后序遍历

//思路 后续遍历我们可以知道,最右边的是根节点r。 2.通过根节点r我们可以判断左子树和右子树。 3.判断左子树中的每个值是否小于r,右子树的每个值是否大于r. 4.对左、右子树递归判断。  function VerifySquenceOfBST(sequence) {     // write code here     if(sequence.length<=0) return;     return test(sequence,0,sequence.length-1) } function test(sequence,start,end){     if(start>=end) return true;     var i=end-1;     while(i>=start && sequence[i]>sequence[end]){         i--;     }     for(var j=i;j>=start;j--){         if(sequence[j]>sequence[end]){             return false;         }     }     return test(sequence,start,i)&&test(sequence,i+1,end-1) }
  • 二叉搜索树的第k个结点

//思路:二叉搜索树,若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;任意节点的左、右子树也分别为二叉查找树; 所以采用中序遍历的方法,遍历后的结果就是从小到大顺序的结果  function KthNode(pRoot, k) {     // write code here      var arr=[];     if(pRoot===null||k<1){         return null;     }     function midInorder(root){         if(root.left!==null){              midInorder(root.left);         }         arr.push(root);         if(root.right!==null){             midInorder(root.right);         }     }     midInorder(pRoot);     return arr[k-1]; }
  • 将有序数组转换为二叉搜索树

 
  • 深度优先遍历

该方法是以纵向的维度对dom树进行遍历,从一个dom节点开始,一直遍历其子节点,直到它的所有子节点都被遍历完毕之后在遍历它的兄弟节点。//递归 function deepFirstSearch(node,nodeList) {       if (node) {             nodeList.push(node);             var children = node.children;             for (var i = 0; i < children.length; i++)          //每次递归的时候将 需要遍历的节点 和 节点所存储的数组传下去         deepFirstSearch(children[i],nodeList);         }         return nodeList;   } 

//deepFirstSearch接受两个参数,第一个参数是需要遍历的节点,第二个是节点所存储的数组,并且返回遍历完之后的数组,该数组的元素顺序就是遍历顺序,调用方法:


//非递归 function deepFirstSearch(node) {     var nodes = [];     if (node != null) {         var stack = [];         stack.push(node);         while (stack.length != 0) {         var item = stack.pop();         nodes.push(item);         var children = item.children;         for (var i = children.length - 1; i >= 0; i--)             stack.push(children[i]);         }     }     return nodes; }
  • 广度优先遍历

该方法是以横向的维度对dom树进行遍历,从该节点的第一个子节点开始,遍历其所有的兄弟节点,再遍历第一个节点的子节点,完成该遍历之后,暂时不深入,开始遍历其兄弟节点的子节点。  //非递归版本 function breadthFirstSearch(node) {       var nodes = [];       if (node != null) {           var queue = [];           queue.unshift(node);           while (queue.length != 0) {               var item = queue.shift();               nodes.push(item);               var children = item.children;               for (var i = 0; i < children.length; i++)                   queue.push(children[i]);           }       }       return nodes;   }

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!