专栏名称: 吴师兄学算法
和程序员小吴一起从初学者的角度学习算法,以动画的形式呈现解题的思路。每周四篇原创文章,期待你的鉴赏!
目录
相关文章推荐
半月谈  ·  基层,不妨大胆拥抱AI ·  9 小时前  
长安街知事  ·  杨靖宇信件公布! ·  昨天  
微观三农  ·  预告 | ... ·  昨天  
四川新闻广播  ·  蜀乡各地春耕备耕忙 ·  2 天前  
中国水利  ·  水利部全面部署各地春灌保障工作 ·  2 天前  
51好读  ›  专栏  ›  吴师兄学算法

今年校招算法最容易上岸的是什么?

吴师兄学算法  · 公众号  ·  · 2024-12-08 10:40

正文

大家好,我是吴师兄。

每年校招季,最扎心的不是「投了简历没人看」,也不是「面试被问哭」,而是 “这年头算法岗到底怎么卷?”

今年依然有不少同学问我:「师兄,哪个方向最好上岸?」「后端岗会认可客户端的实习吗?」。

今天咱们就围绕这些问题,聊聊算法校招的那些事儿。

客户端实习,后端岗也认?

这个问题,答案是肯定的。

客户端和后端在校招层面没有你想象的壁垒那么高。

特别是在大厂,校招的重点是看你的潜力,而不是死磕方向。很多后端团队反而欣赏有客户端经验的候选人:你会从更贴近用户的角度考虑问题,对业务的理解可能更深。至于实习经历,那就像张小龙说的——“用得上的能力,才是值得学的。”所以有机会去做客户端实习,不要犹豫,后端岗还是有戏的。

最容易上岸的算法方向?

还是大模型和搜索推广。

老实说,算法岗「两极分化」很严重。业务导向的岗位需求量大,竞争相对小,比如搜索推广。相比之下,偏学术的方向(比如CV/NLP的纯研究岗)竞争异常激烈。这背后有现实原因:公司烧钱的时代过去了,现在大家更关心 “能不能直接赚钱?”

搜索推广就是那个 「永远不会过时的刚需」 。更神奇的是,不少人即使一开始没做这个方向,最终也会慢慢靠近。要么是主动转岗,要么是被业务需求推动。 市场需要,才是最终决定你去哪里的主旋律。

校招选公司,氛围真的很重要

除了方向,更让人头疼的是选公司。

很多人都问:「选大公司还是选小公司?」「重技术还是重业务?」其实规模和技术方向相差不大的情况下, 团队氛围才是决定你能不能开心工作的关键。

一个良好的团队氛围,不仅能让你干活不累,还能让你在行业里建立靠谱的人脉。反之,进了个气氛组,每天办公室弥漫着压抑的「职场丛林法则」,你可能干着干着就怀疑人生了。

从内容理解到搜索推广:转岗真的难吗?

如果你是做内容理解的,不必担心方向的局限性。

很多搜索推广团队都有「内容理解」相关的项目组,转岗平滑得像地铁换乘。甚至有些大厂专门会留这种内部流动的通道,帮助你实现职业方向的「升级打怪」。

算法岗的竞争从来不是绝路,关键是怎么找自己的优势和市场需求的结合点。

结尾的那点人生感悟

校招这件事,其实是一场「理性和感性的对话」。

理性上,选方向要看清市场的需求,别和现实较劲。

感性上,选团队一定要选让自己舒服的环境,长远来看,这才是稳定输出的关键。

最后借用一个朋友的话送给正在迷茫的校招生:
“人生没有白走的路,每一步都算数。”

也许今天的选择会影响几年后的轨迹,但不管方向如何,只要你认真走下去,都能找到属于自己的光。


回归我们公众号的主题。

继续来一道和「算法岗校招」相关的算法原题。

题目描述

公司老板做了一笔大生意,想要给每位员工分配一些奖金,想通过游戏的方式来决定每个人分多少钱。

按照员工的工号顺序,每个人随机抽取一个数字。按照工号的顺序往后排列,遇到 第一个数字比自己数字大的 ,那么,前面的员工就可以获得 “距离*数字差值” 的奖金。如果遇不到比自己数字大的,就给自己分配随机数数量的奖金。

例如,按照工号顺序的随机数字是: 2,10,3 。那么第 2 个员工的数字 10 比第1个员工的数字 2 大,所以,第 1 个员工可以获得 1*(10-2)=8 。第 2 个员工后面没有比他数字更大的员工,所以,他获得他分配的随机数数量的奖金,就是 10 。第 3 个员工是最后一个员工,后面也没有比他更大数字的员工,所以他得到的奖金是 3

请帮老板计算一下每位员工最终分到的奖金都是多少钱。

输入描述

第一行 n 表示员工数量

第二是每位员工分配的随机数字

输出描述

