最近写的leetcode
的100多题位置的时候,出现了几题由xxx(数组/链表/…)生成树的一类型的题,总结一下
用xx(数组/链表/…)生成树的题:
- 再去找这个树的根节点并生成一个基础的
TreeNode
- 再通过递归解决左右节点,逐渐拼接上去
- 同时要把无结点和只有一个节点的过滤出去
先看105,106由前序+中序/中序+后序生成二叉树
同样是先找到二叉树的根节点
-
前序+中序
- 整棵树根节点为前序数组的第一个数,根据这个根节点
new TreeNode()
- 然后就可以根据这个节点在前序和中序数组中找到根节点的左右子树数组,之后递归
- 同时在函数的最前面将无节点和只有一个节点的两种情况过滤出去
所以代码为
var buildTree = function (preorder, inorder) { if (preorder.length === 0) { return null; } if (preorder.length === 1) { return new TreeNode(preorder[0]); } const root = preorder[0]; // 拿到根节点 const index = inorder.indexOf(root); // 找到根节点在中序数组中的位置 const inLeft = inorder.slice(0, index); // 根据位置拿到前序中序数组中的左右子树 const inRight = inorder.slice(index + 1); const preLeft = preorder.slice(1, index + 1); const preRight = preorder.slice(index + 1); const node = new TreeNode(root); node.left = buildTree(preLeft, inLeft); // 递归 node.right = buildTree(preRight, inRight); return node; };
- 整棵树根节点为前序数组的第一个数,根据这个根节点
-
同样中序加后序也是一样的思路,但是取出子树时的做法和前序+中序有点点不一样,这里涉及的是二叉树的知识了,直接上代码
var buildTree = function (inorder, postorder) { if (inorder.length === 0) { return null; } if (inorder.length === 1) { return new TreeNode(inorder[0]); } const root = postorder[postorder.length - 1]; const index = inorder.indexOf(root); const postLeft = postorder.slice(0, index); const postRight = postorder.slice(index, postorder.length - 1); const inLeft = inorder.slice(0, index); const inRight = inorder.slice(index + 1); const node = new TreeNode(root); node.left = buildTree(inLeft, postLeft); node.right = buildTree(inRight, postRight); return node; };
再看108和109,分别是将有序数组/有序链表转换成二叉搜索树,需要注意的是这里的二叉搜索树指的是 高度平衡二叉搜索树—— 一个高度平衡二叉树是指一个二叉树每个节点的左右两个子树的高度差的绝对值不超过 1
-
数组的比较好处理,因为需要左右子树高度差绝对值不超过1,所以直接找到传入的有序数组中间的那个数作为根节点,将由根节点切割生成的左右数组作为左右子树进行遍历,依旧需要对两种情况进行过滤
var sortedArrayToBST = function (nums) { if (nums.length === 0) { return null; } if (nums.length === 1) { return new TreeNode(nums[0]); } let mid = parseInt(nums.length / 2); // 获得根节点的索引 let root = new TreeNode(nums[mid]); root.left = sortedArrayToBST(nums.slice(0, mid)); root.right = sortedArrayToBST(nums.slice(mid + 1)); return root; };
-
链表的复杂一些,因为它不能直接像数组那样直接取出根节点,所以需要设置两个指针,通过前面的指针每次走两次,后面的指针每次走一次,来获得中间的那个数以生成根节点
(此题参考博客:https://blog.csdn.net/weixin_36769175/article/details/81332038)
同时因为链表不能像数组那样直接
slice
来获得左右子树的数组,所以需要设多一个索引位来作为尾部位置的索引,头部的就使用参数中给的head
所以递归的时候写多一个函数作为传入两个参数:
head
和tail
的递归var childSort = function (head, tail) { if (head === tail) { return null; } else if (head.next === tail) { return new TreeNode(head.val); } else { let front = head; let end = head; while (front !== tail && front.next !== tail) { front = front.next.next; end = end.next; } // 此时的end即为中间位置的节点 var node = new TreeNode(end.val); node.left = childSort(head, end); node.right = childSort(end.next, tail); return node; } }; var sortedListToBST = function (head) { return childSort(head, null); };
所以这几道题看下来,感觉由xxx生成xxx二叉树的题的思路是一样的,过滤=>找到根节点=>将左右子树递归