结构体(Java 类)实战题解笔记(持续更新)

前言

Java也可以有结构体吗?

在 Java 中并没有直接的「结构体」概念,但可以通过自定义类(class) 实现结构体的核心功能------封装一组具有关联关系的数据。本笔记通过实战题目,讲解如何用自定义类存储复杂数据、处理业务逻辑,后续可直接沿用该笔记格式补充更多同类题目。

题目1:歌唱比赛得分统计(求最高平均分)

题目核心需求

统计 n 名同学的歌唱比赛得分,每位同学有 m 名评委打分,得分规则为「去掉一个最高分、一个最低分后求平均值」,最终输出所有同学中的最高平均分(保留 2 位小数)。

解题思路

  1. 封装数据模型 :定义 Student 类,封装每位同学的评委分数数组、最高分、最低分、总分(去高低分后)、平均分。
  2. 初始化数据 :创建 n 个 Student 实例,每个实例初始化对应长度的评委分数数组。
  3. 读取并计算数据:逐行读取评委打分,同时计算该同学的最高分、最低分、原始总分;后续扣除高低分,计算有效平均分。
  4. 排序找最大值:通过冒泡排序对所有同学的平均分进行升序排列,排序后最后一个元素即为最高平均分。
  5. 格式化输出:保留 2 位小数输出结果。

核心代码解析

java 复制代码
package Struct;

import java.util.Scanner;

// 封装学生歌唱比赛数据的类(对应结构体功能)
class Student{
    int id; // 可选:学生编号,本题未要求使用
    int a[]; // 存储m名评委的打分
    int max = -1; // 最高分,初始值低于最小可能得分(0)
    int min = 11; // 最低分,初始值高于最大可能得分(10)
    double avg; // 有效平均分(去高低分后)
    double sum; // 原始总分(所有评委打分之和)
}

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

        int n = sc.nextInt(); // 学生人数
        int m = sc.nextInt(); // 评委人数
        Student sts[] = new Student[n]; // 学生数组,存储所有学生信息

        // 步骤1:初始化学生数组和每个学生的分数数组
        for (int i = 0; i < n; i++) {
            sts[i] = new Student(); // 必须实例化每个Student,否则会出现空指针异常
            sts[i].a = new int[m]; // 初始化每个学生的评委分数数组
        }

        // 步骤2:读取打分并计算每个学生的核心数据(max、min、sum、avg)
        for (int i = 0; i < n ; i++) {
            for (int j = 0; j < m; j++) {
                sts[i].a[j] = sc.nextInt(); // 读取单个评委打分
                sts[i].sum += sts[i].a[j]; // 累加原始总分
                // 更新最高分和最低分
                sts[i].max = Math.max(sts[i].a[j],sts[i].max);
                sts[i].min = Math.min(sts[i].a[j],sts[i].min);
            }
            // 计算有效总分(去掉最高分和最低分)
            sts[i].sum -= (sts[i].max+sts[i].min);
            // 计算有效平均分,*1.0 确保除法结果为小数,避免整数除法丢失精度
            sts[i].avg = sts[i].sum*1.0/(m-2);
        }

        // 步骤3:冒泡排序(升序),将平均分最低的放前面,最高的放后面
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - 1 - i; j++) {
                if (sts[j].avg > sts[j + 1].avg) {
                    // 交换两个学生对象(整体交换,无需单独交换每个字段,体现类封装的优势)
                    Student temp = sts[j];
                    sts[j] = sts[j + 1];
                    sts[j + 1] = temp;
                }
            }
        }

        // 步骤4:格式化输出最高平均分(保留2位小数)
        System.out.printf("%.2f\n",sts[n-1].avg);

        sc.close();
    }
}
关键注意点
  1. 数组初始化Student sts[] = new Student[n] 仅创建了数组容器,每个数组元素(Student 对象)需要单独 new Student() 实例化,否则会抛出 NullPointerException
  2. 数据类型精度 :计算平均分时分母是 m-2(整数),需要通过 *1.0 将分子转为浮点型,避免整数除法(直接舍弃小数部分)导致结果错误。
  3. 极值初始化 :最高分 max 初始化为 -1(低于最小可能得分 0),最低分 min 初始化为 11(高于最大可能得分 10),确保能被评委打分正确覆盖。

输入输出示例

输入:

复制代码
7 6
4 7 2 6 10 7
0 5 0 10 3 10
2 6 8 4 3 6
6 3 6 7 5 8
5 9 3 3 8 1
5 9 9 3 2 0
5 8 0 4 1 10

输出:

复制代码
6.00

题目2:期末考试「旗鼓相当的对手」匹配

题目核心需求

给定 N 名同学的姓名和语、数、英成绩,找出所有「旗鼓相当的对手」组合:

  1. 每科成绩分差均不大于 5;
  2. 总分分差均不大于 10;
  3. 输出时满足字典序:第一个姓名字典序 < 第二个姓名,且组合整体按字典序排序。

