1. 什么是存取控制、 触发器、 存储过程 、 游标
- 存取控制
- 定义:存取控制是数据库管理系统(DBMS)为保障数据安全性与完整性,对不同用户访问数据库对象(如表、视图等)的权限加以管理的机制。它借助定义用户角色和权限,限制用户对数据库的操作,防止未授权访问和数据泄露。
- 要点:包含用户认证(确认用户身份)、授权(赋予用户特定操作权限,如查询、插入、更新、删除等)以及权限管理(可随时修改用户权限)。
- 应用:在多用户的企业级数据库系统中,依据员工的岗位和职责分配不同的数据库操作权限。例如,财务人员可以查询和修改财务数据,而普通员工只能查询部分公开数据。
- Java 代码示例(使用 JDBC 模拟用户认证和授权):
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class AccessControlExample {
public static void main(String[] args) {
try {
// 模拟用户认证
String username = "user1";
String password = "password1";
if (authenticateUser(username, password)) {
// 模拟授权操作,这里假设用户有查询权限
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb", username, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM your_table");
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
resultSet.close();
statement.close();
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean authenticateUser(String username, String password) {
// 简单模拟认证逻辑
return "user1".equals(username) && "password1".equals(password);
}
}
- 触发器
- 定义:触发器是一种特殊的存储过程,会在数据库发生特定事件(如插入、更新、删除操作)时自动执行。它可用于实现复杂的业务规则和数据完整性约束。
- 要点:触发器与特定表关联,在表上的特定操作发生时触发,可包含 SQL 语句和控制逻辑,能在操作前后执行。
- 应用:在电商系统中,当用户下单时,触发一个触发器更新库存表,减少相应商品的库存数量。
- SQL 代码示例(MySQL 触发器):
sql
-- 创建一个表用于存储订单信息
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
product_id INT,
quantity INT
);
-- 创建一个表用于存储商品库存信息
CREATE TABLE inventory (
product_id INT PRIMARY KEY,
stock INT
);
-- 创建触发器,在插入订单时更新库存
DELIMITER //
CREATE TRIGGER update_inventory_after_order
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
UPDATE inventory
SET stock = stock - NEW.quantity
WHERE product_id = NEW.product_id;
END //
DELIMITER ;
- 存储过程
- 定义:存储过程是一组预先编译好的 SQL 语句集合,存储在数据库中,用户可通过调用存储过程来执行这些语句,它能接受参数并返回结果。
- 要点:提高代码复用性和可维护性,减少网络传输开销(在数据库服务器端执行),可实现复杂业务逻辑。
- 应用:在银行系统中,可创建一个存储过程用于计算客户的利息,根据不同的存款类型和存款金额计算利息。
- SQL 代码示例(MySQL 存储过程):
sql
-- 创建一个存储过程用于计算利息
DELIMITER //
CREATE PROCEDURE calculate_interest(IN principal DECIMAL(10, 2), IN rate DECIMAL(5, 2), IN time INT, OUT interest DECIMAL(10, 2))
BEGIN
SET interest = (principal * rate * time) / 100;
END //
DELIMITER ;
-- 调用存储过程
SET @interest = 0;
CALL calculate_interest(1000, 5, 1, @interest);
SELECT @interest;
- 游标
- 定义:游标是一种数据库对象,用于在结果集中逐行移动,允许用户对结果集中的每一行进行单独处理。当 SQL 语句返回多行结果时,游标可方便地对这些结果进行遍历和操作。
- 要点:游标需先声明、打开、使用,最后关闭和释放。使用时要注意资源管理,避免内存泄漏。
- 应用:在处理大量数据时,可使用游标逐行处理数据,例如对数据库中的每一条记录进行数据清洗。
- SQL 代码示例(MySQL 游标):
sql
-- 创建一个表用于测试
CREATE TABLE test_table (
id INT PRIMARY KEY,
name VARCHAR(50)
);
INSERT INTO test_table VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
-- 使用游标遍历表中的记录
DELIMITER //
CREATE PROCEDURE iterate_table()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE v_id INT;
DECLARE v_name VARCHAR(50);
DECLARE cur CURSOR FOR SELECT id, name FROM test_table;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_id, v_name;
IF done THEN
LEAVE read_loop;
END IF;
-- 处理每一行数据,这里简单打印
SELECT CONCAT('ID: ', v_id, ', Name: ', v_name);
END LOOP;
CLOSE cur;
END //
DELIMITER ;
-- 调用存储过程
CALL iterate_table();
2. 如何实现数据库安全
- 物理安全
- 定义:保护数据库的物理设备,防止硬件损坏、自然灾害、盗窃等情况导致数据丢失或泄露。
- 要点:将数据库服务器置于安全机房,配备防火、防盗、防潮、防雷等设施;定期备份数据,并将备份存储在不同物理位置。
- 应用:大型企业会将数据库服务器存放在专业的数据中心,采用 RAID 技术提高数据冗余性,同时定期将数据备份到异地的数据中心。
- Java 代码示例(简单的文件备份模拟):
java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DatabaseBackupExample {
public static void main(String[] args) {
try {
File sourceFile = new File("path/to/your/database/file");
File backupFile = new File("path/to/backup/database/file");
FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(backupFile);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
fis.close();
fos.close();
System.out.println("Database backup completed.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 用户认证和授权
- 定义:通过用户名和密码等方式验证用户身份,只有合法用户才能访问数据库。同时,为不同用户分配不同操作权限,限制用户对数据库的访问范围。
- 要点:采用强密码策略,定期更换密码;采用多因素认证(如短信验证码、指纹识别等)提高认证安全性。
- 应用:在在线银行系统中,用户登录时需要输入用户名、密码和短信验证码进行身份验证,并且不同用户角色(如普通用户、管理员)具有不同的操作权限。
- Java 代码示例(使用 JDBC 进行用户认证):
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
public class UserAuthenticationExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter username: ");
String username = scanner.nextLine();
System.out.print("Enter password: ");
String password = scanner.nextLine();
try {
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb", username, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM your_table");
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
resultSet.close();
statement.close();
connection.close();
} catch (Exception e) {
System.out.println("Authentication failed.");
}
}
}
- 数据加密
- 定义:对数据库中的敏感数据进行加密处理,即使数据被非法获取,没有解密密钥也无法查看数据内容。
- 要点:可对整个数据库或特定列、表进行加密,选择合适的加密算法,如 AES 等。
- 应用:在医疗系统中,对患者的个人信息和病历数据进行加密存储,确保数据的安全性。
- Java 代码示例(使用 AES 加密和解密):
java
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class DataEncryptionExample {
public static void main(String[] args) throws Exception {
String plainText = "Sensitive data";
// 生成密钥
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
// 加密
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("Encrypted text: " + encryptedText);
// 解密
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
String decryptedText = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Decrypted text: " + decryptedText);
}
}
- 审计和监控
- 定义:记录数据库的所有操作,包括用户登录、数据查询、插入、更新、删除等,以便在发生安全事件时进行审计和追溯。同时,实时监控数据库的活动,及时发现异常行为并采取措施。
- 要点:设置合理的审计规则,只记录重要操作;使用监控工具对数据库的性能、连接数等指标进行实时监控。
- 应用:在金融机构的数据库系统中,对所有的资金交易操作进行审计记录,以便在出现问题时进行调查。
- Java 代码示例(简单的日志记录模拟):
java
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class DatabaseAuditExample {
public static void main(String[] args) {
try {
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb", "user", "password");
Statement statement = connection.createStatement();
String sql = "SELECT * FROM your_table";
// 记录操作日志
logOperation(sql);
statement.executeQuery(sql);
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void logOperation(String operation) {
try (PrintWriter writer = new PrintWriter(new FileWriter("database_audit.log", true))) {
writer.println(System.currentTimeMillis() + ": " + operation);
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. mysql 锁机制
- 共享锁(读锁)
- 定义:多个事务可同时对同一资源加共享锁,用于读取数据。加共享锁后,其他事务可继续加共享锁,但不能加排他锁,直到所有共享锁被释放。
- 要点:用于并发读取数据,提高并发性能。在读取数据时,使用
SELECT ... LOCK IN SHARE MODE
语句可加共享锁。 - 应用:在电商系统中,多个用户同时查看商品信息时,可使用共享锁,提高系统的并发处理能力。
- SQL 代码示例(MySQL 共享锁):
sql
-- 会话 1
START TRANSACTION;
SELECT * FROM products WHERE product_id = 1 LOCK IN SHARE MODE;
-- 这里可以继续执行其他操作
COMMIT;
-- 会话 2
START TRANSACTION;
SELECT * FROM products WHERE product_id = 1 LOCK IN SHARE MODE;
-- 可以正常执行,因为共享锁不互斥
COMMIT;
- 排他锁(写锁)
- 定义:一个事务对资源加排他锁后,其他事务不能再对该资源加任何类型的锁,直到排他锁被释放。排他锁用于修改数据,保证数据的一致性。
- 要点:在更新、删除数据时,使用
SELECT ... FOR UPDATE
语句可加排他锁。 - 应用:在银行系统中,当用户进行转账操作时,对涉及的账户记录加排他锁,防止其他事务同时修改该账户信息。
- SQL 代码示例(MySQL 排他锁):
sql
-- 会话 1
START TRANSACTION;
SELECT * FROM accounts WHERE account_id = 1 FOR UPDATE;
-- 进行账户更新操作
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
-- 会话 2
START TRANSACTION;
SELECT * FROM accounts WHERE account_id = 1 FOR UPDATE;
-- 会被阻塞,直到会话 1 的排他锁释放
COMMIT;
- 表级锁
- 定义:对整个表加锁,操作简单,但并发性能较低。当一个事务对表加锁后,其他事务不能对该表进行任何操作,直到锁被释放。
- 要点:在执行
LOCK TABLES
语句时可对表加锁,使用UNLOCK TABLES
语句释放锁。 - 应用:在进行全量数据更新或批量操作时,可使用表级锁,确保数据的一致性。
- SQL 代码示例(MySQL 表级锁):
sql
-- 会话 1
LOCK TABLES products WRITE;
-- 进行表的更新操作
UPDATE products SET price = price * 1.1;
UNLOCK TABLES;
-- 会话 2
-- 在会话 1 释放锁之前,以下操作会被阻塞
SELECT * FROM products;
- 行级锁
- 定义:只对需要操作的行加锁,并发性能较高。不同事务可同时对不同的行进行操作,减少锁的竞争。
- 要点:InnoDB 存储引擎支持行级锁,通过索引来实现。如果没有使用索引,行级锁会退化为表级锁。
- 应用:在高并发的电商系统中,对商品库存记录使用行级锁,不同用户可以同时修改不同商品的库存。
- SQL 代码示例(MySQL 行级锁):
sql
-- 会话 1
START TRANSACTION;
SELECT * FROM products WHERE product_id = 1 FOR UPDATE;
-- 进行行的更新操作
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
COMMIT;
-- 会话 2
START TRANSACTION;
SELECT * FROM products WHERE product_id = 2 FOR UPDATE;
-- 可以正常执行,因为操作的是不同的行
UPDATE products SET stock = stock - 1 WHERE product_id = 2;
COMMIT;
4. 项目中如何实现事务
- 使用数据库的事务支持
- 定义:大多数数据库都提供事务支持,通过
BEGIN TRANSACTION
、COMMIT
和ROLLBACK
语句来实现事务的开始、提交和回滚。 - 要点:在项目中,将需要作为一个事务处理的 SQL 语句放在
BEGIN TRANSACTION
和COMMIT
之间,若执行过程中出现异常,则执行ROLLBACK
语句回滚事务。 - 应用:在电商系统的订单处理中,将订单创建、库存扣减、资金扣除等操作放在一个事务中,确保数据的一致性。
- SQL 代码示例(MySQL 事务):
sql
START TRANSACTION;
-- 插入订单记录
INSERT INTO orders (order_id, product_id, quantity) VALUES (1, 1, 1);
-- 更新库存
UPDATE inventory SET stock = stock - 1 WHERE product_id = 1;
-- 检查是否有足够的库存
IF (SELECT stock FROM inventory WHERE product_id = 1) < 0 THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
- 使用编程语言的事务管理机制
- 定义:许多编程语言的数据库连接库都提供事务管理接口,例如 Java 的 JDBC 提供
Connection.setAutoCommit(false)
方法开启事务,Connection.commit()
方法提交事务,Connection.rollback()
方法回滚事务。 - 要点:在代码中,将需要作为一个事务处理的数据库操作封装在一个事务块中,通过捕获异常来决定是否提交或回滚事务。
- 应用:在 Java Web 项目中,使用 JDBC 进行数据库操作时,将业务逻辑相关的数据库操作封装在一个事务中。
- Java 代码示例(使用 JDBC 实现事务):
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class TransactionExample {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb", "user", "password");
connection.setAutoCommit(false);
statement = connection.createStatement();
// 插入订单记录
statement.executeUpdate("INSERT INTO orders (order_id, product_id, quantity) VALUES (1, 1, 1)");
// 更新库存
statement.executeUpdate("UPDATE inventory SET stock = stock - 1 WHERE product_id = 1");
connection.commit();
} catch (Exception e) {
try {
if (connection != null) {
connection.rollback();
}
} catch (Exception ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
try {
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.setAutoCommit(true);
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
5. 数据库设计一般设计成第几范式
第一范式(1NF)
- 定义:要求数据库表的每一列都是不可再分的原子值,即表中的每个字段都应该是单一的值,不能包含多个值。
- 要点:确保表中的数据具有原子性,避免数据冗余和不一致性。
- 应用:在设计用户信息表时,将用户的联系方式拆分为手机号码、电子邮箱等单独的字段,而不是将多个联系方式放在一个字段中。
- SQL 代码示例(符合 1NF 的表设计):
sql
CREATE TABLE users (
user_id INT PRIMARY KEY,
name VARCHAR(50),
phone_number VARCHAR(20),
email VARCHAR(50)
);
- 第二范式(2NF)
- 定义:在满足第一范式的基础上,要求表中的每一个非主属性完全依赖于主键,而不是部分依赖于主键。
- 要点:消除非主属性对主键的部分依赖,减少数据冗余。
- 应用:在设计订单表时,将订单信息和商品信息分开,避免商品信息因为订单编号的不同而重复存储。
- SQL 代码示例(符合 2NF 的表设计):
sql
-- 订单表
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
order_date DATE,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- 订单商品表
CREATE TABLE order_items (
order_item_id INT PRIMARY KEY,
order_id INT,
product_id INT,
quantity INT,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
- 第三范式(3NF)
- 定义:在满足第二范式的基础上,要求表中的每一个非主属性都不传递依赖于主键。
- 要点:消除非主属性对主键的传递依赖,进一步减少数据冗余。
- 应用:在设计员工信息表时,将部门信息单独存储在一个部门表中,避免员工信息因为部门编号的不同而重复存储部门名称等信息。
- SQL 代码示例(符合 3NF 的表设计):
sql
-- 员工表
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
name VARCHAR(50),
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
-- 部门表
CREATE TABLE departments (
department_id INT PRIMARY KEY,
department_name VARCHAR(50)
);
6. mysql 用的什么版本,5.7 跟 5.6 有啥区别
- 性能优化
- 定义:MySQL 5.7 在性能方面进行了诸多优化,例如引入多线程复制、改进查询优化器等,以提高数据库的处理能力和响应速度。
- 要点:多线程复制可提高主从复制的性能,减少复制延迟;查询优化器能更智能地选择执行计划,提高查询性能。
- 应用:在高并发的电商系统中,使用 MySQL 5.7 的多线程复制可以更快地将主库的数据同步到从库,确保数据的一致性。
- SQL 代码示例(无特定代码,可通过性能测试对比):
sql
-- 可以使用 EXPLAIN 分析查询语句的执行计划
EXPLAIN SELECT * FROM products WHERE category = 'electronics';
- 安全性增强
- 定义:MySQL 5.7 增强了安全性,引入更严格的密码策略、支持角色管理等,以保护数据库免受非法访问和攻击。
- 要点:新的密码策略要求用户使用更复杂的密码,提高密码的安全性;角色管理可更方便地管理用户的权限。
- 应用:在企业级数据库系统中,使用 MySQL 5.7 的角色管理功能可以根据员工的岗位和职责分配不同的数据库操作权限。
- SQL 代码示例(创建角色和分配权限):
sql
-- 创建角色
CREATE ROLE 'read_only_role';
-- 授予角色查询权限
GRANT SELECT ON your_database.* TO 'read_only_role';
-- 创建用户并分配角色
CREATE USER 'new_user'@'localhost' IDENTIFIED BY 'password';
GRANT 'read_only_role' TO 'new_user'@'localhost';
- 功能增强
- 定义:MySQL 5.7 增加了一些新功能,例如 JSON 数据类型、窗口函数等,以满足更复杂的业务需求。
- 要点:JSON 数据类型可方便地存储和查询 JSON 格式的数据;窗口函数可在不使用子查询的情况下进行复杂的分析查询。
- 应用:在社交网络系统中,使用 JSON 数据类型存储用户的个性化设置信息;使用窗口函数分析用户的活跃度排名。
- SQL 代码示例(JSON 数据类型和窗口函数):
sql
-- 创建包含 JSON 字段的表
CREATE TABLE users (
user_id INT PRIMARY KEY,
name VARCHAR(50),
settings JSON
);
-- 插入 JSON 数据
INSERT INTO users (user_id, name, settings) VALUES (1, 'Alice', '{"theme": "dark", "notifications": true}');
-- 查询 JSON 数据
SELECT user_id, name, settings->'$.theme' AS theme FROM users;
-- 使用窗口函数计算排名
SELECT product_id, price, RANK() OVER (ORDER BY price DESC) AS price_rank FROM products;
7. 有一个表 (三个字段:姓名, id, 分数) 要求查出平均分大于 80 的 id 然后分数降序排序。
sql
-- 假设表名为 student_scores
SELECT id
FROM student_scores
GROUP BY id
HAVING AVG(分数) > 80
ORDER BY 分数 DESC;
- 定义:通过
GROUP BY
对id
进行分组,使用AVG
函数计算每个id
的平均分,HAVING
筛选出平均分大于 80 的id
,最后使用ORDER BY
按分数降序排序。 - 要点:
GROUP BY
用于分组,HAVING
用于筛选分组后的结果,ORDER BY
用于排序。 - 应用:在学校的成绩管理系统中,筛选出平均分优秀的学生的
id
,以便进行奖励。
8. 为什么 mysql 事务能保证失败回滚
- 定义
MySQL 的事务通过日志系统(如 InnoDB 的 redo log 和 undo log)来保证失败回滚。当一个事务开始时,MySQL 会记录所有的操作到 undo log 中。如果事务执行过程中出现错误,MySQL 会根据 undo log 中的记录将数据恢复到事务开始前的状态。同时,redo log 用于在数据库崩溃后恢复数据,保证事务的持久性。
- 要点
- undo log 是实现事务回滚的关键,它记录了事务对数据的修改操作,以便在需要时进行反向操作。
- redo log 保证了事务的持久性,即使数据库崩溃,也可以通过 redo log 恢复到崩溃前的状态。
- 应用
在银行转账系统中,如果在转账过程中出现系统故障,事务可以回滚,保证资金的安全。
SQL 代码示例(模拟事务回滚)
sql
START TRANSACTION;
-- 模拟转账操作
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- 模拟出现错误
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Simulated error';
ROLLBACK;
9. 已知学生表, 成绩表, 如何查找学生总成绩大于 500 的学生的姓名跟总分
假设学生表名为 students
,包含字段 id
和 name
;成绩表名为 scores
,包含字段 student_id
和 score
。
sql
SELECT s.name, SUM(sc.score) AS total_score
FROM students s
JOIN scores sc ON s.id = sc.student_id
GROUP BY s.id, s.name
HAVING SUM(sc.score) > 500;
- 定义:通过
JOIN
将学生表和成绩表关联,使用GROUP BY
按学生的id
和name
分组,SUM
计算每个学生的总成绩,HAVING
筛选出总成绩大于 500 的学生。 - 要点:
JOIN
用于关联两个表,GROUP BY
用于分组,SUM
用于计算总成绩,HAVING
用于筛选分组后的结果。 - 应用:在学校的成绩统计系统中,查找总成绩优秀的学生,以便进行表彰。
10. 在一个整形数组中, 找出第三大的数, 注意时间效率
java
import java.util.TreeSet;
public class ThirdLargestNumber {
public static int thirdLargest(int[] nums) {
TreeSet<Integer> set = new TreeSet<>();
for (int num : nums) {
set.add(num);
if (set.size() > 3) {
set.pollFirst();
}
}
return set.size() < 3 ? set.last() : set.first();
}
public static void main(String[] args) {
int[] nums = {3, 2, 1, 5, 6, 4};
System.out.println(thirdLargest(nums));
}
}
- 定义:使用
TreeSet
存储数组元素,TreeSet
会自动排序。遍历数组,将元素添加到TreeSet
中,若TreeSet
大小超过 3,则移除最小元素。最后根据TreeSet
大小返回相应结果。 - 要点:
TreeSet
可自动排序,pollFirst()
移除最小元素,last()
获取最大元素,first()
获取最小元素。 - 应用:在游戏排行榜系统中,找出排名第三的玩家分数。该算法时间复杂度为 \(O(n log k)\),其中 n 是数组长度,k 是要找的第 k 大的数(这里 \(k = 3\))。若要找出第 k 大的数,可将
TreeSet
大小限制改为 k。
友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读