最终每位员工分到的奖金数量

注:随机数字不重复,员工数量范围 1~10000 ,随机数范围 1~100000

示例

输入

3
2 10 3

输出

8 10 3

解题思路

每一个员工本质上是要找到 位于其右边第一个大于它的元素 ,并且求出它们之间的大小差值和距离差值。显然应该立刻想到使用单调栈来完成。

无论是正序还是逆序,都可以完成这个题目。

代码

解法一:逆序遍历

Python



# 输入员工个数n
n = int(input())
# 输入N个员工的随机数数组
nums = list(map(int, input().split()))

# 构建一个单调栈,用来存放不同随机数的索引
# 栈中储存的索引所对应在height中的元素大小,从栈底至栈顶单调递减
# 即更大的数(的下标)位于栈底
stack = list()

# 构建列表ans,用来保存输出结果
# 初始化其中所有的元素和原数组一样,注意此处要使用拷贝
ans = nums.copy()

# 逆序遍历nums中的每一个随机数
for i in range(n-1-1-1):
    num = nums[i]
    # 第i个员工拿到的的随机数num,需要不断地与栈顶元素比较
    # 如果栈顶元素存在并且num【大于等于】栈顶元素stack[-1]这个下标对应的数字
    # 说明栈顶元素stack[-1]这个下标对的数字,并不是随机数num右边最近的比num更大的元素
    # 需要将栈顶元素弹出,继续寻找比num大的栈顶元素
    while len(stack) > 0 and num >= nums[stack[-1]]:
        # 栈顶元素下标对应的数字不大于当前数字num,不是符合要求的更大数字,弹出
        stack.pop()
    # 完成弹出后,如果栈顶仍存在元素,说明stack[-1]所对应的数字,是严格比当前数字num大的下一个数字
    if len(stack) > 0:
        # stack[-1]即为i这个索引所对应的,下一个最近的更大的随机数的下标
        # 它们之间的下标距离为stack[-1]-i,数字大小差值为nums[stack[-1]]-num
        # 相乘后在ans中修改,作为答案
        ans[i] = (stack[-1]-i)*(nums[stack[-1]]-num)

    # 再把当前下标i储存入栈中
    # 注意:所储存的是下标i,而不是随机数num
    stack.append(i)


# ans中的int元素转成str后才能合并成字符串
print(" ".join(map(str, ans)))

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        // 创建Scanner对象用于输入
        Scanner sc = new Scanner(System.in);

        // 输入员工个数n
        int n = sc.nextInt();

        // 输入N个员工的随机数数组
        int[] nums = new int[n];
        for (int i = 0; i             nums[i] = sc.nextInt();
        }

        // 构建一个单调栈,用来存放不同随机数的索引
        // 栈中储存的索引所对应在nums中的元素大小,从栈底至栈顶单调递减
        // 即更大的数(的下标)位于栈底
        Stack stack = new Stack<>();

        // 构建列表ans,用来保存输出结果
        // 初始化其中所有的元素和原数组一样,注意此处要使用拷贝
        int[] ans = Arrays.copyOf(nums, n);

        // 逆序遍历nums中的每一个随机数
        for (int i = n - 1; i >= 0; i--) {
            int num = nums[i];

            // 第i个员工拿到的的随机数num,需要不断地与栈顶元素比较
            // 如果栈顶元素存在并且num【大于等于】栈顶元素stack[-1]这个下标对应的数字
            // 说明栈顶元素stack[-1]这个下标对的数字,并不是随机数num右边最近的比num更大的元素
            // 需要将栈顶元素弹出,继续寻找比num大的栈顶元素
            while (!stack.isEmpty() && num >= nums[stack.peek()]) {
                // 栈顶元素下标对应的数字不大于当前数字num,不是符合要求的更大数字,弹出
                stack.pop();
            }

            // 完成弹出后,如果栈顶仍存在元素,说明stack[-1]所对应的数字,是严格比当前数字num大的下一个数字
            if (!stack.isEmpty()) {
                // stack.peek()即为i这个索引所对应的,下一个最近的更大的随机数的下标
                // 它们之间的下标距离为stack.peek()-i,数字大小差值为nums[stack.peek()]-num
                // 相乘后在ans中修改,作为答案
                ans[i] = (stack.peek() - i) * (nums[stack.peek()] - num);
            }

            // 再把当前下标i储存入栈中
            // 注意:所储存的是下标i,而不是随机数num
            stack.push(i);
        }

        // 输出结果,确保数字之间用空格分隔,没有尾部空格
        for (int i = 0; i             if (i > 0) System.out.print(" ");
            System.out.print(ans[i]);
        }
        System.out.println();
    }
}

C++

#include 
#include 
#include 
using namespace std;

