java 洛谷题单【数据结构1-2】二叉树

墨春

关注

阅读 22

2024-10-16

P4715 【深基16.例1】淘汰赛

解题思路

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        // 读入n
        int n = input.nextInt();
        int numTeams = (int) Math.pow(2, n);  // 参赛队伍的数量为 2^n

        // 读入每个国家的能力值
        int[] abilities = new int[numTeams + 1];  // 1-based indexing
        for (int i = 1; i <= numTeams; i++) {
            abilities[i] = input.nextInt();
        }

        // 找出左半区和右半区的最强者
        int leftBest = 1;  // 初始假设左半区第一个国家是最强的
        int rightBest = numTeams / 2 + 1;  // 初始假设右半区第一个国家是最强的

        // 找左半区最强的国家
        for (int i = 2; i <= numTeams / 2; i++) {
            if (abilities[i] > abilities[leftBest]) {
                leftBest = i;
            }
        }

        // 找右半区最强的国家
        for (int i = numTeams / 2 + 2; i <= numTeams; i++) {
            if (abilities[i] > abilities[rightBest]) {
                rightBest = i;
            }
        }

        // 决赛:leftBest 和 rightBest 进行比赛,输者即为亚军
        if (abilities[leftBest] > abilities[rightBest]) {
            System.out.println(rightBest);
        } else {
            System.out.println(leftBest);
        }
    }
}

P4913 【深基16.例3】二叉树深度 

import java.util.Scanner;
import java.util.Stack;

public class Main {
    static class Node {
        int left, right; // 左右子节点
    }

    static Node[] tree; // 存储树的结构
    static int n;

    // 迭代方式计算树的深度
    static int calculateDepth() {
        Stack<int[]> stack = new Stack<>();
        stack.push(new int[]{1, 1}); // {节点编号, 当前深度}
        int maxDepth = 0;

        while (!stack.isEmpty()) {
            int[] current = stack.pop();
            int id = current[0];
            int deep = current[1];

            if (id == 0) continue; // 如果是叶子节点,跳过

            maxDepth = Math.max(maxDepth, deep); // 更新最大深度
            stack.push(new int[]{tree[id].left, deep + 1}); // 左子节点
            stack.push(new int[]{tree[id].right, deep + 1}); // 右子节点
        }

        return maxDepth;
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        n = input.nextInt(); // 读取节点数
        tree = new Node[n + 1]; // 初始化树的数组

        // 读取节点的左、右子节点
        for (int i = 1; i <= n; i++) {
            tree[i] = new Node(); // 初始化每个节点
            tree[i].left = input.nextInt();
            tree[i].right = input.nextInt();
        }

        int depth = calculateDepth(); // 计算深度
        System.out.println(depth); // 输出深度
    }
}

P1827 [USACO3.4] 美国血统 American Heritage

解题思路

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {
    static String inorder, preorder;
    static Map<Character, Integer> inorderIndexMap = new HashMap<>();

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        inorder = input.nextLine().trim();
        preorder = input.nextLine().trim();

        // 构建中序遍历字符的索引
        for (int i = 0; i < inorder.length(); i++) {
            inorderIndexMap.put(inorder.charAt(i), i);
        }

        // 获取后序遍历
        String postorder = getPostOrder(0, preorder.length() - 1, 0, inorder.length() - 1);
        System.out.println(postorder);
    }

    private static String getPostOrder(int preStart, int preEnd, int inStart, int inEnd) {
        if (preStart > preEnd || inStart > inEnd) {
            return "";
        }

        // 前序遍历的根节点
        char root = preorder.charAt(preStart);
        // 找到根节点在中序遍历中的索引
        int rootIndexInInorder = inorderIndexMap.get(root);
        // 左子树的节点数量
        int leftSubtreeSize = rootIndexInInorder - inStart;

        // 递归处理左子树和右子树
        String leftPostOrder = getPostOrder(preStart + 1, preStart + leftSubtreeSize, inStart, rootIndexInInorder - 1);
        String rightPostOrder = getPostOrder(preStart + leftSubtreeSize + 1, preEnd, rootIndexInInorder + 1, inEnd);

        // 后序遍历是左子树 + 右子树 + 根节点
        return leftPostOrder + rightPostOrder + root;
    }
}

