Amazon 25NG SDE1 三轮面试经验分享:BQ、OOD和Coding - Oavoservice

🎤 Amazon 25NG SDE1 0728 三轮面试面经
👩💻 Candidate 在 Amazon 25NG SDE1 三轮面试中经历了完整的面试流程,包括BQ、OOD系统设计和Coding算法题。
📋 第一轮:BQ面试 (Bar Raiser)
这一轮是Bar Raiser面试,主要考察候选人的行为问题和领导力原则。
🎯 主要问题
- Out of scope: 如何处理项目范围超出预期的情况
- Tight deadline: 在紧张的时间限制下如何交付项目
- Learn new things: 如何快速学习新技术和知识
- Earn trust: 如何在团队中建立信任关系
💡 回答要点
Out of scope 处理策略:
- 及时与stakeholder沟通,明确项目边界
- 重新评估优先级,调整项目计划
- 寻求额外资源或延长timeline
- 记录决策过程和影响分析
Tight deadline 应对方法:
- 分解任务,识别关键路径
- 与团队协作,合理分配工作
- 采用敏捷开发方法,快速迭代
- 及时向上级汇报进度和风险
📋 第二轮:OOD系统设计
题目:设计Linux文件系统
📝 题目要求
- 设计一个类似Linux的文件系统
- 支持文件和目录的创建、删除、移动
- 支持权限管理
- 支持软链接和硬链接
- 考虑性能和扩展性
🎯 主要问题
- Miss deadline: 如何处理项目延期的情况
- Out of scope: 如何处理需求变更和范围扩大
💡 系统设计思路
使用面向对象设计方法:
- 定义核心类和接口
- 设计文件系统结构
- 实现权限管理机制
- 考虑并发访问和性能优化
💻 代码实现
import java.util.*;
import java.time.LocalDateTime;
// 文件系统核心接口
interface FileSystemNode {
String getName();
String getPath();
LocalDateTime getCreatedTime();
LocalDateTime getModifiedTime();
int getSize();
boolean isDirectory();
}
// 权限枚举
enum Permission {
READ, WRITE, EXECUTE
}
// 用户和组
class User {
private String username;
private String group;
public User(String username, String group) {
this.username = username;
this.group = group;
}
public String getUsername() { return username; }
public String getGroup() { return group; }
}
// 权限管理
class FilePermissions {
private Map<String, Set<Permission>> userPermissions;
private Map<String, Set<Permission>> groupPermissions;
private Set<Permission> otherPermissions;
public FilePermissions() {
this.userPermissions = new HashMap<>();
this.groupPermissions = new HashMap<>();
this.otherPermissions = new HashSet<>();
}
public void setUserPermission(String user, Permission permission) {
userPermissions.computeIfAbsent(user, k -> new HashSet<>()).add(permission);
}
public void setGroupPermission(String group, Permission permission) {
groupPermissions.computeIfAbsent(group, k -> new HashSet<>()).add(permission);
}
public void setOtherPermission(Permission permission) {
otherPermissions.add(permission);
}
public boolean hasPermission(User user, Permission permission) {
// 检查用户权限
if (userPermissions.getOrDefault(user.getUsername(), Collections.emptySet()).contains(permission)) {
return true;
}
// 检查组权限
if (groupPermissions.getOrDefault(user.getGroup(), Collections.emptySet()).contains(permission)) {
return true;
}
// 检查其他权限
return otherPermissions.contains(permission);
}
}
// 文件类
class File implements FileSystemNode {
private String name;
private String path;
private byte[] content;
private LocalDateTime createdTime;
private LocalDateTime modifiedTime;
private FilePermissions permissions;
private int referenceCount; // 硬链接计数
public File(String name, String path) {
this.name = name;
this.path = path;
this.content = new byte[0];
this.createdTime = LocalDateTime.now();
this.modifiedTime = LocalDateTime.now();
this.permissions = new FilePermissions();
this.referenceCount = 1;
}
@Override
public String getName() { return name; }
@Override
public String getPath() { return path; }
@Override
public LocalDateTime getCreatedTime() { return createdTime; }
@Override
public LocalDateTime getModifiedTime() { return modifiedTime; }
@Override
public int getSize() { return content.length; }
@Override
public boolean isDirectory() { return false; }
public byte[] getContent() { return content; }
public void setContent(byte[] content) {
this.content = content;
this.modifiedTime = LocalDateTime.now();
}
public FilePermissions getPermissions() { return permissions; }
public int getReferenceCount() { return referenceCount; }
public void incrementReferenceCount() { referenceCount++; }
public void decrementReferenceCount() { referenceCount--; }
}
// 目录类
class Directory implements FileSystemNode {
private String name;
private String path;
private LocalDateTime createdTime;
private LocalDateTime modifiedTime;
private FilePermissions permissions;
private Map<String, FileSystemNode> children;
private Directory parent;
public Directory(String name, String path) {
this.name = name;
this.path = path;
this.createdTime = LocalDateTime.now();
this.modifiedTime = LocalDateTime.now();
this.permissions = new FilePermissions();
this.children = new HashMap<>();
}
@Override
public String getName() { return name; }
@Override
public String getPath() { return path; }
@Override
public LocalDateTime getCreatedTime() { return createdTime; }
@Override
public LocalDateTime getModifiedTime() { return modifiedTime; }
@Override
public int getSize() {
return children.values().stream().mapToInt(FileSystemNode::getSize).sum();
}
@Override
public boolean isDirectory() { return true; }
public Map<String, FileSystemNode> getChildren() { return children; }
public Directory getParent() { return parent; }
public void setParent(Directory parent) { this.parent = parent; }
public FilePermissions getPermissions() { return permissions; }
public void addChild(FileSystemNode node) {
children.put(node.getName(), node);
if (node instanceof Directory) {
((Directory) node).setParent(this);
}
this.modifiedTime = LocalDateTime.now();
}
public void removeChild(String name) {
children.remove(name);
this.modifiedTime = LocalDateTime.now();
}
public FileSystemNode getChild(String name) {
return children.get(name);
}
}
// 软链接类
class SymbolicLink implements FileSystemNode {
private String name;
private String path;
private String targetPath;
private LocalDateTime createdTime;
private LocalDateTime modifiedTime;
private FilePermissions permissions;
public SymbolicLink(String name, String path, String targetPath) {
this.name = name;
this.path = path;
this.targetPath = targetPath;
this.createdTime = LocalDateTime.now();
this.modifiedTime = LocalDateTime.now();
this.permissions = new FilePermissions();
}
@Override
public String getName() { return name; }
@Override
public String getPath() { return path; }
@Override
public LocalDateTime getCreatedTime() { return createdTime; }
@Override
public LocalDateTime getModifiedTime() { return modifiedTime; }
@Override
public int getSize() { return targetPath.length(); }
@Override
public boolean isDirectory() { return false; }
public String getTargetPath() { return targetPath; }
public FilePermissions getPermissions() { return permissions; }
}
// 文件系统主类
class LinuxFileSystem {
private Directory root;
private User currentUser;
private Map<String, User> users;
public LinuxFileSystem() {
this.root = new Directory("/", "/");
this.users = new HashMap<>();
this.currentUser = new User("root", "root");
}
public void setCurrentUser(User user) {
this.currentUser = user;
}
public User getCurrentUser() {
return currentUser;
}
// 创建文件
public boolean createFile(String path, String content) {
try {
String[] parts = path.split("/");
Directory current = root;
// 导航到父目录
for (int i = 1; i < parts.length - 1; i++) {
if (parts[i].isEmpty()) continue;
FileSystemNode node = current.getChild(parts[i]);
if (node == null || !node.isDirectory()) {
return false; // 路径不存在
}
current = (Directory) node;
}
// 检查权限
if (!current.getPermissions().hasPermission(currentUser, Permission.WRITE)) {
return false;
}
// 创建文件
String fileName = parts[parts.length - 1];
File file = new File(fileName, path);
file.setContent(content.getBytes());
current.addChild(file);
return true;
} catch (Exception e) {
return false;
}
}
// 创建目录
public boolean createDirectory(String path) {
try {
String[] parts = path.split("/");
Directory current = root;
// 导航到父目录
for (int i = 1; i < parts.length - 1; i++) {
if (parts[i].isEmpty()) continue;
FileSystemNode node = current.getChild(parts[i]);
if (node == null || !node.isDirectory()) {
return false;
}
current = (Directory) node;
}
// 检查权限
if (!current.getPermissions().hasPermission(currentUser, Permission.WRITE)) {
return false;
}
// 创建目录
String dirName = parts[parts.length - 1];
Directory directory = new Directory(dirName, path);
current.addChild(directory);
return true;
} catch (Exception e) {
return false;
}
}
// 删除文件或目录
public boolean delete(String path) {
try {
String[] parts = path.split("/");
Directory current = root;
// 导航到父目录
for (int i = 1; i < parts.length - 1; i++) {
if (parts[i].isEmpty()) continue;
FileSystemNode node = current.getChild(parts[i]);
if (node == null || !node.isDirectory()) {
return false;
}
current = (Directory) node;
}
// 检查权限
if (!current.getPermissions().hasPermission(currentUser, Permission.WRITE)) {
return false;
}
// 删除节点
String name = parts[parts.length - 1];
FileSystemNode node = current.getChild(name);
if (node == null) {
return false;
}
// 如果是文件,处理硬链接计数
if (node instanceof File) {
File file = (File) node;
file.decrementReferenceCount();
if (file.getReferenceCount() > 0) {
return true; // 还有其他硬链接,不实际删除
}
}
current.removeChild(name);
return true;
} catch (Exception e) {
return false;
}
}
// 创建软链接
public boolean createSymbolicLink(String linkPath, String targetPath) {
try {
String[] parts = linkPath.split("/");
Directory current = root;
// 导航到父目录
for (int i = 1; i < parts.length - 1; i++) {
if (parts[i].isEmpty()) continue;
FileSystemNode node = current.getChild(parts[i]);
if (node == null || !node.isDirectory()) {
return false;
}
current = (Directory) node;
}
// 检查权限
if (!current.getPermissions().hasPermission(currentUser, Permission.WRITE)) {
return false;
}
// 创建软链接
String linkName = parts[parts.length - 1];
SymbolicLink link = new SymbolicLink(linkName, linkPath, targetPath);
current.addChild(link);
return true;
} catch (Exception e) {
return false;
}
}
// 列出目录内容
public List<String> listDirectory(String path) {
try {
String[] parts = path.split("/");
Directory current = root;
// 导航到目标目录
for (int i = 1; i < parts.length; i++) {
if (parts[i].isEmpty()) continue;
FileSystemNode node = current.getChild(parts[i]);
if (node == null || !node.isDirectory()) {
return Collections.emptyList();
}
current = (Directory) node;
}
// 检查权限
if (!current.getPermissions().hasPermission(currentUser, Permission.READ)) {
return Collections.emptyList();
}
return new ArrayList<>(current.getChildren().keySet());
} catch (Exception e) {
return Collections.emptyList();
}
}
// 读取文件内容
public String readFile(String path) {
try {
String[] parts = path.split("/");
Directory current = root;
// 导航到父目录
for (int i = 1; i < parts.length - 1; i++) {
if (parts[i].isEmpty()) continue;
FileSystemNode node = current.getChild(parts[i]);
if (node == null || !node.isDirectory()) {
return null;
}
current = (Directory) node;
}
// 获取文件
String fileName = parts[parts.length - 1];
FileSystemNode node = current.getChild(fileName);
if (node instanceof SymbolicLink) {
// 处理软链接
SymbolicLink link = (SymbolicLink) node;
return readFile(link.getTargetPath());
} else if (node instanceof File) {
// 检查权限
if (!node.getPermissions().hasPermission(currentUser, Permission.READ)) {
return null;
}
File file = (File) node;
return new String(file.getContent());
}
return null;
} catch (Exception e) {
return null;
}
}
}
// 测试方法
public class FileSystemTest {
public static void main(String[] args) {
LinuxFileSystem fs = new LinuxFileSystem();
// 创建目录结构
fs.createDirectory("/home");
fs.createDirectory("/home/user");
fs.createDirectory("/var");
fs.createDirectory("/var/log");
// 创建文件
fs.createFile("/home/user/document.txt", "This is a test document");
fs.createFile("/var/log/app.log", "Application log content");
// 创建软链接
fs.createSymbolicLink("/home/user/link_to_doc", "/home/user/document.txt");
// 列出目录内容
System.out.println("Root directory: " + fs.listDirectory("/"));
System.out.println("Home directory: " + fs.listDirectory("/home"));
System.out.println("User directory: " + fs.listDirectory("/home/user"));
// 读取文件
System.out.println("Document content: " + fs.readFile("/home/user/document.txt"));
System.out.println("Link content: " + fs.readFile("/home/user/link_to_doc"));
}
}
📋 第三轮:Coding算法题
题目:LeetCode 2411 - Smallest Subarrays With Maximum Bitwise OR
📝 题目要求
给定一个数组nums,对于每个位置i,找到以i为起点的最短子数组,使得该子数组的按位OR结果等于从i到数组末尾的按位OR结果。
❓ 问题分析
这是一个典型的位运算和滑动窗口问题:
- 核心思想:利用位运算的性质和滑动窗口
- 关键点:OR运算的单调性
- 优化:从右到左遍历,维护OR值
💡 思路设计
使用位运算和滑动窗口:
- 从右到左遍历数组
- 维护当前OR值和滑动窗口
- 利用OR运算的单调性优化
- 记录每个位置的最短子数组长度
💻 代码实现
import java.util.*;
public class SmallestSubarraysWithMaxOR {
// 方法1:暴力解法 O(n²)
public int[] smallestSubarraysBruteForce(int[] nums) {
int n = nums.length;
int[] result = new int[n];
for (int i = 0; i < n; i++) {
int maxOR = 0;
// 计算从i到末尾的OR值
for (int j = i; j < n; j++) {
maxOR |= nums[j];
}
// 找到最短子数组
int currentOR = 0;
for (int j = i; j < n; j++) {
currentOR |= nums[j];
if (currentOR == maxOR) {
result[i] = j - i + 1;
break;
}
}
}
return result;
}
// 方法2:优化解法 O(n)
public int[] smallestSubarrays(int[] nums) {
int n = nums.length;
int[] result = new int[n];
// 从右到左遍历
for (int i = n - 1; i >= 0; i--) {
result[i] = 1;
// 检查是否可以与右边的元素合并
for (int j = i + 1; j < n; j++) {
// 如果当前元素与右边某个元素的OR值等于右边元素的OR值
// 说明当前元素不会增加OR值,可以合并
if ((nums[i] | nums[j]) == nums[j]) {
result[i] = result[j] + (j - i);
break;
}
}
}
return result;
}
// 方法3:最优解法 - 使用位运算优化
public int[] smallestSubarraysOptimal(int[] nums) {
int n = nums.length;
int[] result = new int[n];
int[] last = new int[30]; // 记录每个bit位最后出现的位置
Arrays.fill(last, -1);
for (int i = n - 1; i >= 0; i--) {
result[i] = 1;
// 更新每个bit位的最后出现位置
for (int j = 0; j < 30; j++) {
if ((nums[i] & (1 << j)) != 0) {
last[j] = i;
}
}
// 找到最远的bit位位置
int maxPos = i;
for (int j = 0; j < 30; j++) {
if (last[j] != -1) {
maxPos = Math.max(maxPos, last[j]);
}
}
result[i] = maxPos - i + 1;
}
return result;
}
// 方法4:滑动窗口解法
public int[] smallestSubarraysSlidingWindow(int[] nums) {
int n = nums.length;
int[] result = new int[n];
// 计算每个位置到末尾的OR值
int[] suffixOR = new int[n];
suffixOR[n - 1] = nums[n - 1];
for (int i = n - 2; i >= 0; i--) {
suffixOR[i] = nums[i] | suffixOR[i + 1];
}
// 对每个位置找最短子数组
for (int i = 0; i < n; i++) {
int targetOR = suffixOR[i];
int currentOR = 0;
int minLength = n - i;
// 滑动窗口找最短子数组
for (int j = i; j < n; j++) {
currentOR |= nums[j];
if (currentOR == targetOR) {
minLength = j - i + 1;
break;
}
}
result[i] = minLength;
}
return result;
}
// 测试方法
public static void main(String[] args) {
SmallestSubarraysWithMaxOR solution = new SmallestSubarraysWithMaxOR();
// 测试用例1
int[] nums1 = {1, 0, 2, 1, 3};
int[] result1 = solution.smallestSubarrays(nums1);
System.out.println("测试1: " + Arrays.toString(result1));
// 期望输出: [3, 3, 2, 2, 1]
// 测试用例2
int[] nums2 = {1, 2};
int[] result2 = solution.smallestSubarrays(nums2);
System.out.println("测试2: " + Arrays.toString(result2));
// 期望输出: [2, 1]
// 测试用例3
int[] nums3 = {0};
int[] result3 = solution.smallestSubarrays(nums3);
System.out.println("测试3: " + Arrays.toString(result3));
// 期望输出: [1]
// 性能测试
int[] largeNums = new int[1000];
for (int i = 0; i < 1000; i++) {
largeNums[i] = (int) (Math.random() * 1000);
}
long startTime = System.currentTimeMillis();
solution.smallestSubarraysOptimal(largeNums);
long endTime = System.currentTimeMillis();
System.out.println("优化算法执行时间: " + (endTime - startTime) + "ms");
}
}
🔍 算法分析
- 暴力解法时间复杂度:O(n²)
- 优化解法时间复杂度:O(n)
- 空间复杂度:O(1)(除了结果数组)
- 核心思想:位运算 + 滑动窗口 + 单调性
📊 测试用例
// 测试用例1
nums = [1, 0, 2, 1, 3]
期望输出: [3, 3, 2, 2, 1]
// 测试用例2
nums = [1, 2]
期望输出: [2, 1]
// 测试用例3
nums = [0]
期望输出: [1]
✨ 面试官反馈
面试官对系统设计和算法实现都很满意:"Great system design! You've covered all the key components of a file system. The coding solution is also optimal with good time complexity."
面试技巧分享
在 Amazon 25NG SDE1 三轮面试中,除了技术实现,面试官还会关注:
1. BQ回答技巧
使用STAR方法(Situation, Task, Action, Result)来回答行为问题,突出具体的技术细节和量化结果。
2. 系统设计能力
能够从需求分析开始,逐步设计系统架构,考虑扩展性、性能和可靠性。
3. 算法优化思维
提供多种解法,从暴力解法开始,逐步优化到最优解,展现算法思维。
4. 代码质量
写出清晰、可读的代码,包含适当的注释和错误处理。
Oavoservice 实时辅助服务
在这次 Amazon 25NG SDE1 三轮面试中,我们的实时辅助系统发挥了关键作用:
- BQ指导:帮助候选人准备Amazon领导力原则的回答
- 系统设计:提供Linux文件系统的完整设计方案
- 算法实现:协助完成LeetCode 2411的最优解法
- 面试技巧:提供实用的面试建议和注意事项
结语
Amazon 25NG SDE1 三轮面试全面考察了候选人的技术能力、系统设计能力和行为表现。BQ面试考察领导力原则,OOD面试考察系统设计能力,Coding面试考察算法实现能力。在面试中,能够全面展示这些能力,展现了候选人的综合素质。
如果你正在准备 Amazon 或其他公司的技术面试,欢迎联系 Oavoservice。我们拥有丰富的面试经验和专业的辅助团队,能够在你最需要的时候提供及时有效的帮助。
Oavoservice - 让每一次面试都成为成功的机会!
需要辅助的同学随时dd哦,vo开始前支持mock!