int main() {
    // 输入员工个数n
    int n;
    cin >> n;

    // 输入N个员工的随机数数组
    vector<intnums(n);
    for (int i = 0; i         cin >> nums[i];
    }

    // 构建一个单调栈,用来存放不同随机数的索引
    // 栈中储存的索引所对应在nums中的元素大小,从栈底至栈顶单调递减
    // 即更大的数(的下标)位于栈底
    stack<int> s;

    // 构建列表ans,用来保存输出结果
    // 初始化其中所有的元素和原数组一样,注意此处要使用拷贝
    vector<int> ans = nums;

    // 逆序遍历nums中的每一个随机数
    for (int i = n - 1; i >= 0; i--) {
        int num = nums[i];

        // 第i个员工拿到的的随机数num,需要不断地与栈顶元素比较
        // 如果栈顶元素存在并且num【大于等于】栈顶元素stack[-1]这个下标对应的数字
        // 说明栈顶元素stack[-1]这个下标对的数字,并不是随机数num右边最近的比num更大的元素
        // 需要将栈顶元素弹出,继续寻找比num大的栈顶元素
        while (!s.empty() && num >= nums[s.top()]) {
            // 栈顶元素下标对应的数字不大于当前数字num,不是符合要求的更大数字,弹出
            s.pop();
        }

        // 完成弹出后,如果栈顶仍存在元素,说明stack[-1]所对应的数字,是严格比当前数字num大的下一个数字
        if (!s.empty()) {
            // s.top()即为i这个索引所对应的,下一个最近的更大的随机数的下标
            // 它们之间的下标距离为s.top()-i,数字大小差值为nums[s.top()]-num
            // 相乘后在ans中修改,作为答案
            ans[i] = (s.top() - i) * (nums[s.top()] - num);
        }

        // 再把当前下标i储存入栈中
        // 注意:所储存的是下标i,而不是随机数num
        s.push(i);
    }

    // 输出结果,确保数字之间用空格分隔,没有尾部空格
    for (int i = 0; i         if (i > 0cout <" ";
        cout <    }
    cout 
    return 0;
}

C

#include 
#include 

// 定义一个栈的数据结构
#define MAX_SIZE 100000
int stack[MAX_SIZE];
int top = -1;

// 向栈中压入元素
void push(int index) {
    stack[++top] = index;
}

// 从栈中弹出元素
int pop() {
    return stack[top--];
}

// 查看栈顶元素
int peek() {
    return stack[top];
}

int main() {
    int n;
    
    // 输入员工个数n
    scanf("%d", &n);

    // 输入N个员工的随机数数组
    int nums[n];
    for (int i = 0; i         scanf("%d", &nums[i]);
    }

    // 构建数组ans,用来保存输出结果
    int ans[n];
    for (int i = 0; i         ans[i] = nums[i];
    }

    // 逆序遍历nums中的每一个随机数
    for (int i = n - 1; i >= 0; i--) {
        int num = nums[i];

        // 第i个员工拿到的的随机数num,需要不断地与栈顶元素比较
        while (top >= 0 && num >= nums[stack[top]]) {
            // 弹出栈顶元素
            pop();
        }

        // 如果栈顶仍存在元素,说明stack[top]所对应的数字,是严格比当前数字num大的下一个数字
        if (top >= 0) {
            ans[i] = (stack[top] - i) * (nums[stack[top]] - num);
        }

        // 把当前下标i储存入栈中
        push(i);
    }

    // 输出结果,确保数字之间用空格分隔,没有尾部空格
    for (int i = 0; i         if (i > 0) {
            printf(" ");
        }
        printf("%d", ans[i]);
    }
    printf("\n");

    return 0;
}

Node JavaScript

const readline = require('readline');

// 创建接口读取输入
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// 读取输入
rl.question('', (n) => {
    rl.question('', (line) => {
        const nums = line.split(' ').map(Number);
        const ans = [...nums];  // 拷贝一份nums数组

        // 单调栈
        const stack = [];

        // 逆序遍历
        for (let i = n - 1; i >= 0; i--) {
            const num = nums[i];

            // 比较栈顶元素与当前num的大小
            while (stack.length > 0 && num >= nums[stack[stack.length - 1]]) {
                stack.pop();
            }

            // 如果栈顶仍然有元素,说明栈顶对应的数字是比当前num大的下一个数字
            if (stack.length > 0) {
                ans[i] = (stack[stack.length - 1] - i) * (nums[stack[stack.length - 1]] - num);
            }

            // 将当前下标i压入栈中
            stack.push(i);
        }

        // 输出结果,确保数字之间用空格分隔,且没有尾部空格
        console.log(ans.join(' '));

        rl.close();  // 关闭接口
    });
});

Go

package main

import (
    "fmt"
)

func main() {
    var n int
    // 输入员工个数n
    fmt.Scan(&n)

    // 输入N个员工的随机数数组
    nums := make([]int, n)
    for i := 0; i         fmt.Scan(&nums[i])
    }

    // 构建一个单调栈
    stack := []int{}
    ans := make([]int, n)
    copy(ans, nums)  // 初始化结果数组

    // 逆序遍历nums中的每一个随机数
    for i := n - 1; i >= 0; i-- {
        num := nums[i]

        // 比较栈顶元素与当前num的大小
        for len(stack) > 0 && num >= nums[stack[len(stack)-1]] {
            // 弹出栈顶元素
            stack = stack[:len(stack)-1]
        }

        // 如果栈顶仍然有元素,说明栈顶对应的数字是比当前num大的下一个数字
        if len(stack) > 0 {
            ans[i] = (stack[len(stack)-1] - i) * (nums[stack[len(stack)-1]] - num)
        }

        // 将当前下标i压入栈中
        stack = append(stack, i)
    }

    // 输出结果,确保数字之间用空格分隔,且没有尾部空格
    for i := 0; i         if i > 0 {
            fmt.Print(" ")
        }
        fmt.Print(ans[i])
    }
    fmt.Println()
}

