class079 树型dp-下【算法】

简介: class079 树型dp-下【算法】

class079 树型dp-下【算法】

算法讲解079【必备】树型dp-下

code1 2477. 到达首都的最少油耗

// 到达首都的最少油耗

// 给你一棵 n 个节点的树(一个无向、连通、无环图)

// 每个节点表示一个城市,编号从 0 到 n - 1 ,且恰好有 n - 1 条路

// 0 是首都。给你一个二维整数数组 roads

// 其中 roads[i] = [ai, bi] ,表示城市 ai 和 bi 之间有一条 双向路

// 每个城市里有一个代表,他们都要去首都参加一个会议

// 每座城市里有一辆车。给你一个整数 seats 表示每辆车里面座位的数目

// 城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车

// 相邻城市之间一辆车的油耗是一升汽油

// 请你返回到达首都最少需要多少升汽油

// 测试链接 : https://leetcode.cn/problems/minimum-fuel-cost-to-report-to-the-capital/

以x为头

孩子的最少油耗+孩子的人数/seats向上取整

package class079;
import java.util.ArrayList;
// 到达首都的最少油耗
// 给你一棵 n 个节点的树(一个无向、连通、无环图)
// 每个节点表示一个城市,编号从 0 到 n - 1 ,且恰好有 n - 1 条路
// 0 是首都。给你一个二维整数数组 roads
// 其中 roads[i] = [ai, bi] ,表示城市 ai 和 bi 之间有一条 双向路
// 每个城市里有一个代表,他们都要去首都参加一个会议
// 每座城市里有一辆车。给你一个整数 seats 表示每辆车里面座位的数目
// 城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车
// 相邻城市之间一辆车的油耗是一升汽油
// 请你返回到达首都最少需要多少升汽油
// 测试链接 : https://leetcode.cn/problems/minimum-fuel-cost-to-report-to-the-capital/
public class Code01_MinimumFuelCost {
  public static long minimumFuelCost(int[][] roads, int seats) {
    int n = roads.length + 1;
    ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
    for (int i = 0; i < n; i++) {
      graph.add(new ArrayList<>());
    }
    for (int[] r : roads) {
      graph.get(r[0]).add(r[1]);
      graph.get(r[1]).add(r[0]);
    }
    int[] size = new int[n];
    long[] cost = new long[n];
    f(graph, seats, 0, -1, size, cost);
    return cost[0];
  }
  // 根据图,当前来到u,u的父节点是p
  // 遍历完成后,请填好size[u]、cost[u]
  public static void f(ArrayList<ArrayList<Integer>> graph, int seats, int u, int p, int[] size, long[] cost) {
    size[u] = 1;
    for (int v : graph.get(u)) {
      if (v != p) {
        f(graph, seats, v, u, size, cost);
        size[u] += size[v];
        cost[u] += cost[v];
        // a/b向上取整,可以写成(a+b-1)/b
        // (size[v]+seats-1) / seats = size[v] / seats 向上取整
        cost[u] += (size[v] + seats - 1) / seats;
      }
    }
  }
}

code2 2246. 相邻字符不同的最长路径

// 相邻字符不同的最长路径

// 给你一棵 树(即一个连通、无向、无环图),根节点是节点 0

// 这棵树由编号从 0 到 n - 1 的 n 个节点组成

// 用下标从 0 开始、长度为 n 的数组 parent 来表示这棵树

// 其中 parent[i] 是节点 i 的父节点

// 由于节点 0 是根节点,所以 parent[0] == -1

// 另给你一个字符串 s ,长度也是 n ,其中 s[i] 表示分配给节点 i 的字符

// 请你找出路径上任意一对相邻节点都没有分配到相同字符的 最长路径

// 并返回该路径的长度

// 测试链接 : https://leetcode.cn/problems/longest-path-with-different-adjacent-characters/

以x为头的树

不要x:孩子的最长路径的最大值

