Meta PE SDE Coding Round 面试经验分享:回文判断和数据分析算法题 - Oavoservice

2025-06-17
Meta PE SDE Coding Round 面试经验分享:回文判断和数据分析算法题 - Oavoservice 封面

🎤 Meta PE SDE 0617 Coding Round 面经

👩‍💻 Candidate 在 Meta PE SDE Coding Round 面试中遇到了两个经典的算法题。

Pe的面经,烙印面试官上来就要求写Java,整体面试体验还不错!

💻 Coding 1: 回文判断

题目:判断 string 是不是 palindrome

📝 题目要求

  • 需要 ignore lower/upper 的 case
  • 比较的时候 skip 掉标点符号
  • 只考虑字母和数字字符
  • 使用双指针算法

❓ 问题分析

这是一个经典的回文判断问题

  • 核心思想:双指针从两端向中间移动
  • 关键点:字符预处理和边界条件
  • 优化:可以原地处理,不需要额外空间

💡 思路设计

使用双指针方法:

  1. 左指针从字符串开头开始
  2. 右指针从字符串末尾开始
  3. 跳过非字母数字字符
  4. 比较字符(忽略大小写)
  5. 向中间移动指针

💻 代码实现

public class PalindromeChecker {
    
    // 方法1:双指针 - 最优解
    public boolean isPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        
        int left = 0;
        int right = s.length() - 1;
        
        while (left < right) {
            // 跳过非字母数字字符
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                left++;
            }
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                right--;
            }
            
            // 比较字符(忽略大小写)
            if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                return false;
            }
            
            left++;
            right--;
        }
        
        return true;
    }
    
    // 方法2:预处理字符串
    public boolean isPalindromePreprocess(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        
        // 预处理:只保留字母数字字符并转为小写
        StringBuilder cleaned = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (Character.isLetterOrDigit(c)) {
                cleaned.append(Character.toLowerCase(c));
            }
        }
        
        String cleanedStr = cleaned.toString();
        int left = 0;
        int right = cleanedStr.length() - 1;
        
        while (left < right) {
            if (cleanedStr.charAt(left) != cleanedStr.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        
        return true;
    }
    
    // 方法3:递归解法
    public boolean isPalindromeRecursive(String s) {
        if (s == null || s.length() == 0) {
            return true;
        }
        
        return isPalindromeHelper(s, 0, s.length() - 1);
    }
    
    private boolean isPalindromeHelper(String s, int left, int right) {
        if (left >= right) {
            return true;
        }
        
        // 跳过非字母数字字符
        while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
            left++;
        }
        while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
            right--;
        }
        
        // 比较字符
        if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
            return false;
        }
        
        return isPalindromeHelper(s, left + 1, right - 1);
    }
    
    // 测试方法
    public static void main(String[] args) {
        PalindromeChecker checker = new PalindromeChecker();
        
        // 测试用例1:基本回文
        String test1 = "A man, a plan, a canal: Panama";
        System.out.println("测试1: " + checker.isPalindrome(test1)); // true
        
        // 测试用例2:非回文
        String test2 = "race a car";
        System.out.println("测试2: " + checker.isPalindrome(test2)); // false
        
        // 测试用例3:空字符串
        String test3 = "";
        System.out.println("测试3: " + checker.isPalindrome(test3)); // true
        
        // 测试用例4:单个字符
        String test4 = "a";
        System.out.println("测试4: " + checker.isPalindrome(test4)); // true
        
        // 测试用例5:只有标点符号
        String test5 = ".,";
        System.out.println("测试5: " + checker.isPalindrome(test5)); // true
        
        // 测试用例6:数字回文
        String test6 = "12321";
        System.out.println("测试6: " + checker.isPalindrome(test6)); // true
    }
}

🔍 算法分析

  • 时间复杂度:O(n),其中n是字符串长度
  • 空间复杂度:O(1)(双指针方法)或O(n)(预处理方法)
  • 核心思想:双指针 + 字符过滤

✨ 面试官反馈

