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

    统计数字 原题 某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*10^9).已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺 ...

  2. RobotFramework + Python 自动化入门 四 (Web进阶)

    在<RobotFramwork + Python 自动化入门 一>中,完成了一个Robot环境搭建及测试脚本的创建和执行. 在<RobotFramwork + Python 自动化入 ...

  3. 计算机网络的参考模型与5G协议

     一.分层思想 二.OSI七层参考模型 三.FPC/IP五层模型 四.数据的封装过程与PDU(协议数据单元) 五.数据的解封装过程 六.各层间通信与设备与层的对应关系 七.总结 一.分层思想 将复杂的 ...

  4. Redisson 分布式锁源码 02:看门狗

    前言 说起 Redisson,比较耳熟能详的就是这个看门狗(Watchdog)机制. 本文就一起看看加锁成功之后的看门狗(Watchdog)是如何实现的? 加锁成功 在前一篇文章中介绍了可重入锁加锁的 ...

  5. Unity3D学习笔记2——绘制一个带纹理的面

    目录 1. 概述 2. 详论 2.1. 网格(Mesh) 2.1.1. 顶点 2.1.2. 顶点索引 2.2. 材质(Material) 2.2.1. 创建材质 2.2.2. 使用材质 2.3. 光照 ...

  6. Linux下的SSH,你知道多少?

    Openssh介绍 OpenSSH 是 SSH (Secure Shell) 协议的免费开源实现.SSH协议族可以用来进行远程控制, 或在计算机之间传送文件.而实现此功能的传统方式,如telnet(终 ...

  7. 删除有序数组中的重复项II

    题目描述 给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素最多出现两次,返回删除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组 并在使用O(1)额外空间的条件下 ...

  8. mac系统终端sudo免输入密码

    p.p1 { margin: 0; font: 12px ".PingFang SC" } p.p2 { margin: 0; font: 12px "Helvetica ...

  9. git使用---安装,提交,回退,修改,分支,标签等

    下面是对git的各种使用及命令的基础使用,来自廖雪峰老师的git教程,这个收录下,作为git的使用总结. github上面地址为:https://github.com/Zhangguoliu/lear ...

  10. Python如何设计面向对象的类(下)

    本文将在上篇文章二维向量Vector2d类的基础上,定义表示多维向量的Vector类. 第1版:兼容Vector2d类 代码如下: from array import array import rep ...