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(); // 关闭输出流
}
}