解法二:正序遍历

Python



# 输入员工个数n
n = int(input())
# 输入N个员工的随机数数组
nums = list(map(int, input().split()))

# 构建一个单调栈,用来存放不同随机数的索引
# 栈中储存的索引所对应在height中的元素大小,从栈底至栈顶单调递减
# 即更大的数(的下标)位于栈底
stack = list()

# 构建列表ans,用来保存输出结果
# 初始化其中所有的元素和原数组一样,注意此处要使用拷贝
ans = nums.copy()

# 从头开始正序遍历nums中的每一个随机数
for i, num in enumerate(nums):
    # 第i个员工拿到的的随机数num,需要不断地与栈顶元素比较
    # 如果栈顶元素存在并且num【大于】栈顶元素stack[-1]这个下标对应的数字
    # 意味着nums中下标为stack[-1]的数字,找到了右边最近的比它更大的数字num
    while len(stack) > 0 and num > nums[stack[-1]]:
        # 首先获取栈顶元素的值,也就是要修改的位置
        preIndex = stack.pop()

        # i即为preIndex这个索引所对应的,下一个最近的更大的随机数的下标
        # 它们之间的下标距离为i-preIndex,数字大小差值为num-nums[preIndex]
        # 相乘后在ans中修改,作为答案
        ans[preIndex] = (i-preIndex)*(num-nums[preIndex])

    # 再把当前下标i储存入栈中
    # 注意:所储存的是下标i,而不是随机数num
    stack.append(i)

# ans中的int元素转成str后才能合并成字符串
print(" ".join(map(str, ans)))

Java

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 输入员工个数n
        int n = sc.nextInt();

        // 输入N个员工的随机数数组
        int[] nums = new int[n];
        for (int i = 0; i             nums[i] = sc.nextInt();
        }

        // 构建一个单调栈,用来存放不同随机数的索引
        // 栈中储存的索引所对应的元素大小,从栈底至栈顶单调递减
        // 即更大的数(的下标)位于栈底
        Stack stack = new Stack<>();

        // 构建列表ans,用来保存输出结果
        // 初始化其中所有的元素和原数组一样,注意此处要使用拷贝
        int[] ans = Arrays.copyOf(nums, n);

        // 从头开始正序遍历nums中的每一个随机数
        for (int i = 0; i             int num = nums[i];

            // 第i个员工拿到的的随机数num,需要不断地与栈顶元素比较
            // 如果栈顶元素存在并且num【大于】栈顶元素stack[-1]这个下标对应的数字
            // 意味着nums中下标为stack[-1]的数字,找到了右边最近的比它更大的数字num
            while (!stack.isEmpty() && num > nums[stack.peek()]) {
                // 首先获取栈顶元素的值,也就是要修改的位置
                int preIndex = stack.pop();

                // i即为preIndex这个索引所对应的,下一个最近的更大的随机数的下标
                // 它们之间的下标距离为i-preIndex,数字大小差值为num-nums[preIndex]
                // 相乘后在ans中修改,作为答案
                ans[preIndex] = (i - preIndex) * (num - nums[preIndex]);
            }

            // 再把当前下标i储存入栈中
            // 注意:所储存的是下标i,而不是随机数num
            stack.push(i);
        }

        // 输出结果,避免最后的空格
        for (int i = 0; i             if (i > 0) {
                System.out.print(" ");  // 在输出之前先输出一个空格
            }
            System.out.print(ans[i]);
        }
        System.out.println();

        sc.close();
    }
}

C++

#include 
#include 
#include 
using namespace std;

int main() {
    int n;
    // 输入员工个数n
    cin >> n;

    // 输入N个员工的随机数数组
    vector<intnums(n);
    for






请到「今天看啥」查看全文