解题思路

  1. 封装数据模型 :定义 Student2 类,封装同学的姓名、三科成绩、总分。
  2. 读取并存储数据 :读取 N 名同学的信息,实例化 Student2 对象并存入数组(输入已按字典序排列,数组天然保留该顺序)。
  3. 双重循环匹配组合 :使用两层循环遍历所有 i < j 的同学组合(避免重复组合,且保证 i 在前 j 在后,符合字典序要求)。
  4. 判断对手条件:逐一验证三科分差和总分分差的条件,满足则输出对应姓名组合。
  5. 保证输出格式 :因输入已按字典序排列,i < j 的循环组合直接输出即可满足题目字典序要求。

核心代码解析

java 复制代码
package Struct;

import java.util.Scanner;

// 封装学生期末考试数据的类(对应结构体功能)
class Student2{
    String name; // 学生姓名
    int chinese; // 语文成绩
    int math; // 数学成绩
    int english; // 英语成绩
    int sum; // 三科总分
}

public class Main2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 学生人数
        Student2 sts[] = new Student2[n]; // 学生数组,存储所有学生信息

        // 步骤1:读取并初始化所有学生的信息
        for (int i = 0; i < n; i++) {
            sts[i] = new Student2(); // 实例化每个Student2对象,避免空指针异常
            // 处理换行符问题:nextInt()后未读取换行符,next()会自动跳过空白字符(包括换行、空格),此处可省略sc.nextLine()
            sts[i].name = sc.next(); // 读取姓名(无空格,用next())
            sts[i].chinese = sc.nextInt(); // 读取语文成绩
            sts[i].math = sc.nextInt(); // 读取数学成绩
            sts[i].english = sc.nextInt(); // 读取英语成绩
            sts[i].sum = sts[i].chinese + sts[i].math + sts[i].english; // 计算总分
        }

        // 步骤2:双重循环匹配「旗鼓相当的对手」(i < j 避免重复组合,且符合字典序)
        for (int i = 0; i < n; i++) {
            for (int j = i+1; j < n; j++) {
                // 条件1:每科成绩分差不大于5
                boolean chineseOk = Math.abs(sts[i].chinese - sts[j].chinese) <= 5;
                boolean mathOk = Math.abs(sts[i].math - sts[j].math) <= 5;
                boolean englishOk = Math.abs(sts[i].english - sts[j].english) <= 5;
                // 条件2:总分分差不大于10
                boolean sumOk = Math.abs(sts[i].sum - sts[j].sum) <= 10;

                // 满足所有条件,输出组合
                if(chineseOk && mathOk && englishOk && sumOk){
                    System.out.println(sts[i].name+" "+sts[j].name);
                }
            }
        }
        sc.close();
    }
}
关键注意点
  1. 避免重复组合 :使用 i < j 的循环逻辑(外层 i 从 0 到 n-1,内层 ji+1 到 n-1),既不会出现 <i,i> 自身组合,也不会出现 <j,i><i,j> 重复的组合。
  2. 字典序保证 :题目明确输入姓名按字典序排列,数组存储顺序与输入顺序一致,i < j 对应的 sts[i].name 字典序必然小于 sts[j].name,直接输出即可满足题目要求。
  3. 输入读取技巧 :读取姓名使用 sc.next()(适用于无空格的字符串),nextInt()next() 会自动跳过空白字符(换行、空格),无需额外处理换行符问题(原代码中的 if(i==0) sc.nextLine() 是冗余的,可删除)。
  4. 分差计算 :使用 Math.abs() 计算绝对值差,避免分差为负数导致判断条件错误。

输入输出示例

输入:

复制代码
3
fafa 90 90 90
lxl 95 85 90
senpai 100 80 91

输出:

复制代码
fafa lxl
lxl senpai

相关推荐
Gain_chance3 小时前
27-学习笔记尚硅谷数仓搭建-数据仓库DWD层介绍及其事务表(行为)相关概念
大数据·数据仓库·笔记·学习
会开花的二叉树3 小时前
高性能定时器:时间轮算法的工程实践
算法
林shir3 小时前
3-14-后端Web进阶(SpringBoot原理)
java·spring boot·后端
毕设源码-邱学长3 小时前
【开题答辩全过程】以 疫苗接种预约平台为例,包含答辩的问题和答案
java
大江东去浪淘尽千古风流人物4 小时前
【LingBot-Depth】Masked Depth Modeling for Spatial Perception
人工智能·算法·机器学习·概率论
Ronaldinho Gaúch4 小时前
leetcode279完全平方数
c++·算法·动态规划
虾说羊4 小时前
公平锁与非公平锁的区别与不同的使用场景
java·开发语言·spring
heartbeat..4 小时前
Redis常见问题及对应解决方案(基础+性能+持久化+高可用全场景)
java·数据库·redis·缓存
子春一4 小时前
Flutter for OpenHarmony:构建一个 Flutter 速记本应用,深入解析可编辑列表、滑动删除与实时笔记管理
笔记·flutter