碎碎念:加油!!!
参考:代码随想录
513.找树左下角的值
题目链接
思想
本题可以使用递归法,也可以使用层序遍历迭代法。
**递归法:**可以使用前序遍历,前序遍历的顺序为左中右,也就是说,我们找到的第一个深度最大的叶子节点应该就是树左下角的值。
递归三部曲:
- 确定参数和返回值
- 终止条件:遍历到叶子节点(左孩子和右孩子都为空),比较深度,修改result。
- 单层逻辑:向左遍历,注意深度要回溯;向右遍历,注意回溯;
本题没有对中的处理逻辑,所以本题前中后序都可以,本题只强调左在右的前面。
也可以把回溯的过程隐藏在参数里面。
层序遍历迭代法:
只需要记录最后一行的第一个节点的数值
题解
// cpp 递归法
class Solution {
public:
int result;
int maxDepth = INT_MIN;
void traversal(TreeNode* root, int depth){
if (root->left == NULL && root->right == NULL) { // 如果是叶子节点,就比较一下是否是最深的叶子节点
if (depth > maxDepth) {
maxDepth = depth;
result = root->val;
}
return;
}
if (root->left) {
depth++;
traversal(root->left, depth);
depth--; // 回溯
}
if (root->right) {
depth++;
traversal(root->right, depth);
depth--;
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
# python 迭代法 精简写法
class Solution:
def traversal(self, node, depth):
if node.left == None and node.right == None:
if depth > self.maxDepth:
self.result = node.val
self.maxDepth = depth
return
if node.left:
self.traversal(node.left, depth+1) # 这句暗含了回溯
if node.right:
self.traversal(node.right, depth+1) # depth+1暗含了回溯
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
self.result = None
self.maxDepth = float('-inf')
self.traversal(root, 0)
return self.result
// cpp 迭代法 层序遍历
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (i == 0) result = node->val; // 记录每一层的第一个节点的值,最后更新会得到最后一层的第一个节点的值
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
# python 迭代法 层序遍历
class Solution:
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0
queue = deque()
queue.append(root)
result = 0
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
if i == 0:
result = node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
反思
注意:最靠左侧的节点不一定是左孩子。
112. 路径总和
题目链接
思想
本题使用前中后序都可以。
递归三部曲:
- 参数和返回值:找到一条路径立刻返回,所以返回值定义成bool类型,返回值设置一个计数器,还有节点。
- 终止条件:遇到叶子节点时当前的路径是否符合题意。
- 单层递归逻辑:左:向左递归,记得回溯;右:向右递归,记得回溯。要判断一下返回值,如果返回true,要把true结果继续向上返回。
关于是否需要返回值:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径就要及时返回。
本题是第三种情况,找到一条符合条件的路径就要返回。
题解
// cpp
class Solution {
public:
bool traversal(TreeNode* cur, int count) {
// 遍历到叶子节点,且count减到了0
if (!cur->left && !cur->right && count == 0) return true;
if (!cur->left && !cur->right) return false;
if (cur->left) {
count -= cur->left->val;
if (traversal(cur->left, count)) return true;
count += cur->left->val; // 回溯
}
if (cur->right) {
count -= cur->right->val;
if (traversal(cur->right, count)) return true;
count += cur->right->val; // 回溯
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return false;
return traversal(root, targetSum - root->val);
}
};
# python 精简写法
class Solution:
def traversal(self, cur, count):
if not cur.left and not cur.right and count == 0:
return True
if not cur.left and not cur.right:
return False
if cur.left:
if self.traversal(cur.left, count - cur.left.val): return True
if cur.right:
if self.traversal(cur.right, count - cur.right.val): return True
return False
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
return self.traversal(root, targetSum - root.val)
# python 再精简一点!!
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
if not root.left and not root.right and targetSum == root.val:
return True
return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)
反思
本题没有中间的处理,所以前中后序都行,我们不关心中间。
113. 路径总和 II
题目链接
思想
本题和上一题很相似,区别在于本题要找出所有符合条件的路径。本题要搜索整棵二叉树,递归函数不需要返回值。
题解
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) {
result.push_back(path);
return;
}
if (!cur->left && !cur->right) return;
if (cur->left) {
path.push_back(cur->left->val);
count -= cur->left->val;
traversal(cur->left, count);
count += cur->left->val; // 回溯
path.pop_back(); // 回溯
}
if (cur->right) {
path.push_back(cur->right->val);
count -= cur->right->val;
traversal(cur->right, count);
count += cur->right->val; // 回溯
path.pop_back(); // 回溯
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
result.clear();
path.clear();
if (root == NULL) return result;
path.push_back(root->val);
traversal(root, targetSum - root->val);
return result;
}
};
class Solution:
def __init__(self):
self.path = []
self.result = []
def traversal(self, cur, count):
if not cur.left and not cur.right and count == 0:
self.result.append(self.path[:]) #path[:] 表示从列表 path 的起始位置到结束位置(即整个列表)的一个浅拷贝(shallow copy)
return
if not cur.left and not cur.right:
return
if cur.left:
self.path.append(cur.left.val)
self.traversal(cur.left, count - cur.left.val)
self.path.pop()
if cur.right:
self.path.append(cur.right.val)
self.traversal(cur.right, count - cur.right.val)
self.path.pop()
return
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
if not root:
return self.result
self.path.append(root.val)
self.traversal(root, targetSum - root.val)
return self.result
反思
从上面两道题中感受搜索整棵树和某一条路径的差别。
python:self.path 是一个可变对象(如列表),而每次调用 self.result.append(self.path) 时,实际上是将 self.result 列表中添加了 self.path 的引用而不是副本。可以通过使用 self.path[:] 或者 list(self.path) 来实现浅拷贝。
106.从中序与后序遍历序列构造二叉树
题目链接
思想
明确中序和后序的遍历顺序。
中序和前后序遍历中的任意一个都可以唯一确定一个二叉树。

递归三部曲:
- 递归函数的参数和返回值:参数两个数组,返回值是节点
- 终止条件:后序为空,return NULL;
2.1. 后序数组的size为0,返回空节点
2.2. 后序数组最后一个元素为节点元素
2.3. 寻找中序数组位置作为切割点
2.4. 切割中序数组:用的index切
2.5. 切割后序数组:用切完中序数组得到的左中序数组的大小、右中序数组的大小切。切割得到左后序数组,右后序数组。 - 单层逻辑:递归处理左中序、左后序;递归处理右中序、右后序。
题解
// cpp
class Solution {
public:
TreeNode* traversal (vector<int>& inorder, vector<int>& postorder){
// 2.1 后序数组的size为0,返回空节点
if (postorder.size() == 0) return NULL;
// 2.2 后序数组最后一个元素为节点元素
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorder.size() == 1) return root;
// 2.3 寻找中序数组位置作为切割点
int delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 2.4 切割中序数组:用delimiterIndex切,得到左中序和右中序,左闭右开
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());
// 2.5 切割后序数组:用左中序和右中序的大小切,得到左后序和右后序
postorder.resize(postorder.size() - 1); // 舍弃末尾元素,因为这个元素是中间节点,已经用过了
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
// 3 单层逻辑
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, postorder);
}
};
# python
class Solution:
def traversal(self, inorder, postorder):
if not postorder:
return None
rootValue = postorder[-1]
root = TreeNode(rootValue)
delimiterIndex = inorder.index(rootValue)
leftInorder = inorder[:delimiterIndex]
rightInorder = inorder[delimiterIndex+1:]
leftPostorder = postorder[:len(leftInorder)]
rightPostorder = postorder[len(leftInorder):len(postorder) - 1]
root.left = self.traversal(leftInorder, leftPostorder)
root.right = self.traversal(rightInorder, rightPostorder)
return root
def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
if len(inorder) == 0 or len(postorder) == 0:
return None
return self.traversal(inorder, postorder)
反思
注意切数组的时候要有一个规定的切法,比如规定左闭右开。
388

被折叠的 条评论
为什么被折叠?



