蓝领招聘网

携程2025校招考什么?

第一部分:数据结构与算法

这是面试的重中之重,通常会通过1-2道算法题来考察。 1:二叉树的最近公共祖先 描述:** 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

携程2025校招考什么?-图1
(图片来源网络,侵删)

给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

        _______3______
       /              \
    ___5__          ___1__
   /      \        /      \
   6      _2_      0      8
         /   \
         7   4

节点5节点1的最近公共祖先是节点3节点5节点4的最近公共祖先是节点5

考察点:

  • 树的遍历(前序、中序、后序)
  • 递归思想
  • 对树结构的理解

解题思路: 这是一个非常经典的树问题,最优雅的解法是使用递归,核心思想是:从根节点开始,递归地在左右子树中寻找p和q节点。

携程2025校招考什么?-图2
(图片来源网络,侵删)
  1. 基本情况:

    • 如果当前节点为空,说明没找到,返回 null
    • 如果当前节点等于p或q,说明找到了其中一个节点,那么这个节点本身就可能是祖先(如果另一个节点在它的子树里),所以返回当前节点。
  2. 递归情况:

    • 分别递归左子树和右子树,得到 leftright
    • 判断 leftright 的结果:
      • leftright 都不为 null,说明p和q分别位于当前节点的左右两侧,那么当前节点就是它们的最近公共祖先,返回当前节点。
      • leftnull,说明p和q都在右子树,那么右子树递归的结果就是最近公共祖先,返回 right
      • rightnull,说明p和q都在左子树,那么左子树递归的结果就是最近公共祖先,返回 left

代码示例 (Java):

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 基本情况1: 如果树为空,或者当前节点是p或q之一
        if (root == null || root == p || root == q) {
            return root;
        }
        // 递归地在左右子树中查找
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        // 判断结果
        if (left != null && right != null) {
            // p和q分别在左右子树,当前节点就是LCA
            return root;
        }
        // 如果left为空,说明p和q都在右子树
        // 如果right为空,说明p和q都在左子树
        // 如果left和right都为空,说明p和q都不在当前子树
        return left != null ? left : right;
    }
}

2:反转链表 描述:** 反转一个单链表。

携程2025校招考什么?-图3
(图片来源网络,侵删)

示例: 输入: 1 -> 2 -> 3 -> 4 -> 5 -> NULL 输出: 5 -> 4 -> 3 -> 2 -> 1 -> NULL

考察点:

  • 链表的基本操作
  • 指针的运用
  • 迭代与递归的实现

解题思路: 这是链表操作的入门题,主要考察对指针的掌控能力,最常用的是迭代法

迭代法思路:

  1. 定义三个指针:prev (前驱节点,初始化为 null),curr (当前节点,初始化为 head),next (后继节点)。
  2. 遍历链表,在每一步:
    • 先用 next 暂存 curr 的下一个节点,因为马上要断开这个链接。
    • curr.next 指向 prev,实现反转。
    • prevcurr 指针向后移动,准备处理下一个节点。
  3. curr 变为 null 时,prev 就指向了新的头节点。

代码示例 (Java):

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next; // 暂存下一个节点
            curr.next = prev;              // 反转指针
            prev = curr;                   // prev后移
            curr = nextTemp;               // curr后移
        }
        return prev; // 当curr为null时,prev是新的头节点
    }
}

递归法思路(拓展): 递归的思路稍微抽象一些,假设 reverseList(head) 函数已经能反转以 head 为头节点的链表并返回新的头节点。

  1. 递归终止条件:head 为空或 head.next 为空(即只有一个节点)。
  2. 递归调用:newHead = reverseList(head.next),这会反转从 head.next 开始的链表,并返回新的头节点(即原链表的尾节点)。
  3. 操作:head.next.next = head; 将当前节点 head 添加到反转后链表的尾部。
  4. head.next = null; 断开原来的链接,防止形成环。
  5. 返回 newHead

第二部分:数据库

3:SQL查询 描述:** 假设有一个用户表 users 和一个订单表 orders

