102. 二叉树的层序遍历

思路:使用队列。

/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
} Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root); while (!queue.isEmpty()) {
// 当前层的结点数目
int size = queue.size();
List<Integer> rowList = new LinkedList<>(); // 每个结点出队时,只要孩子结点不为空就加入队列
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
rowList.add(node.val); if (node.left != null) {
queue.offer(node.left);
} if (node.right != null) {
queue.offer(node.right);
}
} res.add(rowList);
} return res;
}
}

104. 二叉树的最大深度

思路:递归,根据定义编写代码。

/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
//base case
if (root == null) {
return 0;
} return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}

105. 从前序与中序遍历序列构造二叉树

/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
} private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
//base case
if (preStart > preEnd || inStart > inEnd) {
return null;
} int rootVal = preorder[preStart];
// 该步骤可以通过map优化
int index = findIndexInoreder(rootVal, inorder); int leftCnt = index - inStart;
TreeNode root = new TreeNode(rootVal); root.left = build(preorder, preStart + 1, preStart + leftCnt, inorder, inStart, index - 1);
root.right = build(preorder, preStart + leftCnt + 1, preEnd, inorder, index + 1, inEnd); return root;
} private int findIndexInoreder(int rootVal, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
if (rootVal == inorder[i]) {
return i;
}
} throw new IllegalArgumentException("can not find :" + rootVal);
}
}

通过Map优化:

class Solution {
// 通过 map 存储中序序列中各个元素在数组中的下标
private Map<Integer, Integer> map = new HashMap<>(); public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
} return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
} private TreeNode build(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
//base case
if (preStart > preEnd || inStart > inEnd) {
return null;
} int rootVal = preorder[preStart];
int index = map.get(rootVal); int leftCnt = index - inStart;
TreeNode root = new TreeNode(rootVal); root.left = build(preorder, preStart + 1, preStart + leftCnt, inorder, inStart, index - 1);
root.right = build(preorder, preStart + leftCnt + 1, preEnd, inorder, index + 1, inEnd); return root;
}
}

114. 二叉树展开为链表

思路:递归:向将左右孩子分别展平,然后先将展平后的左子树连接右孩子上,再将展平后的右子树连接上。

/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
flat(root);
} private TreeNode flat(TreeNode root) {
//base case
if (root == null) {
return null;
} // 先分别将左右子树展平并返回头结点
TreeNode left = flat(root.left);
TreeNode right = flat(root.right); // 将左子树置为空
root.left = null; // 先将展平后的左子树连接在右孩子上
root.right = left; // 找到右子树的最右一个结点
TreeNode temp = root;
while (temp.right != null) {
temp = temp.right;
} // 连接展平后的右子树
temp.right = right; return root;
}
}

121. 买卖股票的最佳时机

思路:动态规划。

股票买卖问题通用解法:

状态:dp[i][k][0],在第i天结束时,允许交易k次的情况下,最终不持有股票能获得的最大利润;dp[i][k][1],在第i天结束时,允许交易k次的情况下,最终持有股票能获得的最大利润。

base case:dp[-1][k][0] = 0、dp[-1][k][1] = -Infinitydp[i][0][0] = 0、dp[i][0][1] = -Infinity。其中dp[-1][k][1] = dp[i][0][1] = -Infinity 表示在没有进行股票交易时不允许持有股票。备注:Java中可以用Integer.MIN_VALUE来表示-Infinity

转移方程:dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]) dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i])。前者表示:第i天不持有股票可能是前一天就不持有,或者前一天持有但今天卖出去了。后者表示:第i天持有股票可能是前一天就持有,或者前一天不持有但今天买入了。(这里选择买入时消耗一次交易机会

最终最大利润是dp[len - 1][k][0],因为结束时持有 0 份股票的收益一定大于持有 1 份股票的收益。其中len表示prices数组长度。

根据通用解法有:

dp[i][k][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i])

dp[i][k][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i]) = max(dp[i - 1][1][1], -prices[i]) ,其中 dp[i][0][0] = 0。其中 k始终为1,可以简化:

