508. 出现次数最多的子树元素和【中等题】【每日一题】
思路:【DFS】
递归法深度优先统计每个节点的子树和,并将和存入哈希map
,并记录和的出现次数,最后将出现次数最多的子树和返回。
代码:
/**
* 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 {
Map<Integer, Integer> map;
int max;
public int[] findFrequentTreeSum(TreeNode root) {
//定义全局变量 map 用来存储子树和 及其出现次数
this.map = new HashMap<>();
//定义全局变量 max 用来记录子树和的最大出现次数
this.max = 0;
//调用dfs函数 统计所有的子树和 及其对应的出现次数,放入map,并记录最大出现次数max
dfs(root);
//定义集合 list 用来存储出现次数最多的子树和
List<Integer> list = new ArrayList<>();
//遍历map
for (Integer key : map.keySet()) {
//如果当前key对应的value 即当前子树和 的出现次数 与最大值 max 相等,说明这个key就是出现次数最多的子树和,将其加入列表list
if (map.get(key) == max){
list.add(key);
}
}
//将list转为int数组 ans
int n = list.size();
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = list.get(i);
}
//返回ans
return ans;
}
public int dfs(TreeNode root){
//如果当前节点为空 返回 0
if (root == null){
return 0;
}
//记录当前节点的子树和 sum 等于 当前节点值 + 当前节点的左子树和 + 当前节点的右子树和
int sum = root.val + dfs(root.left) + dfs(root.right);
// 将当前节点的子树和存入map,并将map中sum对应的value+1
map.put(sum,map.getOrDefault(sum,0)+1);
//更新最大次数 max = max 和 map中sum的value 这两者的最大值
max = Math.max(max,map.get(sum));
//返回当前节点的子树和 sum
return sum;
}
}
剑指 Offer II 086. 分割回文子字符串【中等题】
思路:【回溯+动态规划】
先用动态规划标记出哪些子串是回文子串,再采用回溯法找出所有的回文串分割方案。
对题解的代码加了自己的详细注释。
代码:
class Solution {
static int n;
static boolean[][] dp;
static List<List<String>> list;
static List<String> cur;
public String[][] partition(String s) {
//字符串长度
n = s.length();
//dp数组,dp[i,j]表示 s.substring(i,j+1) 是否是回文串
dp = new boolean[n][n];
//存储所有的分割方案
list = new ArrayList<>();
//存储当前分割方案
cur = new ArrayList<>();
//将dp数组初始化为全true
//因为 i > j 时 可以理解为 子串是空 默认为true ; i = j时,表示这个子串是一个单字符,必为true,因此为 true
for (int i = 0; i < n; i++) {
Arrays.fill(dp[i],true);
}
//自底向上 对 右上三角区域进行递推
for (int i = n - 2; i >= 0 ; i--) {// i 为矩阵的行 子串的起始下标
for (int j = i + 1; j < n; j++) {// j 为矩阵的列 子串的结束下标
//当 i 和 j 位置字符相等时 如果 字符串去掉首尾字符 即 [i+1,j-1]为回文串,即dp[i+1,j-1]=true 那么说明当前子串也为回文串 dp[i,j]=true
dp[i][j] = (s.charAt(i) == s.charAt(j)) && dp[i+1][j-1];
}
}
//当我们确定好哪些分割方案为回文串之后,对字符串进行回溯分割
backtrack(s,0);
//分割完毕后,有效的分割方案存储在列表list中,我们需要把list转为String[][]数组形式的ans
int rows = list.size();
String[][] ans = new String[rows][];
for (int i = 0; i < rows; i++) {
int cols = list.get(i).size();
ans[i] = new String[cols];
for (int j = 0; j < cols; j++) {
ans[i][j] = list.get(i).get(j);
}
}
//返回ans
return ans;
}
public void backtrack(String s,int i){
//如果 i 走到 字符串s 的结尾 说明找到了一个有效的字符串分割方案,将这个方案存入list
if (i == n){
list.add(new ArrayList<>(cur));
}
//i为字符串s当前待处理的下标,从i开始往后遍历
for (int j = i; j < n; j++) {
//如果dp[i][j]为true,表示字符串的子串[i,j]是回文串
if (dp[i][j]){
//将s.substring(i,j+1) 添加到当前方案cur中
cur.add(s.substring(i,j+1));
//调用回溯函数继续寻找 j+1 起始的回文子串
backtrack(s,j+1);
//函数调用结束,回溯至调用前状态,即将cur中最后一个元素删除
cur.remove(cur.size()-1);
}
}
}
}