P5076 【深基16.例7】普通二叉树(简化版)

解题思路

import java.util.*;

public class Main {
    private static TreeSet<Integer> set;

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int q = input.nextInt();
        set = new TreeSet<>();

        StringBuilder output = new StringBuilder();

        for (int i = 0; i < q; i++) {
            int op = input.nextInt();
            int x = input.nextInt();

            switch (op) {
                case 1: // 查询排名
                    int rank = getRank(x);
                    output.append(rank).append("\n");
                    break;
                case 2: // 查询排名为x的数
                    int value = getValueByRank(x);
                    output.append(value).append("\n");
                    break;
                case 3: // 求前驱
                    int predecessor = getPredecessor(x);
                    output.append(predecessor).append("\n");
                    break;
                case 4: // 求后继
                    int successor = getSuccessor(x);
                    output.append(successor).append("\n");
                    break;
                case 5: // 插入数x
                    set.add(x);
                    break;
                default:
                    break;
            }
        }

        System.out.print(output);
    }

    private static int getRank(int x) {
        // 计算小于x的元素个数
        return set.headSet(x).size() + 1; // +1因为排名从1开始
    }

    private static int getValueByRank(int rank) {
        // 获取排名为rank的数
        return (Integer) set.toArray()[rank - 1]; // rank-1因为数组索引从0开始
    }

    private static int getPredecessor(int x) {
        // 获取前驱
        Integer pred = set.lower(x);
        return (pred != null) ? pred : -2147483647; // 若不存在前驱返回 -2147483647
    }

    private static int getSuccessor(int x) {
        // 获取后继
        Integer succ = set.higher(x);
        return (succ != null) ? succ : 2147483647; // 若不存在后继返回 2147483647
    }
}

P1364 医院设置

解题思路

import java.util.*;

class TreeNode {
    int population; // 当前结点的人口数
    int left;       // 左子结点的索引
    int right;      // 右子结点的索引

    public TreeNode(int population, int left, int right) {
        this.population = population;
        this.left = left;
        this.right = right;
    }
}

public class Main {
    static TreeNode[] tree; // 用数组来表示树
    static int[] subtreePopulation; // 记录以当前结点为根的子树总人口数
    static int[] subtreeDistanceSum; // 记录以当前结点为根的子树距离和
    static int n; // 结点数

    // 计算以当前结点为根的子树人口和以及距离和
    static void dfs1(int node) {
        if (node == 0) return;
        int left = tree[node].left;
        int right = tree[node].right;

        dfs1(left);  // 递归计算左子树
        dfs1(right); // 递归计算右子树

        // 计算以当前结点为根的子树总人口和
        subtreePopulation[node] = tree[node].population;
        if (left != 0) subtreePopulation[node] += subtreePopulation[left];
        if (right != 0) subtreePopulation[node] += subtreePopulation[right];

        // 计算以当前结点为根的子树距离和
        if (left != 0) subtreeDistanceSum[node] += subtreeDistanceSum[left] + subtreePopulation[left];
        if (right != 0) subtreeDistanceSum[node] += subtreeDistanceSum[right] + subtreePopulation[right];
    }