要x:和x不同的耗子的最长路径+次长路径

返回[包含头结点的最长路径,最长路径]

叶子结点返回(1,1)

package class079;
import java.util.ArrayList;
// 相邻字符不同的最长路径
// 给你一棵 树(即一个连通、无向、无环图),根节点是节点 0
// 这棵树由编号从 0 到 n - 1 的 n 个节点组成
// 用下标从 0 开始、长度为 n 的数组 parent 来表示这棵树
// 其中 parent[i] 是节点 i 的父节点
// 由于节点 0 是根节点,所以 parent[0] == -1
// 另给你一个字符串 s ,长度也是 n ,其中 s[i] 表示分配给节点 i 的字符
// 请你找出路径上任意一对相邻节点都没有分配到相同字符的 最长路径
// 并返回该路径的长度
// 测试链接 : https://leetcode.cn/problems/longest-path-with-different-adjacent-characters/
public class Code02_LongestPathWithDifferentAdjacent {
  public static int longestPath(int[] parent, String str) {
    int n = parent.length;
    char[] s = str.toCharArray();
    ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
    for (int i = 0; i < n; i++) {
      graph.add(new ArrayList<>());
    }
    for (int i = 1; i < n; i++) {
      graph.get(parent[i]).add(i);
    }
    return f(s, graph, 0).maxPath;
  }
  public static class Info {
    public int maxPathFromHead; // 一定要从头节点出发的情况下,相邻字符不等的最长路径长度
    public int maxPath; // 整棵树上,相邻字符不等的最长路径长度
    public Info(int a, int b) {
      maxPathFromHead = a;
      maxPath = b;
    }
  }
  public static Info f(char[] s, ArrayList<ArrayList<Integer>> graph, int u) {
    if (graph.get(u).isEmpty()) {
      // u节点是叶
      return new Info(1, 1);
    }
    int max1 = 0; // 下方最长链
    int max2 = 0; // 下方次长链
    int maxPath = 1;
    for (int v : graph.get(u)) {
      Info nextInfo = f(s, graph, v);
      maxPath = Math.max(maxPath, nextInfo.maxPath);
      if (s[u] != s[v]) {
        if (nextInfo.maxPathFromHead > max1) {
          max2 = max1;
          max1 = nextInfo.maxPathFromHead;
        } else if (nextInfo.maxPathFromHead > max2) {
          max2 = nextInfo.maxPathFromHead;
        }
      }
    }
    int maxPathFromHead = max1 + 1;
    maxPath = Math.max(maxPath, max1 + max2 + 1);
    return new Info(maxPathFromHead, maxPath);
  }
}

code3 2458. 移除子树后的二叉树高度

// 移除子树后的二叉树高度

// 给你一棵 二叉树 的根节点 root ,树中有 n 个节点

// 每个节点都可以被分配一个从 1 到 n 且互不相同的值

// 另给你一个长度为 m 的数组 queries

// 你必须在树上执行 m 个 独立 的查询,其中第 i 个查询你需要执行以下操作:

// 从树中 移除 以 queries[i] 的值作为根节点的子树

// 题目所用测试用例保证 queries[i] 不等于根节点的值

// 返回一个长度为 m 的数组 answer

// 其中 answer[i] 是执行第 i 个查询后树的高度

// 注意:

// 查询之间是独立的,所以在每个查询执行后,树会回到其初始状态

// 树的高度是从根到树中某个节点的 最长简单路径中的边数

// 测试链接 : https://leetcode.cn/problems/height-of-binary-tree-after-subtree-removal-queries/

dfn序号+size

每一个结点的深度