面试官对双指针解法很满意:"Great! Two pointer approach is optimal. You've handled the edge cases well."

💻 Coding 2: 数据分析

题目:Data Analysis - 恐龙数据分析

📝 题目要求

  • 接收两个CSV文件
  • 两个文件都有恐龙的meta data
  • 按照速度打印恐龙,需要降序排列
  • 涉及文件IO操作

❓ 问题分析

这是一个典型的文件IO和数据处理问题

  • 核心思想:读取CSV文件,解析数据,排序输出
  • 关键点:CSV解析和排序算法
  • 挑战:处理文件格式和数据类型转换

💡 思路设计

使用Java文件IO和集合排序:

  1. 定义恐龙数据模型
  2. 读取CSV文件
  3. 解析每行数据
  4. 合并两个文件的数据
  5. 按速度降序排序
  6. 输出结果

💻 代码实现

import java.io.*;
import java.util.*;

public class DinosaurDataAnalyzer {
    
    // 恐龙数据模型
    static class Dinosaur {
        String name;
        double speed; // km/h
        String period;
        String diet;
        
        public Dinosaur(String name, double speed, String period, String diet) {
            this.name = name;
            this.speed = speed;
            this.period = period;
            this.diet = diet;
        }
        
        @Override
        public String toString() {
            return String.format("%s: %.1f km/h (%s, %s)", name, speed, period, diet);
        }
    }
    
    // 主方法:分析恐龙数据
    public void analyzeDinosaurData(String file1Path, String file2Path) {
        List<Dinosaur> dinosaurs = new ArrayList<>();
        
        try {
            // 读取第一个CSV文件
            readCSVFile(file1Path, dinosaurs);
            
            // 读取第二个CSV文件
            readCSVFile(file2Path, dinosaurs);
            
            // 按速度降序排序
            dinosaurs.sort((d1, d2) -> Double.compare(d2.speed, d1.speed));
            
            // 输出结果
            printResults(dinosaurs);
            
        } catch (IOException e) {
            System.err.println("Error reading files: " + e.getMessage());
        }
    }
    
    // 读取CSV文件
    private void readCSVFile(String filePath, List<Dinosaur> dinosaurs) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            boolean isFirstLine = true;
            
