题目
题目来源:力扣(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 }// 这里说明这个数还没有被使用,入栈 tracktrack.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都会进来// 这里说明这个数还没有被使用,入栈pathpath.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};