package class079;
// 移除子树后的二叉树高度
// 给你一棵 二叉树 的根节点 root ,树中有 n 个节点
// 每个节点都可以被分配一个从 1 到 n 且互不相同的值
// 另给你一个长度为 m 的数组 queries
// 你必须在树上执行 m 个 独立 的查询,其中第 i 个查询你需要执行以下操作:
// 从树中 移除 以 queries[i] 的值作为根节点的子树
// 题目所用测试用例保证 queries[i] 不等于根节点的值
// 返回一个长度为 m 的数组 answer
// 其中 answer[i] 是执行第 i 个查询后树的高度
// 注意:
// 查询之间是独立的,所以在每个查询执行后,树会回到其初始状态
// 树的高度是从根到树中某个节点的 最长简单路径中的边数
// 测试链接 : https://leetcode.cn/problems/height-of-binary-tree-after-subtree-removal-queries/
public class Code03_HeightRemovalQueries {
  // 不要提交这个类
  public static class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
  }
  // 提交如下的方法
  public static final int MAXN = 100010;
  // 下标为节点的值
  public static int[] dfn = new int[MAXN];
  // 下标为dfn序号
  public static int[] deep = new int[MAXN];
  // 下标为dfn序号
  public static int[] size = new int[MAXN];
  public static int[] maxl = new int[MAXN];
  public static int[] maxr = new int[MAXN];
  public static int dfnCnt;
  public static int[] treeQueries(TreeNode root, int[] queries) {
    dfnCnt = 0;
    f(root, 0);
    for (int i = 1; i <= dfnCnt; i++) {
      maxl[i] = Math.max(maxl[i - 1], deep[i]);
    }
    maxr[dfnCnt + 1] = 0;
    for (int i = dfnCnt; i >= 1; i--) {
      maxr[i] = Math.max(maxr[i + 1], deep[i]);
    }
    int m = queries.length;
    int[] ans = new int[m];
    for (int i = 0; i < m; i++) {
      int leftMax = maxl[dfn[queries[i]] - 1];
      int rightMax = maxr[dfn[queries[i]] + size[dfn[queries[i]]]];
      ans[i] = Math.max(leftMax, rightMax);
    }
    return ans;
  }
  // 来到x节点,从头节点到x节点经过了k条边
  public static void f(TreeNode x, int k) {
    int i = ++dfnCnt;
    dfn[x.val] = i;
    deep[i] = k;
    size[i] = 1;
    if (x.left != null) {
      f(x.left, k + 1);
      size[i] += size[dfn[x.left.val]];
    }
    if (x.right != null) {
      f(x.right, k + 1);
      size[i] += size[dfn[x.right.val]];
    }
  }
}

code4 2322. 从树中删除边的最小分数

// 从树中删除边的最小分数

// 存在一棵无向连通树,树中有编号从0到n-1的n个节点,以及n-1条边

// 给你一个下标从0开始的整数数组nums长度为n,其中nums[i]表示第i个节点的值

// 另给你一个二维整数数组edges长度为n-1

// 其中 edges[i] = [ai, bi] 表示树中存在一条位于节点 ai 和 bi 之间的边

// 删除树中两条不同的边以形成三个连通组件,对于一种删除边方案,定义如下步骤以计算其分数:

// 分别获取三个组件每个组件中所有节点值的异或值

// 最大 异或值和 最小 异或值的 差值 就是这种删除边方案的分数

// 返回可能的最小分数

// 测试链接 : https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree/

暴力枚举两条边

如果x,y

y>x,y是x的子树

sum1=sumY

sum2=sumX^sumY

sum3=sum0^sumX

y>x,y不是x的子树

sum1=sumY

sum2=sumX

sum3=sum0^ sumX^sumY