    // 递归从根结点开始转移
    static void dfs2(int node, int parentDistanceSum) {
        if (node == 0) return;

        // 当前结点的总距离和 = 父结点的距离和 + 父结点距离和 - 左右子树的影响
        subtreeDistanceSum[node] = parentDistanceSum;
        int left = tree[node].left;
        int right = tree[node].right;

        if (left != 0) dfs2(left, subtreeDistanceSum[node] - subtreePopulation[left] + (subtreePopulation[1] - subtreePopulation[left]));
        if (right != 0) dfs2(right, subtreeDistanceSum[node] - subtreePopulation[right] + (subtreePopulation[1] - subtreePopulation[right]));
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        n = input.nextInt();
        tree = new TreeNode[n + 1];
        subtreePopulation = new int[n + 1];
        subtreeDistanceSum = new int[n + 1];

        for (int i = 1; i <= n; i++) {
            int population = input.nextInt();
            int left = input.nextInt();
            int right = input.nextInt();
            tree[i] = new TreeNode(population, left, right);
        }

        // 第一次 DFS:计算以 1 为根的子树人口和与距离和
        dfs1(1);

        // 第二次 DFS:动态转移,从根结点向其他结点转移
        dfs2(1, subtreeDistanceSum[1]);

        // 寻找最小的距离和
        int minDistanceSum = Integer.MAX_VALUE;
        for (int i = 1; i <= n; i++) {
            minDistanceSum = Math.min(minDistanceSum, subtreeDistanceSum[i]);
        }

        System.out.println(minDistanceSum);
    }
}

P1229 遍历问题

解题思路

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        String str1 = input.next();
        String str2 = input.next();

        int ans = 0;

        for (int i = 0; i < str1.length(); i++) {
            for (int j = 1; j < str2.length(); j++) {
                if (str1.charAt(i) == str2.charAt(j) && (i + 1 < str1.length() && str1.charAt(i + 1) == str2.charAt(j - 1))) {
                    ans++;
                }
            }
        }

        System.out.println(1 << ans);
    }
}

P1305 新二叉树

解题思路

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Node {
    char val;
    Node left;
    Node right;

    public Node(char val) {
        this.val = val;
        this.left = null;
        this.right = null;
    }
}

public class Main {
    static Map<Character, Node> tree = new HashMap<>();

    // 前序遍历
    public static void preOrder(Node root) {
        if (root == null) return;
        System.out.print(root.val);  // 先输出根节点
        preOrder(root.left);  // 递归遍历左子树
        preOrder(root.right);  // 递归遍历右子树
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();  // 读取节点数
        input.nextLine();  // 读取下一行

        // 根节点的字符
        char rootNode = ' ';

        // 读入每个节点和其左右子节点的关系
        for (int i = 0; i < n; i++) {
            String line = input.nextLine();
            char parent = line.charAt(0);
            char leftChild = line.charAt(1);
            char rightChild = line.charAt(2);

            // 记录第一个输入的节点为根节点
            if (i == 0) {
                rootNode = parent;
            }

            // 如果父节点还不存在,创建一个新的节点
            tree.putIfAbsent(parent, new Node(parent));

            // 如果左子节点不是'*',创建并连接
            if (leftChild != '*') {
                tree.putIfAbsent(leftChild, new Node(leftChild));
                tree.get(parent).left = tree.get(leftChild);
            }

            // 如果右子节点不是'*',创建并连接
            if (rightChild != '*') {
                tree.putIfAbsent(rightChild, new Node(rightChild));
                tree.get(parent).right = tree.get(rightChild);
            }
        }

        // 根节点就是第一个输入的节点
        Node root = tree.get(rootNode);

        // 前序遍历
        preOrder(root);
    }
}

P1030 [NOIP2001 普及组] 求先序排列

解题思路

import java.util.Scanner;

public class Main {