class Solution {
public int maxProfit(int[] prices) {
int len = prices.length; // dp[i][0] 表示第i天,不持有股票获得的最大收益(允许交易一次)
// dp[i][1] 表示第i天,持有股票获得的最大收益
int[][] dp = new int[len][2]; // base case
// 第0天不持有股票所能获得的最大利润
dp[0][0] = 0;
// 第0天持有股票所能获得的最大利润
dp[0][1] = -prices[0]; // 状态转移
for (int i = 1; i < len; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
} // 肯定是不持有股票时利润最大
return dp[len - 1][0];
}
}

由于第 i 天的最大收益只和第 i - 1 天的最大收益相关,空间复杂度可以由O(n) 降到 O(1)。空间优化:

class Solution {
public int maxProfit(int[] prices) {
int len = prices.length; //base case
int dp0 = 0;
int dp1 = -prices[0]; //状态转移
for (int i = 1; i < len; i++) {
dp0 = Math.max(dp0, dp1 + prices[i]);
dp1 = Math.max(dp1, -prices[i]);
} return dp0;
}
}

推荐题解:股票问题系列通解(转载翻译)

124. 二叉树中的最大路径和

思路:递归

/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxPathSum(TreeNode root) {
maxSumToDescendant(root);
return maxPathSum;
} //先初始化为最小值
private int maxPathSum = Integer.MIN_VALUE; // 结点到 子孙(不一定包含叶子结点) 结点的最大路径和(最少为其自身一个结点)
private int maxSumToDescendant(TreeNode root) {
if (root == null) {
return 0;
} // 小于0则认为对最大路径和没有贡献
int left = Math.max(0, maxSumToDescendant(root.left));
int right = Math.max(0, maxSumToDescendant(root.right)); maxPathSum = Math.max(maxPathSum, left + root.val + right); return root.val + Math.max(left, right);
}
}

128. 最长连续序列

class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int num : nums) {
set.add(num);
} int maxLen = 0;
for (int num : nums) {
// 找到包含 num 的连续序列的下边界
if (set.contains(num - 1)) {
continue;
} // 找到下边界后开始计算长度
int len = 1;
while (set.contains(num + 1)) {
len++;
num++;
} maxLen = Math.max(maxLen, len);
} return maxLen;
}
}

136. 只出现一次的数字

思路:位运算,每个元素依次异或。

i ^ 0 = i;

i ^ i = 0;

且异或满足交换律和结合律。

class Solution {
public int singleNumber(int[] nums) {
int res = 0; for (int num : nums) {
res = res ^ num;
} return res;
}
}

139. 单词拆分

思路:动态规划

class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> set = new HashSet<>();
for (String str : wordDict) {
set.add(str);
} int len = s.length(); // 状态: s 中前 i 个字符能否拆分成功
boolean[] dp = new boolean[len + 1]; // base case
dp[0] = true; // 状态转移
// s[0, i]能否被分割取决于:区间[j, i]是否属于set和dp[j]的值(前j个字符 [0, j - 1] 能否被分割),j <= i
for (int i = 1; i < len + 1; i++) {
for (int j = 0; j < i; j++) {
if (set.contains(s.substring(j, i)) && dp[j]) {
dp[i] = true;
break;
}
}
} return dp[len];
}
}

141. 环形链表

思路:快慢指针,若慢指针最终追上快指针说明有环

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
//没有或只有一个结点说明没有环
if (head == null || head.next == null) {
return false;
} ListNode slow = head;
ListNode fast = head.next; while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next; if (slow == fast) {
return true;
}
} return false; }
}