package class079;
import java.util.ArrayList;
import java.util.Arrays;
// 从树中删除边的最小分数
// 存在一棵无向连通树,树中有编号从0到n-1的n个节点,以及n-1条边
// 给你一个下标从0开始的整数数组nums长度为n,其中nums[i]表示第i个节点的值
// 另给你一个二维整数数组edges长度为n-1
// 其中 edges[i] = [ai, bi] 表示树中存在一条位于节点 ai 和 bi 之间的边
// 删除树中两条不同的边以形成三个连通组件,对于一种删除边方案,定义如下步骤以计算其分数:
// 分别获取三个组件每个组件中所有节点值的异或值
// 最大 异或值和 最小 异或值的 差值 就是这种删除边方案的分数
// 返回可能的最小分数
// 测试链接 : https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree/
public class Code04_MinimumScoreAfterRemovals {
  public static int MAXN = 1001;
  // 下标为原始节点编号
  public static int[] dfn = new int[MAXN];
  // 下标为dfn序号
  public static int[] xor = new int[MAXN];
  // 下标为dfn序号
  public static int[] size = new int[MAXN];
  public static int dfnCnt;
  public static int minimumScore(int[] nums, int[][] edges) {
    int n = nums.length;
    ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
    for (int i = 0; i < n; i++) {
      graph.add(new ArrayList<>());
    }
    for (int[] edge : edges) {
      graph.get(edge[0]).add(edge[1]);
      graph.get(edge[1]).add(edge[0]);
    }
    Arrays.fill(dfn, 0, n, 0);
    dfnCnt = 0;
    f(nums, graph, 0);
    int m = edges.length;
    int ans = Integer.MAX_VALUE;
    for (int i = 0, a, b, pre, pos, sum1, sum2, sum3; i < m; i++) {
      a = Math.max(dfn[edges[i][0]], dfn[edges[i][1]]);
      for (int j = i + 1; j < m; j++) {
        b = Math.max(dfn[edges[j][0]], dfn[edges[j][1]]);
        if (a < b) {
          pre = a;
          pos = b;
        } else {
          pre = b;
          pos = a;
        }
        sum1 = xor[pos];
        // xor[1] : 整棵树的异或和
        // 因为头节点是0,一定拥有最小的dfn序号1
        // f函数调用的时候,也是从0节点开始的
        if (pos < pre + size[pre]) {
          sum2 = xor[pre] ^ xor[pos];
          sum3 = xor[1] ^ xor[pre];
        } else {
          sum2 = xor[pre];
          sum3 = xor[1] ^ sum1 ^ sum2;
        }
        ans = Math.min(ans, Math.max(Math.max(sum1, sum2), sum3) - Math.min(Math.min(sum1, sum2), sum3));
      }
    }
    return ans;
  }
  // 当前来到原始编号u,遍历u的整棵树
  public static void f(int[] nums, ArrayList<ArrayList<Integer>> graph, int u) {
    int i = ++dfnCnt;
    dfn[u] = i;
    xor[i] = nums[u];
    size[i] = 1;
    for (int v : graph.get(u)) {
      if (dfn[v] == 0) {
        f(nums, graph, v);
        xor[i] ^= xor[dfn[v]];
        size[i] += size[dfn[v]];
      }
    }
  }
}

code5 P2014 [CTSC1997] 选课

// 选课

// 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习

// 在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习

// 现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课

// 若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b

// 一个学生要从这些课程里选择 M 门课程学习

// 问他能获得的最大学分是多少

// 测试链接 : https://www.luogu.com.cn/problem/P2014

// 请同学们务必参考如下代码中关于输入、输出的处理

// 这是输入输出处理效率很高的写法

// 提交以下的code,提交时请把类名改成"Main",可以直接通过

方法1

当前来到i号节点为头的子树

只在i号节点、及其i号节点下方的前j棵子树上挑选节点

一共挑选k个节点,并且保证挑选的节点连成一片

返回最大的累加和

public static int f(int i, int j, int k)

f(i,j-1,k)

以i最后第一个孩子枚举s,1<=s<k

f(i,j-1,k-s)+f(v,v.size,s)

方法2

dp[i][j] : i ~ n+1 范围的节点,选择j个节点一定要形成有效结构的情况下,最大的累加和

不选i及其子树

学i结点