            while ((line = reader.readLine()) != null) {
                // 跳过标题行
                if (isFirstLine) {
                    isFirstLine = false;
                    continue;
                }
                
                // 解析CSV行
                Dinosaur dinosaur = parseCSVLine(line);
                if (dinosaur != null) {
                    dinosaurs.add(dinosaur);
                }
            }
        }
    }
    
    // 解析CSV行
    private Dinosaur parseCSVLine(String line) {
        try {
            // 简单的CSV解析(假设没有逗号在引号内的情况)
            String[] fields = line.split(",");
            
            if (fields.length >= 4) {
                String name = fields[0].trim();
                double speed = Double.parseDouble(fields[1].trim());
                String period = fields[2].trim();
                String diet = fields[3].trim();
                
                return new Dinosaur(name, speed, period, diet);
            }
        } catch (NumberFormatException e) {
            System.err.println("Error parsing line: " + line);
        }
        
        return null;
    }
    
    // 输出结果
    private void printResults(List<Dinosaur> dinosaurs) {
        System.out.println("恐龙速度排行榜(降序):");
        System.out.println("================================");
        
        for (int i = 0; i < dinosaurs.size(); i++) {
            System.out.printf("%d. %s%n", i + 1, dinosaurs.get(i));
        }
        
        System.out.println("================================");
        System.out.printf("总共分析了 %d 只恐龙%n", dinosaurs.size());
    }
    
    // 高级版本:支持更复杂的CSV解析
    public void analyzeDinosaurDataAdvanced(String file1Path, String file2Path) {
        List<Dinosaur> dinosaurs = new ArrayList<>();
        
        try {
            // 读取并合并数据
            readCSVFileAdvanced(file1Path, dinosaurs);
            readCSVFileAdvanced(file2Path, dinosaurs);
            
            // 去重(基于名称)
            removeDuplicates(dinosaurs);
            
            // 按速度降序排序
            dinosaurs.sort((d1, d2) -> Double.compare(d2.speed, d1.speed));
            
            // 输出详细统计
            printAdvancedResults(dinosaurs);
            
        } catch (IOException e) {
            System.err.println("Error reading files: " + e.getMessage());
        }
    }
    
    // 高级CSV解析(处理引号内的逗号)
    private void readCSVFileAdvanced(String filePath, List<Dinosaur> dinosaurs) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            boolean isFirstLine = true;
            
            while ((line = reader.readLine()) != null) {
                if (isFirstLine) {
                    isFirstLine = false;
                    continue;
                }
                
                Dinosaur dinosaur = parseCSVLineAdvanced(line);
                if (dinosaur != null) {
                    dinosaurs.add(dinosaur);
                }
            }
        }
    }
    
    // 高级CSV行解析
    private Dinosaur parseCSVLineAdvanced(String line) {
        try {
            List<String> fields = parseCSVFields(line);
            
            if (fields.size() >= 4) {
                String name = fields.get(0).trim();
                double speed = Double.parseDouble(fields.get(1).trim());
                String period = fields.get(2).trim();
                String diet = fields.get(3).trim();
                
                return new Dinosaur(name, speed, period, diet);
            }
        } catch (NumberFormatException e) {
            System.err.println("Error parsing line: " + line);
        }
        
        return null;
    }
    
    // 解析CSV字段(处理引号)
    private List<String> parseCSVFields(String line) {
        List<String> fields = new ArrayList<>();
        StringBuilder currentField = new StringBuilder();
        boolean inQuotes = false;
        
        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            
            if (c == '"') {
                inQuotes = !inQuotes;
            } else if (c == ',' && !inQuotes) {
                fields.add(currentField.toString());
                currentField = new StringBuilder();
            } else {
                currentField.append(c);
            }
        }
        
        fields.add(currentField.toString());
        return fields;
    }
    
    // 去重
    private void removeDuplicates(List<Dinosaur> dinosaurs) {
        Map<String, Dinosaur> uniqueDinosaurs = new LinkedHashMap<>();
        
        for (Dinosaur dinosaur : dinosaurs) {
            String key = dinosaur.name.toLowerCase();
            if (!uniqueDinosaurs.containsKey(key)) {
                uniqueDinosaurs.put(key, dinosaur);
            }
        }
        
        dinosaurs.clear();
        dinosaurs.addAll(uniqueDinosaurs.values());
    }
    
    // 输出高级结果
    private void printAdvancedResults(List<Dinosaur> dinosaurs) {
        System.out.println("恐龙速度详细分析报告");
        System.out.println("================================");
        
        // 速度统计
        double maxSpeed = dinosaurs.get(0).speed;
        double minSpeed = dinosaurs.get(dinosaurs.size() - 1).speed;
        double avgSpeed = dinosaurs.stream().mapToDouble(d -> d.speed).average().orElse(0);
        
        System.out.printf("最高速度: %.1f km/h%n", maxSpeed);
        System.out.printf("最低速度: %.1f km/h%n", minSpeed);
        System.out.printf("平均速度: %.1f km/h%n", avgSpeed);
        System.out.println();
        
        // 排行榜
        System.out.println("速度排行榜(前10名):");
        for (int i = 0; i < Math.min(10, dinosaurs.size()); i++) {
            System.out.printf("%d. %s%n", i + 1, dinosaurs.get(i));
        }
        
        System.out.println("================================");
    }
    
    // 测试方法
    public static void main(String[] args) {
        DinosaurDataAnalyzer analyzer = new DinosaurDataAnalyzer();
        
        // 创建测试CSV文件
        createTestCSVFiles();
        
        // 分析数据
        analyzer.analyzeDinosaurData("dinosaurs1.csv", "dinosaurs2.csv");
        
        System.out.println("\n" + "=".repeat(50) + "\n");
        
        // 高级分析
        analyzer.analyzeDinosaurDataAdvanced("dinosaurs1.csv", "dinosaurs2.csv");
    }
    
    // 创建测试CSV文件
    private static void createTestCSVFiles() {
        try {
            // 创建第一个CSV文件
            try (PrintWriter writer = new PrintWriter("dinosaurs1.csv")) {
                writer.println("Name,Speed,Period,Diet");
                writer.println("Tyrannosaurus Rex,40,Cretaceous,Carnivore");
                writer.println("Velociraptor,60,Cretaceous,Carnivore");
                writer.println("Triceratops,25,Cretaceous,Herbivore");
            }
            
            // 创建第二个CSV文件
            try (PrintWriter writer = new PrintWriter("dinosaurs2.csv")) {
                writer.println("Name,Speed,Period,Diet");
                writer.println("Allosaurus,35,Jurassic,Carnivore");
                writer.println("Stegosaurus,15,Jurassic,Herbivore");
                writer.println("Brachiosaurus,20,Jurassic,Herbivore");
            }
            
        } catch (IOException e) {
            System.err.println("Error creating test files: " + e.getMessage());
        }
    }
}