🔥 LeetCode 热题 HOT 100(41-50)的更多相关文章

  1. LeetCode 热题 HOT 100(05,正则表达式匹配)

    LeetCode 热题 HOT 100(05,正则表达式匹配) 不够优秀,发量尚多,千锤百炼,方可成佛. 算法的重要性不言而喻,无论你是研究者,还是最近比较火热的IT 打工人,都理应需要一定的算法能力 ...

  2. 🔥 LeetCode 热题 HOT 100(81-90)

    337. 打家劫舍 III 思路:后序遍历 + 动态规划 推荐题解:树形 dp 入门问题(理解「无后效性」和「后序遍历」) /** * Definition for a binary tree nod ...

  3. 🔥 LeetCode 热题 HOT 100(71-80)

    253. 会议室 II(NO) 279. 完全平方数 class Solution { public int numSquares(int n) { // dp[i] : 组成和为 i 的最少完全平方 ...

  4. 🔥 LeetCode 热题 HOT 100(51-60)

    142. 环形链表 II 思路:快慢指针,快慢指针相遇后,慢指针回到头,快慢指针步伐一致一起移动,相遇点即为入环点 /** * Definition for singly-linked list. * ...

  5. 🔥 LeetCode 热题 HOT 100(31-40)

    75. 颜色分类 思路:将 2 往后放,0 往前放,剩余的1自然就放好了. 使用双指针:left.right 分别指向待插入的 0 和 2 的位置,初始 left 指向数组头,right 指向数组尾部 ...

  6. 🔥 LeetCode 热题 HOT 100(21-30)

    46. 全排列 思路:典型回溯法 class Solution { public List<List<Integer>> permute(int[] nums) { Linke ...

  7. 🔥 LeetCode 热题 HOT 100(61-70)

    207. 课程表 思路:根据题意可知:当课程之间不存在 环状 循环依赖时,便能完成所有课程的学习,反之则不能.因此可以将问题转换成: 判断有向图中是否存在环.使用 拓扑排序法 : 构建 入度表:记录每 ...

  8. 🔥 LeetCode 热题 HOT 100(11-20)

    20. 有效的括号 class Solution { public boolean isValid(String s) { Map<Character, Character> map = ...

  9. 🔥 LeetCode 热题 HOT 100(1-10)

    1. 两数之和 思路一:暴力遍历所有组合 class Solution { public int[] twoSum(int[] nums, int target) { for (int i = 0; ...

随机推荐

  1. 支持向量机(SVM)之硬阈值

    支持向量机 ( support vector machine, SVM ) 是使用超平面来对给定的 p 维向量进行分类的非概率二元线性分类器. 一.超平面 ( hyperplane ) 在一个p维的输 ...

  2. 使用Spring Data JPA 访问 Mysql 数据库-配置项

    jpa操作数据库 注意:数据库采用的是本机数据库,下面是建表语句及初始化数据: SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------- ...

  3. 【spring源码系列】之【Bean的生命周期】

    为源码付出的每一分努力都不会白费. 1. Bean的实例化概述 前一篇分析了BeanDefinition的封装过程,最终将beanName与BeanDefinition以一对一映射关系放到beanDe ...

  4. 混沌工程之ChaosToolkit使用之一删除K8s POD

    今天我们来玩一下混沌工程的开源工具chaostoolkit . 它的目标是提供一个免费,开放,社区驱动的工具集以及api. 官方源码链接:https://github.com/chaostoolkit ...

  5. 『无为则无心』Python序列 — 22、Python集合及其常用操作

    目录 1.Python集合特点 2.Python集合的创建 3.操作集合常用API (1)增加数据 @1.add()方法 @2.update()方法 (2)删除数据 @1.remove()方法 @2. ...

  6. 跟我一起学Go系列:Go gRPC 安全认证方式-Token和自定义认证

    Go gRPC 系列: 跟我一起学Go系列:gRPC安全认证机制-SSL/TLS认证 跟我一起学 Go 系列:gRPC 拦截器使用 跟我一起学 Go 系列:gRPC 入门必备 接上一篇继续讲 gRPC ...

  7. 码云使用svn无法提交空文件夹

    错误信息: svn: E200015: Commit failed (details follow): svn: E200015: Empty directories is not supported ...

  8. kafka 安装和配置

    转载自:https://www.cnblogs.com/heijinli/p/13545182.html 下载及安装  第一步:进入kafka官网  按照自己的需求选择版本,我这里选择 最新版的 2. ...

  9. IntelliJ idea -- 在WEB-INF下创建两个文件夹:classes 和 lib

    1.首先在WEB-INF下面创建两个文件夹 classes  和 lib 2.文件 --> 项目结构 3.选择路径 4.选择依赖项 5.选择刚创建好的lib文件夹,然后确定 6.选择 Jar D ...

  10. 9 shell 退出状态

    退出状态和逻辑运算符的组合 Shell 逻辑运算符 举栗 命令的退出状态(exit statu)是指执行完Linux命令或shell函数后,该命令或函数返回给调用它的程序的一个比较小的整数值.if 语 ...