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. Ajax 和 JSON

    Ajax:异步更新页面的技术,必须在http或者https网络网络协议下使用             1.原生js实现:共4步             2.jQuery:$.ajax(配置对象);   ...

  2. NOIP模拟测试28「阴阳·虎·山洞」

    写这几个题解我觉得我就像在按照官方题解抄一样 阴阳 题解 将题目中给的阴阳看作黑色和白色 首先我们观察到最后生成图中某种颜色必须是竖着单调递增或竖着单调递减 类似这样 否则不满足这个条件 但合法染色方 ...

  3. 跟着官方文档学Maven构建生命周期

    在IntelliJ IDEA中,显示了Maven的Lifecycle: 只需要学习这些命令,就能构建一个Maven项目. 三个内置生命周期 Maven内置了三个生命周期:clean.default和s ...

  4. SpringBoot实战:10分钟快速搞定环境

    什么是 springboot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程. 该框架使用了特定的方式来进行配置,从而使开发人 ...

  5. 解决两个相邻的span,或者input和button中间有间隙,在css中还看不到

    <span id="time"></span><span id="second"></span> <inp ...

  6. 『动善时』JMeter基础 — 50、使用JMeter测试WebService接口

    目录 1.什么是WebService 2.WebService和SOAP的关系 3.什么是WSDL 4.测试WebService接口前的准备 (1)如何判断是WebService接口 (2)如何获取W ...

  7. js笔记12

    1.元素的属性 div.attributes是所有标签属性构成的数组集合 div.classList是所有class名构成的数组集合 在classList的原型链上可以看到add()和remove() ...

  8. Linux用户体系

    1.系统中和用户相关的文件 (1)/etc/passwd:记录系统用户信息文件 (2)/etc/shadow:系统用户密码文件 (3)/etc/group:组用户信息文件 (4)/etc/gshado ...

  9. 21、linux系统grup和bios加密

    21.1.grup加密(加密单用户模式): 1.使用加密算法对grup密码进行加密: 2.修改grup.conf配置文件: 21.2.bios加密(因为救援模式可以破解grup加密,所以使用bios加 ...

  10. 【问答分享第一弹】MySQL锁总结:MySQL行锁、表锁、排他锁、共享锁的特点

    大家好,我是小于哥哈.前几天能分享了第一期面试题,MySQL 中有哪几种锁 和 这些锁各有哪些特点 ,这道面试题是经常会被问到的一个面试题,大家反馈的都挺不错的.今天特此来总结一下. 首发于公众号[终 ...