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

2025-07-28
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: 如何处理需求变更和范围扩大

💡 系统设计思路

使用面向对象设计方法:

  1. 定义核心类和接口
  2. 设计文件系统结构
  3. 实现权限管理机制
  4. 考虑并发访问和性能优化

💻 代码实现

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值

💡 思路设计

使用位运算和滑动窗口:

  1. 从右到左遍历数组
  2. 维护当前OR值和滑动窗口
  3. 利用OR运算的单调性优化
  4. 记录每个位置的最短子数组长度

💻 代码实现

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!