题目
题目来源:力扣(LeetCode)
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例 1:
输入: root = [2,1,3]
输出: 1
示例 2:
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7
思路分析
前序遍历 + 回溯 (递归)
- 我们使用前序遍历,优先进行左边搜索,判断当前是否是最大深度,当前结点是否是最左边的结点。
- 我们可以设置2个全局变量,maxLevel用来记录最大深度,curLevel用来记录当前深度。当结点是叶子结点,且其所 深度比已记录的最大深度大时,我们就更新最左值和最大深度值。
- 同深度下只会进行一次值的更新,由于是前序遍历,这唯一一次更新的最左值就是此深度下最左边的 值。
- 这样递归完成后,我们就已经找到这颗树最左下角的值了。
- 在找最大深度的时候,递归的过程中依然要使用回溯。
/**
* 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 permute = function (nums) {
// 保存结果数组(保存每个路径(排列))
const result = [];
// 执行回溯函数
backtrack(nums, []);
return result;
// 定义回溯递归函数
// track 用来存储路径,定义为一个栈
function backtrack(nums, track) {
// 递归出口
// 当收集元素的数组track的大小达到和nums数组一样大的时候,说明找到了一个全排列,也表示到达了叶子节点,将路径推入结果数组,并返回
if (track.length === nums.length) {
result.push(track);
return;
}
// 遍历候选字符
for (let i = 0; i < nums.length; i++) {
// 如果当前字符在路径中已经被使用过了,则跳过当前字符,进入下一轮循环
if (track.includes(nums[i])) { continue }
// 这里说明这个数还没有被使用,入栈 track
track.push(nums[i]);
const newTrack = [...track];
// 继续递归填下一个数
backtrack(nums, newTrack);
// 回溯【状态重置】撤销之前的操作
track.pop();
}
}
}
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
// 保存结果数组,保存每个路径(排列)
const result = []
// 调用回溯函数,传入参数
backtracking(nums, nums.length, [], [])
// 返回结果数组
return result
// 定义回溯递归函数,传入数组,长度,节点是否被使用过的数组
// used 用来标记节点是否被用过 path 用来存储路径,定义为一个栈
function backtracking(nums, len, used, path) {
// 递归出口
// 如果到达叶子节点,将路径推入结果数组,并返回
if (path.length === len) {
result.push([...path])
return
}
// 遍历候选字符
for (let i = 0; i < len; i++) {
// 使用过就下一轮循环
if (!used[i]) {
// undefind和fasle都会进来
// 这里说明这个数还没有被使用,入栈path
path.push(nums[i])
// 标记这个数被使用过了
used[i] = true
// 开始进行递归
backtracking(nums, len, used, path)
// 回溯【状态重置】撤销之前的操作
path.pop()
used[i] = false
}
}
}
};
var findBottomLeftValue = function (root) {
// 记录最大深度,初值设置为负无穷,方便比较
let maxLevel = -Infinity,
// 当前深度
curLevel = 0,
// 最左值
maxLeftVal = 0
// 前序遍历
let preOrderTraversal = function (node) {
// 如果结点不存在则返回
if (!node) return
// 当前深度递增
curLevel++
// 当结点是叶子结点,且当前深度最大时,它便是树最左下角的结点。
// 前序遍历优先搜索左边的值,同深度下,最左边的结点最先被搜索到
// 同深度下,此判断语句内的代码只会被执行一次
if (curLevel > maxLevel && !node.left && !node.right) {
// 记录最大深度
maxLevel = curLevel
// 记录最左值
maxLeftVal = node.val
}
// 遍历左子树
node.left && preOrderTraversal(node.left)
// 遍历右子树
node.right && preOrderTraversal(node.right)
// 回溯,深度递减
curLevel--
}
// 从根结点开始向下遍历
preOrderTraversal(root)
return maxLeftVal
};