🔍 算法分析

  • 时间复杂度:O(n log n),其中n是恐龙数量(排序占主导)
  • 空间复杂度:O(n),存储恐龙数据
  • 核心思想:文件IO + 数据解析 + 排序算法

📊 测试用例

// 测试CSV文件1 (dinosaurs1.csv)
Name,Speed,Period,Diet
Tyrannosaurus Rex,40,Cretaceous,Carnivore
Velociraptor,60,Cretaceous,Carnivore
Triceratops,25,Cretaceous,Herbivore

// 测试CSV文件2 (dinosaurs2.csv)
Name,Speed,Period,Diet
Allosaurus,35,Jurassic,Carnivore
Stegosaurus,15,Jurassic,Herbivore
Brachiosaurus,20,Jurassic,Herbivore

// 期望输出(按速度降序):
1. Velociraptor: 60.0 km/h (Cretaceous, Carnivore)
2. Tyrannosaurus Rex: 40.0 km/h (Cretaceous, Carnivore)
3. Allosaurus: 35.0 km/h (Jurassic, Carnivore)
4. Triceratops: 25.0 km/h (Cretaceous, Herbivore)
5. Brachiosaurus: 20.0 km/h (Jurassic, Herbivore)
6. Stegosaurus: 15.0 km/h (Jurassic, Herbivore)

✨ 面试官反馈

面试官对文件IO处理很满意:"Good implementation! You've handled CSV parsing and sorting well. The code is clean and handles edge cases."

面试技巧分享

在 Meta PE SDE Coding Round 面试中,除了算法实现,面试官还会关注:

1. 代码质量

写出清晰、可读的代码,包含适当的注释和错误处理。

2. 边界条件处理

考虑空字符串、文件不存在、数据格式错误等特殊情况。

3. 算法选择

选择最优的算法,如双指针解决回文问题,排序算法处理数据分析。

4. 文件IO处理

正确处理文件读取、CSV解析、异常处理等实际工程问题。

Oavoservice 实时辅助服务

在这次 Meta PE SDE Coding Round 面试中,我们的实时辅助系统发挥了关键作用:

  • 算法指导:帮助候选人快速选择双指针算法解决回文问题
  • 代码实现:提供完整的Java代码实现
  • 文件IO:协助处理CSV文件读取和解析
  • 错误处理:确保代码包含适当的异常处理

结语

Meta PE SDE Coding Round 的两道题目虽然看似简单,但考察了候选人的基础算法能力和实际工程经验。回文判断考察双指针算法,数据分析考察文件IO和数据处理能力。在面试中,能够快速实现并处理边界条件,展现了候选人的编程功底。

如果你正在准备 Meta 或其他公司的技术面试,欢迎联系 Oavoservice。我们拥有丰富的面试经验和专业的辅助团队,能够在你最需要的时候提供及时有效的帮助。

Oavoservice - 让每一次面试都成为成功的机会!

需要辅助的同学随时dd哦,vo开始前支持mock!