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. noip2006总结

    T1 能量项链 原题 在Mars星球上,每个Mars人都随身佩带着一串能量项链.在项链上有N颗能量珠.能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数.并且,对于相邻的两颗珠子,前一颗珠子 ...

  2. 每日三道面试题,通往自由的道路4——JVM篇

    茫茫人海千千万万,感谢这一秒你看到这里.希望我的面试题系列能对你的有所帮助!共勉! 愿你在未来的日子,保持热爱,奔赴山海! 每日三道面试题,成就更好自我 昨天既然你有讲到字符串常量池是吧,那这样吧 1 ...

  3. Golang中的各种时间操作

    Golang中的各种时间操作 需求 时间格式的转换比较麻烦,自己写了个工具,可以通过工具中的这些方法相互调用转成自己想要的格式,代码如下,后续有新的函数再添加 实现代码 package utils i ...

  4. count、counta函数巧妙运用于合并单元格填充序号

    函数运用: 1.COUNT(value1,value2, ...)      value1 是必需参数. 要计算其中数字的个数的第一项.单元格引用或区域.      value2, ... 为可选参数 ...

  5. Dagger2入门,以初学者角度

    2016-12-21 更新:添加@Subcomponent注解以及Lazy与Provider的使用,本文基本完结!如果有好的建议请提出,感谢大家的支持,谢谢 依赖注入 Dagger2是Android中 ...

  6. 『心善渊』Selenium3.0基础 — 14、Selenium对单选和多选按钮的操作

    目录 1.页面中的单选按钮和多选按钮 2.判断按钮是否选中is_selected() 3.单选按钮的操作 4.多选按钮的操作 5.选择部分多选按钮的操作 1.页面中的单选按钮和多选按钮 页面中的单选按 ...

  7. Dockerfile优化方式

    如今GitHub 仓库中已经包含了成千上万的Dockerfile,但并不是所有的Dockerfile都是高效的.本文将从四个方面来介绍Dockerfile的最佳实践,以此来帮助大家编写更优雅的Dock ...

  8. 配置Oracle遇到问题<一>

    1, 将D:\app\product\11.2.0\dbhome_1\NETWORK复制到D:\app\product\instantclient_11_2.为了处理: 12154错误,不过没有解决. ...

  9. XCTF logmein

    一.查壳 发现是64位的Linux文件(ELF可以看出是linux的文件) 二.拖入ida64,静态分析 注意这里两个坑: 1.strcpy是复制字符串的意思,前面定义的v8数组只有8个,但是后面的字 ...

  10. 2013年第四届蓝桥杯C/C++程序设计本科B组省赛 马虎的算式

    题目描述 马虎的算式 小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了. 有一次,老师出的题目是:36 x 495 = ? 他却给抄成了:396 x 45 = ? 但结果却很戏剧性,他的答 ...