算法题
Leetcode 513.找树左下角的值
题目链接:513.找树左下角的值
大佬视频讲解:找树左下角的值视频讲解
个人思路
翻译一下题目就是要在树的最后一行找到最左边的值。使用层序遍历记录最后一行第一个节点的数值就可以了。
解法
递归法
可以用递归法,其中深度最大的叶子节点一定是最后一行。对于最左边的值,保证左节点第一个被遍历就行,所以使用前序遍历,保证优先左边搜索,记录深度最大的叶子节点,就找到了树的最后一行最左边的值。
1.确定递归函数的参数和返回值
参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度和result记录最大深度最左节点的数值。
2.确定终止条件
当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
3.确定单层递归的逻辑
因为需要找最大深度,所以递归的过程中依然要回溯
class Solution {
private int Deep = -1;
private int value = 0;
public int findBottomLeftValue(TreeNode root) {
value = root.val;
findLeftValue(root,0);
return value;
}
private void findLeftValue (TreeNode root,int deep) {
if (root == null) return;//终止条件
if (root.left == null && root.right == null) {//找叶子节点
if (deep > Deep) {
value = root.val;//记录最左下角值
Deep = deep;//记录最深层数
}
}
if (root.left != null) findLeftValue(root.left,deep + 1);//先遍历左节点
if (root.right != null) findLeftValue(root.right,deep + 1);
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(递归树的高度h)
迭代法
依旧是层序遍历的模板,加上对于左下角值的判断即可;
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int res = 0;
while (!queue.isEmpty()) {
int size = queue.size();//每层节点数
for (int i = 0; i < size; i++) {
TreeNode poll = queue.poll();
if (i == 0) {//最左边的节点;这样一直遍历到最底层,只会保存树左下角的节点的值
res = poll.val;
}
if (poll.left != null) {
queue.offer(poll.left);
}
if (poll.right != null) {
queue.offer(poll.right);
}
}
}
return res;
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(一个队列的空间)
Leetcode 112. 路径总和
题目链接:112. 路径总和
大佬视频讲解:路径总和视频讲解
个人思路
和257. 二叉树的所有路径 有点想象,只不过这里是返回是否 有路径结果符合值,可以用递归,将需要查询的值,一层层减下去,一直到递归完叶子节点还没找到结果就返回false;
解法
递归法
递归三步走;
1.确定递归函数的参数和返回类型
遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,用bool类型表示。
2.确定终止条件
让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。
如果最后count == 0,同时到了叶子节点的话,说明找到了目标和。
如果遍历到了叶子节点,count不为0,就是没找到。
3.确定单层递归的逻辑
终止条件是判断叶子节点,递归的过程中不要让空节点进入递归。
class solution {
public boolean hasPathSum(treenode root, int targetsum) {
if (root == null) {
return false;
}
targetsum -= root.val;//最后结果值
// 判断是否为叶子结点
if (root.left == null && root.right == null) {
return targetsum == 0;
}
if (root.left != null) {
boolean left = hasPathSum(root.left, targetsum);
if (left) { return true;}// 已经找到
}
if (root.right != null) {
boolean right = haspathsum(root.right, targetsum);
if (right) {return true; }// 已经找到
}
return false;
}
}
时间复杂度:O(n);(最差遍历一遍树)
空间复杂度:O(n);(递归树的高度h)
迭代法
用迭代法的话只能使用栈模拟递归;此时需要两个栈,一个要记录该节点指针,一个记录从头结点到该节点的路径数值总和。
class solution {
public boolean haspathsum(treenode root, int targetsum) {
if(root == null) return false;
stack<treenode> stack1 = new stack<>();
stack<integer> stack2 = new stack<>();
stack1.push(root);//记录节点
stack2.push(root.val);//记录值的总和
while(!stack1.isempty()) {
int size = stack1.size();
for(int i = 0; i < size; i++) {
treenode node = stack1.pop();
int sum = stack2.pop();
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
if(node.left == null && node.right == null && sum == targetsum) {
return true;
}
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if(node.right != null){
stack1.push(node.right);
stack2.push(sum + node.right.val);
}
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if(node.left != null) {
stack1.push(node.left);
stack2.push(sum + node.left.val);
}
}
}
return false;
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(使用两个栈,一个节点,一个存值)
Leetcode 113. 路径总和ii
题目链接:113. 路径总和ii
个人思路
理解了路径总和,那么这道题逻辑也差不多,只不过这道题需要返回符合结果的 路径
解法
递归法
和上面那题思路差不多,只不过找到结果需要存值;
class solution {
public List<List<Integer>> pathsum(TreeNode root, int targetsum) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res; // 非空判断
List<Integer> path = new LinkedList<>();
preorderdfs(root, targetsum, res, path);
return res;
}
public void preorderdfs(TreeNode root, int targetsum, List<List<Integer>> res, List<Integer> path) {
path.add(root.val);
// 遇到了叶子节点
if (root.left == null && root.right == null) {
// 找到了和为 targetsum 的路径
if (targetsum - root.val == 0) {
res.add(new ArrayList<>(path));
}
return; // 如果和不为 targetsum,返回
}
if (root.left != null) {
preorderdfs(root.left, targetsum - root.val, res, path);
path.remove(path.size() - 1); // 回溯
}
if (root.right != null) {
preorderdfs(root.right, targetsum - root.val, res, path);
path.remove(path.size() - 1); // 回溯
}
}
}
时间复杂度:O(n);(最差遍历一遍树)
空间复杂度:O(n);(递归树的高度h)
迭代法
这里迭代和上一题一样,比较麻烦,方法一样,用的是DFS统一迭代法(加入空节点作为标记的方法);
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> result = new ArrayList<>();//存结果
Stack<TreeNode> nodeStack = new Stack<>();//存节点
Stack<Integer> sumStack = new Stack<>();//存值
Stack<ArrayList<Integer>> pathStack = new Stack<>();//存结果中的路径
if(root == null) return result;
nodeStack.add(root);
sumStack.add(root.val);
pathStack.add(new ArrayList<>());
while(!nodeStack.isEmpty()){
TreeNode currNode = nodeStack.peek();
int currSum = sumStack.pop();
ArrayList<Integer> currPath = pathStack.pop();
if(currNode != null){
nodeStack.pop();
nodeStack.add(currNode);
nodeStack.add(null);
sumStack.add(currSum);
currPath.add(currNode.val);
pathStack.add(new ArrayList(currPath));//当前路径
if(currNode.right != null){
nodeStack.add(currNode.right);
sumStack.add(currSum + currNode.right.val);
pathStack.add(new ArrayList(currPath));
}
if(currNode.left != null){
nodeStack.add(currNode.left);
sumStack.add(currSum + currNode.left.val);
pathStack.add(new ArrayList(currPath));
}
}else{
nodeStack.pop();
TreeNode temp = nodeStack.pop();
//判断是否为叶子节点 和结果是否符合
if(temp.left == null && temp.right == null && currSum == targetSum)
result.add(new ArrayList(currPath));//存储结果
}
}
return result;
}
}
时间复杂度:O(n);(遍历整棵树)
空间复杂度:O(n);(两个栈,一个路径一个结果,都不会超过n)
Leetcode 106.从中序与后序遍历序列构造二叉树
题目链接:106.从中序与后序遍历序列构造二叉树
大佬视频讲解:从中序与后序遍历序列构造二叉树视频讲解
个人思路
思路不清晰时,先画图回顾一下怎么构造二叉树
解法
可以一层层切割,这就用到递归法;分步骤来看;
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组
第五步:切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间
递归法
在切割时,边界值需要确定下来。这里使用左闭右开。
首先要切割中序数组
切割点在后序数组的最后一个元素,用这个元素来切割中序数组的;而且中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割。
接下来切割后序数组
首先后序数组的最后一个元素指定不能要了,这是切割点。
此时有一个很重的点,就是中序数组大小一定是和后序数组的大小相同的(这是必然)。
中序数组都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。
class Solution {
Map<Integer, Integer> map; // 方便根据数值查找位置
public TreeNode buildTree(int[] inorder, int[] postorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, postorder,0, postorder.length);
}
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
// 参数里的范围都是左闭右开
if (inBegin >= inEnd || postBegin >= postEnd) {
// 不满足左闭右开,说明没有元素,返回空树
return null;
}
int rootIndex = map.get(postorder[postEnd - 1]); // 找到后序遍历的最后一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[rootIndex]); // 构造结点
int lenOfLeft = rootIndex - inBegin; // 保存中序左子树个数,用来确定后序数列的个数
root.left = findNode(inorder, inBegin, rootIndex,
postorder, postBegin, postBegin + lenOfLeft);
root.right = findNode(inorder, rootIndex + 1, inEnd,
postorder, postBegin + lenOfLeft, postEnd - 1);
return root;
}
}
时间复杂度:O(n);(遍历二叉树)
空间复杂度:O(n);(递归树的高度h 和map映射)
Leetcode 105.从前序与中序遍历序列构造二叉树
题目链接:105.从前序与中序遍历序列构造二叉树
个人思路
解决了上面那道,这道就容易了;与前中序和后序构造二叉树类似,后序数组中的最后一个节点改为前序数组中的第一个节点即可;
解法
递归法
分如下几步
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取前序数组第一个元素作为节点元素。
第三步:找到前序数组第一个一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组
第五步:切割前序数组,切成前序左数组和前序右数组
第六步:递归处理左区间和右区间
class Solution {
Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
public TreeNode findNode(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd) {
// 参数里的范围都是左闭右开
if (preBegin >= preEnd || inBegin >= inEnd) {
// 不满足左闭右开,说明没有元素,返回空树
return null;
}
int rootIndex = map.get(preorder[preBegin]); // 找到前序遍历的第一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[rootIndex]); // 构造结点
int lenOfLeft = rootIndex - inBegin; // 保存中序左子树个数,用来确定前序数列的个数
root.left = findNode(preorder, preBegin + 1, preBegin + lenOfLeft + 1,
inorder, inBegin, rootIndex);
root.right = findNode(preorder, preBegin + lenOfLeft + 1, preEnd,
inorder, rootIndex + 1, inEnd);
return root;
}
}
时间复杂度:O(n);(遍历二叉树)
空间复杂度:O(n);(递归树的高度h 和map映射)
以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网