    // 递归构造先序遍历
    public static void findPreOrder(String inOrder, String postOrder) {
        if (postOrder.isEmpty()) {
            return;
        }

        // 后序遍历的最后一个字符是根节点
        char root = postOrder.charAt(postOrder.length() - 1);

        // 输出根节点
        System.out.print(root);

        // 在中序遍历中找到根节点的位置
        int rootIndex = inOrder.indexOf(root);

        // 划分中序遍历为左子树和右子树
        String inOrderLeft = inOrder.substring(0, rootIndex);
        String inOrderRight = inOrder.substring(rootIndex + 1);

        // 划分后序遍历为左子树和右子树的后序遍历
        String postOrderLeft = postOrder.substring(0, inOrderLeft.length());
        String postOrderRight = postOrder.substring(inOrderLeft.length(), postOrder.length() - 1);

        // 递归处理左子树
        findPreOrder(inOrderLeft, postOrderLeft);

        // 递归处理右子树
        findPreOrder(inOrderRight, postOrderRight);
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        // 输入中序遍历和后序遍历
        String inOrder = input.nextLine();
        String postOrder = input.nextLine();

        // 求先序遍历并输出
        findPreOrder(inOrder, postOrder);
    }
}

P3884 [JLOI2009] 二叉树问题

解题思路

import java.util.*;

class Node {
    int to, next, value;
    // 构造函数,用于初始化节点信息,包括目的节点、下一个边的索引以及边的权值
    Node(int to, int next, int value) {
        this.to = to;
        this.next = next;
        this.value = value;
    }
}

public class Main {
    static final int MAXN = 1000; // 最大节点数
    static int[] dis = new int[MAXN + 1], vis = new int[MAXN + 1], head = new int[MAXN + 1], box = new int[MAXN + 1];
    static Node[] edge = new Node[2 * MAXN]; // 用于存储所有边的邻接表
    static int tot = 0; // 边的计数器

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt(); // 输入节点数量
        // 读取树的边,并建立邻接表
        for (int i = 1; i < n; i++) {
            int u = input.nextInt(), v = input.nextInt(); // 输入每条边的两个节点
            addedge(u, v, 1); // 从父节点到子节点的边权为 1
            addedge(v, u, 2); // 从子节点到父节点的边权为 2
        }
        int u = input.nextInt(), v = input.nextInt(); // 输入需要查询路径的两个节点
        SPFA(1); // 从根节点 1 开始,使用 SPFA 算法计算最短路径

        int ans = 0;
        // 遍历每个节点,统计距离根节点的最远距离(即最大深度)
        for (int i = 1; i <= n; i++) {
            box[dis[i]]++; // 统计每层的节点数量
            ans = Math.max(ans, dis[i]); // 计算最大深度
        }
        System.out.println(ans + 1); // 输出树的最大深度(加上根节点)

        ans = 0;
        // 找到层中最多节点的层数(即最大宽度)
        for (int i = 1; i <= n; i++) {
            ans = Math.max(ans, box[i]); // 计算最大宽度
        }
        System.out.println(ans); // 输出最大宽度

        // 重新计算从节点 u 到节点 v 的最短路径
        SPFA(u);
        System.out.println(dis[v]); // 输出节点 u 到节点 v 的最短距离
    }

    // 添加一条边到邻接表
    static void addedge(int x, int y, int w) {
        edge[++tot] = new Node(y, head[x], w); // 创建新边
        head[x] = tot; // 更新当前节点的头部指针
    }

    // SPFA 算法用于计算单源最短路径
    static void SPFA(int s) {
        Queue<Integer> q = new LinkedList<>(); // 队列用于存储待处理的节点
        Arrays.fill(dis, Integer.MAX_VALUE); // 初始化所有节点的距离为无穷大
        Arrays.fill(vis, 0); // 初始化访问标记为 0,表示节点未在队列中
        dis[s] = 0; // 起点到自身的距离为 0
        vis[s] = 1; // 将起点标记为已访问
        q.add(s); // 将起点加入队列

        // 开始松弛操作,更新节点的最短距离
        while (!q.isEmpty()) {
            int x = q.poll(); // 从队列中取出一个节点
            vis[x] = 0; // 标记该节点不在队列中
            // 遍历从节点 x 出发的所有边
            for (int i = head[x]; i != 0; i = edge[i].next) {
                // 如果通过 x 可以使某个相邻节点的距离更短,则进行更新
                if (dis[edge[i].to] > dis[x] + edge[i].value) {
                    dis[edge[i].to] = dis[x] + edge[i].value; // 更新相邻节点的最短距离
                    // 如果该相邻节点不在队列中,则将其加入队列
                    if (vis[edge[i].to] == 0) {
                        vis[edge[i].to] = 1; // 标记相邻节点已在队列中
                        q.add(edge[i].to); // 将该节点加入队列
                    }
                }
            }
        }
    }
}

