2022年8月18日荣耀笔试题第三题
题目描述:
C语言的结构体是C语言中最常见的一种数据结构,C语言中复合数据类型的一类。结构体是一些元素的集合,这些元素称为结构体的成员,且这些成员可以为不同的类型,也可以是其他结构体。
由于结构体是一个复杂类型,因此在代码写作中,经常需要获取到结构体占用的长度,现在仅考虑一字节对齐的情况(即结构体的总长度就等于其所有成员的长度总和),计算结构体的长度。
输入描述:
若干个结构体,结构体中成员的类型仅包含:
char(长度1)
short(长度2)
long(长度4)
其他结构体类型
不包含其他情况,格式如下(//以及后面的说明是为了解释题目,不会在输入中出现):
struct struct_a { // 1、格式为 struct + 空格 + 结构体名字 + 空格 + {
char member_a; // 2、基本数据类型成员格式为:四个空格 + 类型名称 + 空格 + 成员名字 + ;
short member_b;
long member_c;
struct struct_b member_d; // 3、结构体类型成员的格式为:四个空格 + struct + 空格 + 成员名字 + ;
}; // 4、一个结构体以 } + ; 结尾
struct struct_b {
char member_a; // 不同的结构体成员名字可以相同,比如 struct_b 和 struct_a 都可以有一个成员名字叫member_a
};
sizeof(struct struct_a) // 5、格式为 sizeof + ( + struct + 空格 + 结构体名字 + ),输入最后一行是要求计算的 结构体长度,需要根据这行指定的结构体输出结构体大小
结构体名字和成员名字是标识符,输入会确保仅包括字母,数字和下划线。
同一个结构体的成员名称之间不会重复,不同结构体的成员名称之间可能重复。
如果输入有多个结构体,第一个结构体和第二个结构体之间没有空行。
输入的每一行代码不会超过500个字符,结构体名称和成员名称不会超过50个字符。结构体的总长度不会超过10_000_000。
输出描述:
如果结构体的长度能够算出来,则直接根据输入,输出数字。比如上面输入要求sizeof(struct struct_a),代表需要输出struct_a的长度:
8
如果结构体的长度无法算出来(比如:一个结构体A的某个成员是另一个结构体B,但结构体B并未定义;或者结构体A的某个成员是结构体B,结构体B的某个成员是结构体A,导致循环依赖)则直接输出:
0
示例1
输入
struct RecordList {
short recordListTotalValue;
long recordListTotalPrice;
struct RecordItem recordItem;
};
struct RecordItem {
char nameA;
char nameB;
short value;
long price;
};
sizeof(RecordList)
输出
14
示例2
输入
struct RecordList {
short recordListTotalValue;
long recordListTotalPrice;
struct RecordItem recordItem;
};
sizeof(RecordList)
输出
0
【拙劣解法,欢迎指教】
思路:
构建一个有向图,每个struct看成一个图节点,struct里面包含的char、short、long成员直接累加长度,记录为节点的权值,而struct里面的struct成员则作为当前节点的邻居节点,当前节点有一条边指向它。
这样构造有向图,然后使用拓扑排序计算整个图所有节点的权值之和,同时判断图是否有环。
代码:
import java.util.*;
public class Main {
private static final Map<String, Integer> len = new HashMap<>();
static {
len.put("char", 1);
len.put("short", 2);
len.put("long", 4);
}
// 图节点定义
static class Node {
int weight;
List<String> neighbors;
Node() {
weight = 0;
neighbors = new ArrayList<>();
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
List<String> lines = new ArrayList<>();
String s;
while (!(s = in.nextLine().trim()).isBlank()) {
lines.add(s);
}
System.out.println(totalLength(lines));
}
private static int totalLength(List<String> lines) {
Map<String, Node> graph = new HashMap<>();
for (int i = 0; i < lines.size(); ) {
String s1 = lines.get(i++);
String s2;
if (s1.startsWith("struct")) {
String name = s1.split(" ")[1];
graph.put(name, new Node());
int w = 0;
while (true) {
s2 = lines.get(i++);
if (s2.equals("};")) {
break;
}
if (s2.startsWith("struct")) {
String neighbor = s2.split(" ")[1];
graph.get(name).neighbors.add(neighbor);
} else {
w += len.get(s2.split(" ")[0]);
}
}
graph.get(name).weight = w;
} else if (s1.startsWith("sizeof")) {
return topSort(graph);
}
}
return 0;
}
private static int topSort(Map<String, Node> graph) {
int ans = 0;
int count = 0;
Set<String> nodes = graph.keySet();
Map<String, Integer> inDegree = new HashMap<>();
// 计算节点入度
for (String node : nodes) {
for (String nb : graph.get(node).neighbors) {
if (!graph.containsKey(nb)) {
// 邻居节点nb没有被定义
return 0;
}
inDegree.put(nb, inDegree.getOrDefault(nb, 0) + 1);
}
}
Queue<String> queue = new LinkedList<>();
// 没有入度的节点先入队
for (String node : nodes) {
if (!inDegree.containsKey(node)) {
queue.offer(node);
ans += graph.get(node).weight;
count++;
}
}
while (!queue.isEmpty()) {
String p = queue.poll();
for (String nb : graph.get(p).neighbors) {
inDegree.put(nb, inDegree.get(nb) - 1);
if (inDegree.get(nb) == 0) {
queue.offer(nb);
ans += graph.get(nb).weight;
count++;
}
}
}
return count == nodes.size() ? ans : 0;
}
}