dp[i][j] = Math.max(dp[i + size[i]][j], val[i] + dp[i + 1][j - 1]);

package class079;
// 选课
// 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习
// 在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习
// 现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课
// 若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b
// 一个学生要从这些课程里选择 M 门课程学习
// 问他能获得的最大学分是多少
// 测试链接 : https://www.luogu.com.cn/problem/P2014
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;
// 普通解法,邻接表建图 + 相对好懂的动态规划
// 几乎所有题解都是普通解法的思路,只不过优化了常数时间、做了空间压缩
// 但时间复杂度依然是O(n * 每个节点的孩子平均数量 * m的平方)
public class Code05_CourseSelection1 {
  public static int MAXN = 301;
  public static int[] nums = new int[MAXN];
  public static ArrayList<ArrayList<Integer>> graph;
  static {
    graph = new ArrayList<>();
    for (int i = 0; i < MAXN; i++) {
      graph.add(new ArrayList<>());
    }
  }
  public static int[][][] dp = new int[MAXN][][];
  public static int n, m;
  public static void build(int n) {
    for (int i = 0; i <= n; i++) {
      graph.get(i).clear();
    }
  }
  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StreamTokenizer in = new StreamTokenizer(br);
    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    while (in.nextToken() != StreamTokenizer.TT_EOF) {
      // 节点编号从0~n
      n = (int) in.nval;
      in.nextToken();
      m = (int) in.nval + 1;
      build(n);
      for (int i = 1, pre; i <= n; i++) {
        in.nextToken();
        pre = (int) in.nval;
        graph.get(pre).add(i);
        in.nextToken();
        nums[i] = (int) in.nval;
      }
      out.println(compute());
    }
    out.flush();
    out.close();
    br.close();
  }
  public static int compute() {
    for (int i = 0; i <= n; i++) {
      dp[i] = new int[graph.get(i).size() + 1][m + 1];
    }
    for (int i = 0; i <= n; i++) {
      for (int j = 0; j < dp[i].length; j++) {
        for (int k = 0; k <= m; k++) {
          dp[i][j][k] = -1;
        }
      }
    }
    return f(0, graph.get(0).size(), m);
  }
  // 当前来到i号节点为头的子树
  // 只在i号节点、及其i号节点下方的前j棵子树上挑选节点
  // 一共挑选k个节点,并且保证挑选的节点连成一片
  // 返回最大的累加和
  public static int f(int i, int j, int k) {
    if (k == 0) {
      return 0;
    }
    if (j == 0 || k == 1) {
      return nums[i];
    }
    if (dp[i][j][k] != -1) {
      return dp[i][j][k];
    }
    int ans = f(i, j - 1, k);
    // 第j棵子树头节点v 
    int v = graph.get(i).get(j - 1);
    for (int s = 1; s < k; s++) {
      ans = Math.max(ans, f(i, j - 1, k - s) + f(v, graph.get(v).size(), s));
    }
    dp[i][j][k] = ans;
    return ans;
  }
}
package class079;
// 选课
// 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习
// 在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习
// 现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课
// 若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b
// 一个学生要从这些课程里选择 M 门课程学习
// 问他能获得的最大学分是多少
// 测试链接 : https://www.luogu.com.cn/problem/P2014
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下的code,提交时请把类名改成"Main",可以直接通过
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;
// 最优解,链式前向星建图 + dfn序的利用 + 巧妙定义下的尝试
// 时间复杂度O(n*m),觉得难可以跳过,这个最优解是非常巧妙和精彩的!
public class Code05_CourseSelection2 {
  public static int MAXN = 301;
  public static int[] nums = new int[MAXN];
  // 链式前向星建图
  public static int edgeCnt;
  public static int[] head = new int[MAXN];
  public static int[] next = new int[MAXN];
  public static int[] to = new int[MAXN];
  // dfn的计数
  public static int dfnCnt;
  // 下标为dfn序号
  public static int[] val = new int[MAXN + 1];
  // 下标为dfn序号
  public static int[] size = new int[MAXN + 1];
  // 动态规划表
  public static int[][] dp = new int[MAXN + 2][MAXN];
  public static int n, m;
  public static void build(int n, int m) {
    edgeCnt = 1;
    dfnCnt = 0;
    Arrays.fill(head, 0, n + 1, 0);
    Arrays.fill(dp[n + 2], 0, m + 1, 0);
  }
  public static void addEdge(int u, int v) {
    next[edgeCnt] = head[u];
    to[edgeCnt] = v;
    head[u] = edgeCnt++;
  }
  public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StreamTokenizer in = new StreamTokenizer(br);
    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    while (in.nextToken() != StreamTokenizer.TT_EOF) {
      n = (int) in.nval;
      in.nextToken();
      m = (int) in.nval;
      build(n, m);
      for (int i = 1; i <= n; i++) {
        in.nextToken();
        addEdge((int) in.nval, i);
        in.nextToken();
        nums[i] = (int) in.nval;
      }
      out.println(compute());
    }
    out.flush();
    out.close();
    br.close();
  }
  public static int compute() {
    f(0);
    // 节点编号0 ~ n,dfn序号范围1 ~ n+1
    // 接下来的逻辑其实就是01背包!不过经历了很多转化
    // 整体的顺序是根据dfn序来进行的,从大的dfn序,遍历到小的dfn序
    // dp[i][j] : i ~ n+1 范围的节点,选择j个节点一定要形成有效结构的情况下,最大的累加和
    // 怎么定义有效结构?重点!重点!重点!
    // 假设i ~ n+1范围上,目前所有头节点的上方,有一个总的头节点
    // i ~ n+1范围所有节点,选出来j个节点的结构,
    // 挂在这个假想的总头节点之下,是一个连续的结构,没有断开的情况
    // 那么就说,i ~ n+1范围所有节点,选出来j个节点的结构是一个有效结构
    for (int i = n + 1; i >= 2; i--) {
      for (int j = 1; j <= m; j++) {
        dp[i][j] = Math.max(dp[i + size[i]][j], val[i] + dp[i + 1][j - 1]);
      }
    }
    // dp[2][m] : 2 ~ n范围上,选择m个节点一定要形成有效结构的情况下,最大的累加和
    // 最后来到dfn序为1的节点,一定是原始的0号节点
    // 原始0号节点下方一定挂着有效结构
    // 并且和补充的0号节点一定能整体连在一起,没有任何跳跃连接
    // 于是整个问题解决
    return nums[0] + dp[2][m];
  }
  // u这棵子树的节点数返回
  public static int f(int u) {
    int i = ++dfnCnt;
    val[i] = nums[u];
    size[i] = 1;
    for (int ei = head[u], v; ei > 0; ei = next[ei]) {
      v = to[ei];
      size[i] += f(v);
    }
    return size[i];
  }
}

2023-11-26 20:48:06

相关文章
|
4月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
112 1
|
1月前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
36 2
|
6月前
|
存储 算法 Linux
【数据结构和算法】---二叉树(1)--树概念及结构
【数据结构和算法】---二叉树(1)--树概念及结构
55 0
|
2月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
31 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
3月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
67 2
|
2月前
|
存储 算法
动态规划算法学习一:DP的重要知识点、矩阵连乘算法
这篇文章是关于动态规划算法中矩阵连乘问题的详解,包括问题描述、最优子结构、重叠子问题、递归方法、备忘录方法和动态规划算法设计的步骤。
150 0
|
2月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
31 0
|
2月前
|
存储 算法 Java
数据结构和算法--分段树
数据结构和算法--分段树
23 0
|
2月前
|
算法 C++
【算法】DP背包问题(C/C++)
【算法】DP背包问题(C/C++)
下一篇
DataWorks