P1185 绘制二叉树

解题思路

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class Main {
    static int k, n, m, p, x, y; // k: 树的深度,n: 节点数,m: 画布宽度,p: 不可绘制的节点数
    static char[][] c = new char[800][1600]; // 绘制树的字符数组
    static boolean[][] f = new boolean[800][1600]; // 记录不可绘制的节点
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 输入流
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // 输出流
    static StringTokenizer st; // 用于分割输入字符串

    // 深度优先搜索,绘制树形结构
    static void dfs1(int x, int y, int a, int b, int k, int xx, int yy) {
        // 如果到达树的最底层,绘制叶子节点
        if (x == n) {
            c[x][y] = 'o';
            return;
        }
        // 根据当前的 k 值决定绘制点还是边
        if (k == 1) {
            c[x][y] = 'o'; // 绘制节点
            int X = xx + 1, Y = (yy - 1) * 2 + 1; // 左儿子的位置
            // 如果左儿子不可绘制,则继续绘制
            if (!f[X][Y]) dfs1(x + 1, y - 1, a + 1, b, 2, X, Y);
            X = xx + 1;
            Y = yy * 2; // 右儿子的位置
            // 如果右儿子不可绘制,则继续绘制
            if (!f[X][Y]) dfs1(x + 1, y + 1, a + 1, b, 3, X, Y);
        } else if (k == 2) {
            c[x][y] = '/'; // 绘制左边的边
            // 判断接下来绘制点还是边
            if (a * 2 == b) dfs1(x + 1, y - 1, 1, a, 1, xx, yy);
            else dfs1(x + 1, y - 1, a + 1, b, 2, xx, yy);
        } else if (k == 3) {
            c[x][y] = '\\'; // 绘制右边的边
            // 判断接下来绘制点还是边
            if (a * 2 == b) dfs1(x + 1, y + 1, 1, a, 1, xx, yy);
            else dfs1(x + 1, y + 1, a + 1, b, 3, xx, yy);
        }
    }

    // 计算树的大小和绘制树形结构
    static void make(int k) {
        n = 3; // 初始设置为 3 层
        for (int i = 3; i <= k; i++) n *= 2; // 计算层数
        m = 6 * (1 << (k - 2)) - 1; // 计算画布的宽度
        // 初始化画布,填充空格
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) c[i][j] = ' ';
        dfs1(1, m / 2 + 1, 1, n, 1, 1, 1); // 从根节点开始绘制
    }

    public static void main(String[] args) throws IOException {
        // 读取输入的树的深度和不可绘制节点数
        st = new StringTokenizer(br.readLine());
        k = Integer.parseInt(st.nextToken());
        p = Integer.parseInt(st.nextToken());

        // 读取不可绘制的节点
        while (p-- > 0) {
            st = new StringTokenizer(br.readLine());
            x = Integer.parseInt(st.nextToken());
            y = Integer.parseInt(st.nextToken());
            f[x][y] = true; // 标记这个节点为不可绘制
        }

        // 特殊情况处理
        if (k == 1) {
            n = m = 1;
            c[1][1] = 'o'; // 仅绘制一个节点
        } else {
            make(k); // 计算并绘制树
        }

        // 使用 BufferedWriter 输出结果
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                bw.write(c[i][j]); // 输出画布的每个字符
            }
            bw.newLine(); // 换行
        }

        bw.flush(); // 刷新输出流,确保所有数据都被写出
        bw.close(); // 关闭输出流
    }
}

精彩评论(0)

0 0 举报