买卖股票的最佳时机(贪心 / DP)
最长回文子串(中心扩散法)
二叉树的锯齿形层序遍历(层序遍历+记忆)
二叉树的最近公共祖先(DFS)
全排列(简单记忆+搜索)
一 买卖股票的最佳时机
解决方法:贪心 / 动态规划
但对一次性买卖,其实两者考虑思路差不多。
贪心——随区间左端点固定,右端点开始遍历移动,确定带当前区间的最小值,同时比较每次:右端点值-当前最小值 的最大值
public int maxProfit(int[] prices) {
int res = 0;
int miv = Integer.MAX_VALUE;
for (int i = 0; i < prices.length; i++) {
miv = Math.min(miv, prices[i]);
res = Math.max(res, prices[i] - miv);
}
return res;
}
动态规划(其实思想与贪心一致(低买高卖)但是动态规划的格式)
dp[i][0]表示第i天的持有 = (第i-1天持有)dp[i-1][0] 或 (第i-1天不持有+第i天买入) -prices[i]
dp[i][1]表示第i天的不持有 = (第i-1天不持有)dp[i-1][1] 或 (第i-1天持有+第i天卖出) dp[i-1][0]+price[i]
显然dp[0]只能从dp[0]推出,因为只能买卖一次。
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length + 1][2];
dp[1][0] = -prices[0]; // 初始化,不如最小必然都是0
for (int i = 2; i <= prices.length; i++) {
dp[i][0] = Math.max(dp[i-1][0], -prices[i-1]); // 注意负号,此时买得少对应max
dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i-1]);
}
return dp[prices.length][1];
}
可以优化为一维,很简单:
public int maxProfit(int[] prices) {
int[] dp = new int[2];
dp[0] = -prices[0]; // 初始化,不如最小必然都是0
for (int i = 2; i <= prices.length; i++) {
dp[0] = Math.max(dp[0], -prices[i-1]); // 注意负号,此时买得少对应max
dp[1] = Math.max(dp[1], dp[0] + prices[i-1]);
}
return dp[1];
}
二 最长回文子串
这里采用中心扩散法,思路非常之节点,时间复杂度O(n^2)力扣上的时间空间都跑90%。
思路:从头节点遍历到尾节点的上一个节点(因为最糟糕的情况也是一个字母,所以不需要遍历到尾节点),然后把当前节点当做回文子串中心向两边扩散,或把当前节点和当前节点的下一个节点当回文子串向两边扩散,记录最长的边界差值,然后记录到两个位置上,以便最后读取回文子串。
class Solution {
public String longestPalindrome(String s) {
int start = 0;
int end = 0;
for (int i = 0; i < s.length() - 1; i++) {
int len1 = expandAroundCenter(s, i, i); // 奇数连续子串的情况
int len2 = expandAroundCenter(s, i, i + 1); // 偶数连续子串的情况
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2; // len就是当前节点最长子串的长度
end = i + len / 2;
}
}
return s.substring(start, end + 1); // 左闭右开读取
}
// 按left right同时向两边扩散,以判断是否回文(注意边界)
public int expandAroundCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
--left;
++right;
}
return right - left - 1;
}
}
三 二叉树的锯齿形层序遍历
非常之简单,其实就是二叉树的层序遍历加个每层布尔值判断。
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Deque<TreeNode> queue = new LinkedList<>();
queue.add(root);
boolean bo = true; // true代表左往右,false代表右往左
while (!queue.isEmpty()) {
List<Integer> list = new ArrayList<>();
int curSize = queue.size();
while (curSize-- > 0) {
if (bo) {
TreeNode node = queue.pollFirst();
list.add(node.val);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
} else {
TreeNode node = queue.pollLast();
list.add(node.val);
if (node.right != null) queue.addFirst(node.right);
if (node.left != null) queue.addFirst(node.left);
}
}
res.add(list);
bo = !bo;
}
return res;
}
}
四 二叉树的最近公共祖先
用DFS,以后序遍历的方式从底至顶找根节点比较。注意一个重要的条件:节点值唯一。
思路:深度搜索从底至顶搜索,用两个布尔值记录是否找到节点P/Q,若有一个节点找到,那么该递归层就会返回true,如果遇到另一个递归层也返回true,那么就已找到root,后续的另一边迭代层不可能再有true,因为p/q值唯一。如果刚好在迭代返回true时,p/q之一巧好在某一根节点,那么也找到root。每次迭代的布尔值即为当前节点值是否为p/q值或是否已经访问过p/q值。
class Solution {
TreeNode res = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
dfs(root, p, q);
return res;
}
public boolean dfs(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return false; // 有点后序遍历的意思,遍历到最底层开始找
boolean bol = dfs(root.left, p, q);
boolean bor = dfs(root.right, p, q);
// 前者针对已经找到两边为TRUE,后者针对根节点是p/q之一,而且注意所有节点的值是惟一的,pq不能相同
// 后者并不冲突,一旦两边同时达到,那么肯定是p/q根节点重合
if ((bol && bor) || ((root.val == p.val || root.val == q.val) && (bol || bor))) {
res = root;
}
// 找到p/q或前面已经找到p/q则返回true
return root.val == p.val || root.val == q.val || bol || bor;
}
}
五 全排列
存思路型的全排列,即用按for循环遍历,然后用Set或布尔值数组记录是否访问即可,思路清晰简单,速度能在85%左右,over,存储来说,布尔值数组会底一些,建议后者。
class Solution {
List<List<Integer>> res;
HashSet<Integer> set; // 两种方法,set判断或布尔值数组bo[]判断
List<Integer> list;
public List<List<Integer>> permute(int[] nums) {
res = new ArrayList<>();
set = new HashSet<>();
list = new ArrayList<>();
dfs(nums);
return res;
}
private void dfs(int[] nums) {
if (list.size() == nums.length) {
List<Integer> result = new ArrayList<>();
for (Integer i : list) result.add(i);
res.add(result);
}
for (int i = 0; i < nums.length; i++) {
if (set.contains(nums[i])) continue;
list.add(nums[i]);
set.add(nums[i]); // bo[i] = true
dfs(nums);
list.remove(list.size() - 1);
set.remove(nums[i]); // bo[i] = false
}
}
}