先说一个消息,为了方便互相交流学习,青铜三人行建了个微信群,感兴趣的伙伴可以扫码加下面的小助手抱你入群哦!
每周一题,代码无敌~这周「青铜三人行」带了一个二叉树的问题:
在二叉树中增加一行
青铜三人行——每周一题@在二叉树中增加一行
力扣题目
给定一个二叉树,根节点为第1层,深度为 1。在其第 d 层追加一行值为 v 的节点。
添加规则:给定一个深度值 d (正整数),针对深度为 d-1 层的每一非空节点 N,为 N 创建两个值为 v 的左子树和右子树。
将 N 原先的左子树,连接为新节点 v 的左子树;将 N 原先的右子树,连接为新节点 v 的右子树。
如果 d 的值为 1,深度 d - 1 不存在,则创建一个新的根节点 v,原先的整棵树将作为 v 的左子树。
输入:
二叉树如下所示:
4
/ \
2 6
/ \ /
3 1 5
v = 1
d = 2
输出:
4
/ \
1 1
/ \
2 6
/ \ /
3 1 5
输入:
二叉树如下所示:
4
/
2
/ \
3 1
v = 1
d = 3
输出:
4
/
2
/ \
1 1
/ \
3 1
- 输入的深度值 d 的范围是:[1,二叉树最大深度 + 1]
- 输入的二叉树至少有一个节点。
题目的关键在于找到对应的位置插入节点。而要找到这个“位置”的关键就在于找到插入层的所有父节点。因此对于二叉树的遍历不太了解的小伙伴,建议先去了解一下二叉树的各种遍历方式:
二叉树遍历_百度百科
对于二叉树的遍历,递归是一种常用的解法。在这道题中 helen 就使用了递归的方法来找到对应的层级,然后进行节点插入。
设定满足条件为进行 d-1 次递归,当满足时,这个节点就为需要插入节点的父节点。值的注意的是,要记得在每次递归时同时对左子树和右子树递归:
var addOneRow = function (root, v, d) {
if (d === 1) {
const head = {
val: v,
left: root,
right: null,
}
return head;
}
function traverse (root, count) {
if (!root) return;
if (count === d - 1) {
root.left = {
val: v,
left: root.left,
right: null,
};
root.right = {
val: v,
right: root.right,
left: null,
}
} else {
traverse(root.left, count+1);
traverse(root.right, count+1);
}
}
traverse(root,1);
return root;
}
函数递归是一种简化、理解遍历的方法。但是递归的缺点在于有可能在递归层级过深的时候造成爆栈的。在所有的程序中,只要有递归解法,就一定会存在遍历解法。因此书香带来了遍历的思路。
在其中,先遍历二叉树找到 d 层节点,存放入数组中,再次遍历数组中的节点,插入新的节点:
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} v
* @param {number} d
* @return {TreeNode}
*/
var addOneRow = function (root, v, d) {
function findDNodes(root, d) {
let resultNodes = [root];
for (let i = 1; i < d; i++) {
resultNodes = resultNodes
.filter(node => node)
.reduce((sofar, curr) => {
return sofar.concat([curr.left, curr.right]);
}, []);
}
return resultNodes;
}
if (d === 1) {
const insertNode = new TreeNode(v);
insertNode.left = root;
return insertNode;
}
const lastNodes = findDNodes(root, d - 1);
lastNodes
.filter(node => node)
.forEach(node => {
if (node) {
const insertNodeL = new TreeNode(v);
const insertNodeR = new TreeNode(v);
insertNodeL.left = node.left;
insertNodeR.right = node.right;
node.left = insertNodeL;
node.right = insertNodeR;
}
})
return root;
};
最后,依然是曾大师的 go 语言 show time, 他同样是递归的思路哦~
func addOneRow(root *TreeNode, v int, d int) *TreeNode {
head := root
// 跟节点换掉
if(d==1){
trRoot := new(TreeNode)
trRoot.Val = v
trRoot.Left= root
trRoot.Right =nil
return trRoot;
}
inorderDepth(root,1,d,v)
return head
}
func inorderDepth(root *TreeNode,depth int,respected int,v int) {
// d-1 层为空节点
if(root == nil){
return
}
if(respected == depth+1){
left := root.Left
right := root.Right
trLeft := new(TreeNode)
trLeft.Val = v
trLeft.Left=left
trLeft.Right = nil
root.Left = trLeft
trRight := new(TreeNode)
trRight.Val = v
trRight.Right=right
trRight.Left = nil
root.Right = trRight
return
}
// 当深度等于或者超过respected-1时,就不需要遍历了
if(respected < depth+1){
return
}
if(root != nil){
inorderDepth(root.Left,depth+1,respected,v)
inorderDepth(root.Right,depth+1,respected,v)
}
}
结束
二叉树是相对链表更复杂一点的数据结构。它的典型用法是对节点定义一个标记函数,将一些值与每个节点相关系。这样标记的二叉树就可以实现二叉搜索树和二叉堆,并应用于高效率的搜索和排序。了解这些数据结构会有助于我们实现更高效的程序哦~下周见!
三人行