508. 出现次数最多的子树元素和 / 剑指 Offer II 086. 分割回文子字符串

这篇博客介绍了如何使用深度优先搜索(DFS)来解决二叉树问题,例如找到出现次数最多的子树元素和。同时,还讲解了结合动态规划和回溯法解决字符串回文子串分割的问题。通过这两个实例,深入理解了DFS和回溯在处理树结构和字符串操作中的应用。
摘要由CSDN通过智能技术生成

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);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值