9.25lc
今日随计刷题:
每日抑题~~~~
动态规划 : 空间换时间
思考步骤
1.dp数组 以及下标的含义
2.递归公式
3.dp数组进行初始化
4.遍历顺序
5.是在不对 打印数组进行观察、
1143. 最长公共子序列 - 力扣(LeetCode) (leetcode-cn.com)
首先,区分两个概念:子序列可以是不连续的;子数组(子字符串)需要是连续的;
另外,动态规划也是有套路的:
- 单个数组或者字符串要用动态规划时,可以把动态规划 dp[i] 定义为 nums[0:i] 中想要求的结果;
- 当两个数组或者字符串要用动态规划时,可以把动态规划定义成两维的 dp[i][j],其含义是在 A[0:i] 与 B[0:j] 之间匹配得到的想要的结果。
1.状态定义
比如对于本题而言,可以定义 dp[i][j] 表示 text1[0:i-1] 和 text2[0:j-1] 的最长公共子序列。 (注:text1[0:i-1] 表示的是 text1 的 第 0 个元素到第 i - 1 个元素,两端都包含)
之所以 dp[i][j] 的定义不是 text1[0:i] 和 text2[0:j] ,是为了方便当 i = 0 或者 j = 0 的时候,dp[i][j]表示的为空字符串和另外一个字符串的匹配,这样 dp[i][j] 可以初始化为 0.
2.状态转移方程
知道状态定义之后,我们开始写状态转移方程。
- 当 text1[i - 1] == text2[j - 1] 时,说明两个子字符串的最后一位相等,所以最长公共子序列又增加了 1,所以 dp[i][j] = dp[i - 1][j - 1] + 1;举个例子,比如对于 ac 和 bc 而言,他们的最长公共子序列的长度等于 a 和 b 的最长公共子序列长度 0 + 1 = 1。
- 当 text1[i - 1] != text2[j - 1] 时,说明两个子字符串的最后一位不相等,那么此时的状态 dp[i][j] 应该是 dp[i - 1][j] 和 dp[i][j - 1] 的最大值。
- 举个例子,比如对于 “ace “和” bc” 而言,他们的最长公共子序列的长度等于
- ① ace 和 b 的最长公共子序列长度0
- ② ac 和 bc 的最长公共子序列长度1 的最大值,即 1。
综上状态转移方程为:
dp[i][j] = dp[i - 1][j - 1] + 1dp[i][j]=dp[i−1][j−1]+1, 当 text1[i - 1] == text2[j - 1];text1[i−1]==text2[j−1];
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])dp[i][j]=max(dp[i−1][j],dp[i][j−1]), 当 text1[i - 1] != text2[j - 1]text1[i−1]!=text2[j−1]
3.状态的初始化
初始化就是要看当 i = 0 与 j = 0 时, dp[i][j] 应该取值为多少。
- 当 i = 0 时,dp[0][j] 表示的是 text1text1 中取空字符串 跟 text2text2 的最长公共子序列,结果肯定为 0.
- 当 j = 0 时,dp[i][0] 表示的是 text2text2 中取空字符串 跟 text1text1 的最长公共子序列,结果肯定为 0.
综上,当 i = 0 或者 j = 0 时,dp[i][j] 初始化为 0.
4.遍历方向与范围
由于 dp[i][j] 依赖与 dp[i - 1][j - 1] , dp[i - 1][j], dp[i][j - 1],所以 ii 和 jj 的遍历顺序肯定是从小到大的。
另外,由于当 ii 和 jj 取值为 0 的时候,dp[i][j] = 0,而 dp 数组本身初始化就是为 0,所以,直接让 ii 和 jj 从 1 开始遍历。遍历的结束应该是字符串的长度为 len(text1)len(text1) 和 len(text2)len(text2)。
5.最终返回结果
由于 dp[i][j] 的含义是 text1[0:i-1] 和 text2[0:j-1] 的最长公共子序列。我们最终希望求的是 text1 和 text2 的最长公共子序列。所以需要返回的结果是 i = len(text1) 并且 j = len(text2) 时的 dp[len(text1)][len(text2)]。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int len1=text1.length();
int len2=text2.length();
//二维的数组 表示的 两个字符串 相同字符的长度问题:
int[][] dp=new int[len1+1][len2+1];
//进行初始化:表示的 0 和某一个字符串的比较就 == 0;
dp[0][0] =0;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(text1.charAt(i-1)==text2.charAt(j-1)){
//相等 长度+1
dp[i][j]=dp[i-1][j-1]+1;
}
else{
//不等 去两个字符串中连续长度 最大的!
dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
}
}
}
return dp[len1][len2];
}
}
583. 两个字符串的删除操作 - 力扣(LeetCode) (leetcode-cn.com)
上一题思路的一样:
class Solution {
public int minDistance(String word1, String word2) {
int len1=word1.length();
int len2=word2.length();
int[][] dp=new int[len1+1][len2+1];
dp[0][0] =0;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(word1.charAt(i-1)==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}
else{
dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
}
}
}
return len2+len1 - 2*dp[len1][len2];
}
}
617. 合并二叉树 - 力扣(LeetCode) (leetcode-cn.com)
: dfs:
- 注意 递归的条件。
- 读懂题意;不要因为简单 导致边界问题:
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
TreeNode cur= new TreeNode(0);
if(root1 ==null){
return root2;
}
if(root2 ==null){
return root1;
}
//这里的 cur.left; 应该注意返回值:
// 注意 node 节点的 写法!
cur=new TreeNode(root1.val+root2.val);
//注意 递归的返回值 !
cur.left=mergeTrees(root1.left,root2.left);
cur.right= mergeTrees(root1.right,root2.right);
return cur;
}
}
:为了降低空间复杂度 直接原来的树上改:
时间复杂度:O*(min(m,*n))
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
TreeNode cur= new TreeNode(0);
if(root1 ==null){
return root2;
}
if(root2 ==null){
return root1;
}
//这里的 cur.left; 应该主义返回值:
root1.val+=root2.val;
root1.left=mergeTrees(root1.left,root2.left);
root1.right= mergeTrees(root1.right,root2.right);
return root1;
}
}
116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode) (leetcode-cn.com)
:思路: 二叉树的宽度遍历:
- 注意 判断每层最后一个的条件:
- queue.peek() 和 queue.elment()的区别;
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
Queue<Node> queue =new LinkedList<>();
queue.add(root);
if(root ==null) return root;
while( !queue.isEmpty()){
int size=queue.size();
for(int i=0; i<size;i++){
Node cur =queue.poll();
// i =0 开始
if(i<size-1){
//peek() 指向队列头 没有值为 null;
cur.next =queue.peek();
}
if(cur.left != null){
queue.add(cur.left);
}
if(cur.right != null){
queue.add(cur.right);
}
}
}
return root;
}
}
2.: 省空间的方法:
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/
class Solution {
public Node connect(Node root) {
if(root ==null) return root;
Node pre=root;
while(pre.left != null){
Node cur =pre;
while(cur !=null){
cur.left.next=cur.right;
if(cur.next != null){
cur.right.next=cur.next.left;
}
cur=cur.next;
}
pre=pre.left;
}
return root;
}
}
695. 岛屿的最大面积 - 力扣(LeetCode) (leetcode-cn.com)
:dfs
: 沉没岛屿思想: 将每次找的的岛屿进行 置0 即 沉没 。保证了以后的遍历不会出错:
:常用的控制 方向的 方法:
-
方向数组
-
int[] up ={0,0,1,-1}; int[] left={1,-1,0,0};
-
直接初始化指针 移动: +1,-1;
-
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int res=0;
for(int i=0; i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
if(grid[i][j]==1){
res =Math.max(res,dfs(i,j,grid));
}
}
}
return res;
}
public int dfs(int i,int j, int[][] grid){
//边界条件: 出界 或者 无岛屿 直接 返回0
if(i<0||i>=grid.length ||j<0||j>= grid[0].length || grid[i][j] ==0){
return 0;
}
// 沉没岛屿
grid[i][j] =0;
int num=1;
num+= dfs(i+1,j,grid);
num+= dfs(i-1,j,grid);
num+= dfs(i,j+1,grid);
num+= dfs(i,j-1,grid);
return num;
}
}