users 表结构: | user_id (主键) | username | | :--- | :--- | | 1 | 'Alice' | | 2 | 'Bob' | | 3 | 'Charlie' |

orders 表结构: | order_id (主键) | user_id (外键) | amount | | :--- | :--- | :--- | | 101 | 1 | 100 | | 102 | 2 | 150 | | 103 | 1 | 200 | | 104 | 4 | 300 |

问题: 请编写一条SQL语句,查询所有下过订单的用户信息(包括没有下过订单的用户,用户名显示为 "Inactive"),并按照 user_id 升序排列。

考察点:

  • LEFT JOIN (或 LEFT OUTER JOIN) 的使用
  • CASE WHEN 条件表达式
  • COALESCEIFNULL 函数(作为备选方案)
  • ORDER BY 排序

解题思路: 这个问题需要将 users 表作为主表,即使它们在 orders 表中没有匹配项也要显示出来,这正是 LEFT JOIN 的用途,对于 orders 表中可能为 NULLusername,使用 CASE WHEN 语句进行条件替换。

SQL 解答:

SELECT
    u.user_id,
    CASE
        WHEN o.username IS NOT NULL THEN o.username
        ELSE 'Inactive'
    END AS username
FROM
    users u
LEFT JOIN
    orders o ON u.user_id = o.user_id
ORDER BY
    u.user_id ASC;

解释:

  1. FROM users u LEFT JOIN orders o ON u.user_id = o.user_id:将 users 表左连接到 orders 表,这样,users 表中的所有记录都会被保留,orders 表中如果没有匹配项,则对应字段为 NULL
  2. CASE WHEN o.username IS NOT NULL THEN o.username ELSE 'Inactive' END AS username:检查连接后的 username 是否为 NULL,如果不是,说明用户下过单,使用其真实用户名;如果是 NULL,说明用户没有下过单,显示为 "Inactive"。
  3. ORDER BY u.user_id ASC:按照 user_id 升序排列结果。

第三部分:操作系统与网络

4:Linux命令 描述:** 如何查看一个进程占用的CPU和内存情况?如何实时查看一个名为 nginx 的进程的CPU使用率变化?

考察点:

  • Linux常用系统监控命令
  • 对进程状态的理解

解答:

  1. 查看一个进程占用的CPU和内存情况: 最常用的命令是 pstop

    • ps 命令: 如果你想查看当前所有进程的CPU和内存占用,可以使用:

      ps aux
      • a: 显示所有进程
      • u: 以用户为中心显示
      • x: 显示没有控制终端的进程 输出结果中的 %CPU%MEM 列分别表示进程的CPU和内存占用百分比。

      如果你想查看特定PID(例如PID为1234)的进程:

      ps -p 1234 -o pid,ppid,cmd,%cpu,%mem
    • top 命令: top 是一个动态的、交互式的进程查看器,它默认会按照CPU使用率降序排列进程,并实时刷新,你可以在 top 界面中直接看到 %CPU%MEM 列。

  2. 实时查看名为 nginx 的进程的CPU使用率变化: 可以结合 pswatch 命令,或者直接使用 top

    • 使用 top (推荐):

      top -p $(pgrep nginx)
      • pgrep nginx 会查找所有名为 nginx 的进程的PID,并返回这些PID组成的列表。
      • top -p ... 会只显示这些指定PID的进程信息,并实时刷新。
    • 使用 pswatch:

      watch -n 1 "ps -C nginx -o pid,user,%cpu,%mem --sort=-%cpu"
      • watch -n 1:每秒执行一次后面的命令。
      • ps -C nginx:只显示名为 nginx 的进程。
      • --sort=-%cpu:按照CPU使用率降序排列。

第四部分:系统设计

5:设计一个短链接服务 描述:** 请设计一个类似 t.cn 这样的短链接服务,当用户访问一个短链接时,服务需要将其重定向到原始的长链接。

考察点:

  • 系统设计能力
  • 架构思维
  • 数据库设计
  • 性能和扩展性考虑

