其他:
今日总结
往期打卡
跳转: 77. 组合
学习: 代码随想录公开讲解
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
递归传入剩余需选个数,归零终止.
剪枝: 用需选个数可以求出遍历范围,及至少给以后的选择留出足够的数
因为使用dfs,一次只需要存储一条路径即可
使用迭代法需要储存大量的子状态. 而且因为空间需要提前预定义,所以不方便做剪枝.
其上下层之间的操作相对独立,没有明显练习,所以无法做递推.
class Solution {
int k;
List<List<Integer>> ans;
private final List<Integer> path = new ArrayList<>();
void dfs(int n){
int d = k-path.size();
if(d==0) {
ans.add(new ArrayList<>(path));
return;
}
for(int j=n;j>=d;j--){
path.add(j);
dfs(j-1);
path.remove(path.size()-1);
}
}
public List<List<Integer>> combine(int n, int k) {
this.k = k;
ans = new ArrayList<>();
dfs(n);
return ans;
}
}
跳转: 216. 组合总和 III
学习: 代码随想录公开讲解
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
递归的终止条件改为两个,和大于n或还需选择数为0
class Solution {
List<List<Integer>> ans ;
List<Integer> path = new ArrayList<>();
int sum = 0;
int n;
void dfs(int k,int start){
if(sum>n) return;
if(k==0){
if(sum==n) ans.add(new ArrayList<>(path));
return;
}
for(int i=start;i<=9;i++){
sum+=i;
path.add(i);
dfs(k-1,i+1);
sum-=i;
path.remove(path.size()-1);
}
}
public List<List<Integer>> combinationSum3(int k, int n) {
ans = new ArrayList<>();
this.n = n;
dfs(k,1);
return ans;
}
}
跳转: 17. 电话号码的字母组合
学习: 代码随想录公开讲解
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
终止条件为按过全部电话键,因为这题求的是全排列,所以没法做剪枝操作
class Solution {
private static final String[] MAPPING = new String[]{ "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
List<String> ans;
private char[] path;
void backtracking(char[] list,int index){
if(index==list.length){
ans.add(new String(path));
return;
}
int k = list[index]-50;
for(char c:MAPPING[k].toCharArray()){
path[index] = c;
backtracking(list,index+1);
}
}
public List<String> letterCombinations(String digits) {
int n = digits.length();
ans = new ArrayList<>();
if(n==0) return ans;
path = new char[n];
backtracking(digits.toCharArray(),0);
return ans;
}
}
跳转: 2537. 统计好子数组的数目
学习: 灵茶山艾府题解
给你一个整数数组 nums
和一个整数 k
,请你返回 nums
中 好 子数组的数目。
一个子数组 arr
如果有 至少 k
对下标 (i, j)
满足 i < j
且 arr[i] == arr[j]
,那么称它是一个 好 子数组。
子数组 是原数组中一段连续 非空 的元素序列。
这题可以使用滑动窗口加哈希表,每次收缩到刚好满足条件,计算时加上被抛弃的前缀数
求的是选出两个相同元素,对于单个元素来讲,刚好是0,1,3,6,每次加一个n-1到n
class Solution {
public long countGood(int[] nums, int k) {
int n = nums.length;
Map<Integer,Integer> map = new HashMap<>();
int sum = 0;
long ans = 0;
for(int i=0,pre = 0;i<n;i++){
sum+=map.getOrDefault(nums[i],0);
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
while(sum>=k){
map.put(nums[pre],map.get(nums[pre])-1);
sum-=map.get(nums[pre]);
pre++;
// System.out.println(pre);
}
ans=ans+pre;
}
return ans;
}
}
跳转: 516. 最长回文子序列
学习: 灵茶山艾府题解
给你一个字符串 s
,找出其中最长的回文子序列,并返回该序列的长度。
子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
题目中不要求连续,但给出了回文序列的条件.
满足条件就都选,不满足条件留使子串最大的一边
dp数组值的含义是当前区间内能选出的最大的回文子串长度
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[] f = new int[n];
for(int i=n-1;i>=0;i--){
f[i] = 1;
int pre = 0;
for(int j=i+1;j<n;j++){
int tmp = f[j];
if(s.charAt(i)==s.charAt(j)) f[j] = pre+2;
else f[j] = Math.max(f[j],f[j-1]);
pre = tmp;
}
}
return f[n-1];
}
}
class Solution {
int[][] cache;
int dfs(String s,int l,int r){
if(l==r) return 1;
if(l>r) return 0;
if(cache[l][r]!=-1) return cache[l][r];
if(s.charAt(l)==s.charAt(r)) return cache[l][r] = dfs(s,l+1,r-1)+2;
else return cache[l][r] = Math.max(dfs(s,l+1,r),dfs(s,l,r-1));
}
public int longestPalindromeSubseq(String s) {
cache = new int[s.length()][s.length()];
for(int[] i:cache){
Arrays.fill(i,-1);
}
return dfs(s,0,s.length()-1);
}
}
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] f = new int[n][n];
for(int i=n-1;i>=0;i--){
for(int j=0;j<n;j++){
if(i==j) f[i][j] = 1;
else if(i>j) continue;
else {
if(s.charAt(i)==s.charAt(j)) f[i][j] = f[i+1][j-1]+2;
else f[i][j] = Math.max(f[i+1][j],f[i][j-1]);
}
}
}
return f[0][n-1];
}
}
跳转: 1039. 多边形三角剖分的最低得分
学习: 灵茶山艾府题解
你有一个凸的 n
边形,其每个顶点都有一个整数值。给定一个整数数组 values
,其中 values[i]
是第 i
个顶点的值(即 顺时针顺序 )。
假设将多边形 剖分 为 n - 2
个三角形。对于每个三角形,该三角形的值是顶点标记的乘积,三角剖分的分数是进行三角剖分后所有 n - 2
个三角形的值之和。
返回 多边形进行三角剖分后可以得到的最低分 。
遍历区间找所有可能的三角形,每次划分继续二分区间遍历
class Solution {
public int minScoreTriangulation(int[] values) {
int n = values.length;
int[][] f = new int[n][n];
for(int i = n-3;i>=0;i--){
for(int j=i+2;j<n;j++){
f[i][j] = Integer.MAX_VALUE;
for(int k=i+1;k<j;k++){
f[i][j] = Math.min(f[i][j],f[i][k]+f[k][j]+values[i]*values[k]*values[j]);
}
}
}
return f[0][n-1];
}
}
class Solution {
int[] v;
int[][] cache;
int dfs(int l,int r){
if(r<=l+1) return 0;
if(cache[l][r]!=0) return cache[l][r];
int min = Integer.MAX_VALUE>>1;
for(int i=l+1;i<r;i++){
int tmp = dfs(l,i)+dfs(i,r)+v[i]*v[l]*v[r];
if(tmp<min) min = tmp;
}
return cache[l][r]=min;
}
public int minScoreTriangulation(int[] values) {
int n = values.length;
cache = new int[n][n];
v = values;
return dfs(0,n-1);
}
}
跳转: 543. 二叉树的直径
学习: 灵茶山艾府题解
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root
。
两节点之间路径的 长度 由它们之间边数表示。
当前最大是左最大+右最大+1
然后返回左右最大的一条数量
class Solution {
int ans=0;
int dfs(TreeNode root){
if(root==null) return 0;
int l = dfs(root.left);
int r = dfs(root.right);
ans = Math.max(ans,l+r);
return Math.max(l,r)+1;
}
public int diameterOfBinaryTree(TreeNode root) {
dfs(root);
return ans;
}
}
跳转: 124. 二叉树中的最大路径和
学习: 灵茶山艾府题解
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root
,返回其 最大路径和 。
dfs,上一题求数量改求和了
不过需要注意,子树如果为负不应该选,所以可以直接返回时和0比较一下
class Solution {
int ans=Integer.MIN_VALUE;
int dfs(TreeNode root){
if(root==null) return 0;
int l = dfs(root.left);
int r = dfs(root.right);
ans = Math.max(ans,l+r+root.val);
return Math.max(Math.max(l,r)+root.val,0);
}
public int maxPathSum(TreeNode root) {
dfs(root);
return ans;
}
}
跳转: 2246. 相邻字符不同的最长路径
学习: 灵茶山艾府题解
给你一棵 树(即一个连通、无向、无环图),根节点是节点 0
,这棵树由编号从 0
到 n - 1
的 n
个节点组成。用下标从 0 开始、长度为 n
的数组 parent
来表示这棵树,其中 parent[i]
是节点 i
的父节点,由于节点 0
是根节点,所以 parent[0] == -1
。
另给你一个字符串 s
,长度也是 n
,其中 s[i]
表示分配给节点 i
的字符。
请你找出路径上任意一对相邻节点都没有分配到相同字符的 最长路径 ,并返回该路径的长度。
父节点的父节点一定比子节点的父节点小吗? 显然这题不是这样的,我们无法排出一个合理的顺序.
所以这题不能使用倒序遍历,只能自顶向下遍历.
题目中给的是父节点数组,所以需要构造和子节点的哈希.
class Solution {
private List<Integer>[] g;
private char[] s;
private int ans;
int dfs(int x){
int maxLen = 0;
for(int y:g[x]){
int len = dfs(y)+1;
if(s[y]!=s[x]){
ans = Math.max(ans,maxLen+len);
maxLen = Math.max(maxLen,len);
}
}
return maxLen;
}
public int longestPath(int[] parent, String s) {
int n = parent.length;
this.s = s.toCharArray();
g = new ArrayList[n];
Arrays.setAll(g, e -> new ArrayList<>());
for(int i=1;i<n;i++){
g[parent[i]].add(i);
}
dfs(0);
return ans+1;
}
}
练习了组合回溯以及区间DP,树性DP
回溯算法模板框架:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
代码随想录算法训练营第十八天
代码随想录算法训练营第十七天
代码随想录算法训练营周末三
代码随想录算法训练营第十六天
代码随想录算法训练营第十五天
代码随想录算法训练营第十四天
代码随想录算法训练营第十三天
代码随想录算法训练营第十二天
代码随想录算法训练营第十一天
代码随想录算法训练营周末二
代码随想录算法训练营第十天
代码随想录算法训练营第九天
代码随想录算法训练营第八天
代码随想录算法训练营第七天
代码随想录算法训练营第六天
代码随想录算法训练营第五天
代码随想录算法训练营周末一
代码随想录算法训练营第四天
代码随想录算法训练营第三天
代码随想录算法训练营第二天
代码随想录算法训练营第一天