解答思路: 这是一个典型的系统设计题,可以从以下几个方面展开:

功能需求分析

  • 核心功能:
    • 创建短链接: 用户输入一个长URL,服务生成一个唯一的短URL。
    • 重定向: 当用户访问短URL时,服务返回一个HTTP 301(永久重定向)或302(临时重定向)响应,将浏览器引导至原始长URL。
  • 附加功能(可选):
    • 自定义短链接后缀。
    • 查看短链接的访问统计信息(点击次数、访问时间、IP地址等)。
    • 短链接的有效期设置。

API设计

  • POST /shorten
    • Request Body: { "long_url": "https://www.a-very-long-url.com/path/to/resource" }
    • Response Body: { "short_url": "http://short.ly/Ab3cd" }
  • GET /{short_key}
    • 访问 http://short.ly/Ab3cd
    • Response: HTTP 301 Redirect to https://www.a-very-long-url.com/path/to/resource

数据库设计 这是设计的核心,我们需要存储长短URL的映射关系。

  • 直接使用数据库自增ID

    • 表结构:
      CREATE TABLE `urls` (
        `id` INT AUTO_INCREMENT PRIMARY KEY,
        `long_url` VARCHAR(2048) NOT NULL,
        `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX `idx_long_url` (`long_url`)
      );
    • 生成短链接:
      1. 将长URL存入数据库,获得自增的 id
      2. 将这个 id 转换成62进制(包含0-9, a-z, A-Z)的字符串,作为 short_key12345 -> bZ
      3. 组合成完整的短URL,如 http://short.ly/bZ
    • 重定向:
      1. 从请求中解析出 short_key (如 bZ)。
      2. short_key 转换回10进制的 id (如 bZ -> 12345)。
      3. 用这个 id 去数据库中查询 long_url
      4. 执行重定向。
    • 优点: id 是唯一的,且自增,保证了 short_key 的唯一性,存储 short_key 的计算是O(1)的。
    • 缺点: 如果想自定义短链接,此方案不适用。
  • 直接生成随机字符串

    • 表结构:
      CREATE TABLE `short_urls` (
        `short_key` VARCHAR(10) PRIMARY KEY,
        `long_url` VARCHAR(2048) NOT NULL,
        `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      );
    • 生成短链接:
      1. 生成一个足够长的随机字符串(如6-8位)作为 short_key
      2. 检查 short_key 是否已存在于数据库中(使用唯一索引防止冲突)。
      3. 如果冲突,重新生成;如果未冲突,将 (short_key, long_url) 存入数据库。
    • 优点: 支持自定义短链接。
    • 缺点: 存在哈希冲突的可能,需要处理重试逻辑,随着数据量增大,冲突概率增加,性能可能下降。

性能与扩展性考虑

  • 缓存: 短链接的“读”操作(重定向)远多于“写”操作(创建),必须使用缓存(如 Redis)来加速。
    • 缓存策略: 当创建短链接时,除了写入数据库,也将 (short_key -> long_url) 的映射关系存入Redis,当用户访问短链接时,先查询Redis,如果命中则直接返回,未命中再查询数据库,并将结果回填到缓存中。
  • 高可用:

    数据库和缓存都需要做主从复制或集群部署,避免单点故障。

  • 负载均衡:

    在短链接服务前部署负载均衡器(如Nginx, SLB),将流量分发到多个后端服务实例。

  • 分布式ID生成(如果采用方案一):

    如果系统是分布式的,数据库的自增ID可能成为瓶颈,可以考虑使用雪花算法(Snowflake)等分布式ID生成方案来代替数据库自增ID。

  • 安全性:
    • 需要对用户输入的长URL进行合法性校验,防止恶意链接。
    • 可以对URL进行签名,防止用户篡改。

一个完整的短链接服务 = API接口 + 长短URL映射存储(DB) + 高速缓存 + 负载均衡 + 监控告警,面试时,清晰地画出架构图并阐述各个组件的作用是加分项。

分享:
扫描分享到社交APP
上一篇
下一篇