专栏名称: 吴师兄学算法
和程序员小吴一起从初学者的角度学习算法,以动画的形式呈现解题的思路。每周四篇原创文章,期待你的鉴赏!
目录
相关文章推荐
北美留学生观察  ·  日本县城高中,挤满中国中产娃... ·  5 小时前  
北美留学生观察  ·  藤校进入9万美元时代!扩招、涨价、割中产娃韭菜… ·  2 天前  
BioArt  ·  Cell | ... ·  3 天前  
51好读  ›  专栏  ›  吴师兄学算法

华为不同职级 薪资待遇一览表。。。

吴师兄学算法  · 公众号  ·  · 2024-12-17 11:30

正文

大家好,我是吴师兄。

最近看到一张华为不同职级的薪资待遇表,不得不说,真的是让人看了心动又焦虑。

对于我们这些职场小白来说,华为的职级体系和薪资待遇就像是职业生涯的一个“终极目标”,但这也意味着:有多少人能从13级做到20级?

华为的职级从 13 级到 22 级,23 级及以上是高层管理人员,普通员工最多也就能升到 20 级。

每一级的薪资差距非常明显,而且更高等级的薪资数据几乎是“神秘”状态。最有意思的是, 华为的每个职级分为A/B/C三个小级别,但技术岗位并不细分等级

这就意味着,不同岗位的晋升速度和难度差距也挺大。

我从朋友那里得知,华为的16级已经是技术骨干的级别,18级基本上就是“技术专家”的身份了。

到了19、20级,年薪直接突破百万!这是常规岗位无法想象的数字,但这背后是否隐藏着更高的压力和挑战呢?

来看看华为 13 级到 20 级的薪资待遇表——

  • 13级 基本工资20.8万,年终奖3.4万,股权激励0,年薪大概24.2万。
  • 14级 基本工资28.8万,年终奖5.8万,股权激励0.3万,年薪大概34.9万。
  • 15级 基本工资31.5万,年终奖10万,股权激励3万,年薪大概44.5万。
  • 16级 基本工资42万,年终奖13.5万,股权激励8.5万,年薪大概64万。
  • 17级 基本工资50万,年终奖19.5万,股权激励18.6万,年薪大概88.1万。
  • 18级 基本工资72万,年终奖40.7万,股权激励27.2万,年薪大概139.9万。
  • 19级 基本工资75.1万,年终奖63.5万,股权激励22万,年薪大概160.6万。
  • 20级 基本工资60.5万,年终奖75万,股权激励125.8万,年薪大概261.3万。

可以看到, 薪资差距堪称“天壤之别”

13级的年薪才24.2万,而20级则已经突破了260万!这差距不仅仅是数字上的变化,更是在于职场地位、影响力、责任的巨大差异。

但你会发现,华为的职级晋升不光看能力,还要有一定的运气成分。

16级到18级是一个关键节点,18级更是技术专家的标志,意味着你已经在行业内有了话语权。

但问题是,能做到这一点的人究竟有多少?有些人可能刚进华为就被卡在13级,做了一辈子基础工作;有些人则通过自己的努力,爬到了16级以上,年薪翻了几倍,简直“逆袭人生”。

很多人都说, “在华为,选择和努力同样重要” 。确实,很多人从13级走到16级、18级,不仅仅是因为他们有技术,更是因为他们抓住了机会,迎合了市场需求,甚至有运气成分在内。

可问题是,能抓住机会的那些人, 大多都是那些站在行业风口浪尖的人 。而我们这些普通员工,或许就会因为一个偶然的失误,被卡在低职级,难以晋升。



回归我们公众号的主题, 每天掌握一道算法题

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

本原题练习网址:https://www.algomooc.com/problem/P3004

题目描述

跳房子,也叫跳飞机,是一种世界性的儿童游戏。

游戏参与者需要分多个回合按顺序跳到第 1 格直到房子的最后一格,然后获得一次选房子的机会,直到所有房子都被选完,房子最多的人获胜。

跳房子的过程中,如果有踩线等违规行为会结束当前回合,甚至可能倒退几步。

假设房子的总格数是 count ,小红每回合可能连续跳的步数都放在数据 steps 中,请问数组中是否有一种步数的组合,可以让小红三个回合跳到最后一格?如果有,请输出索引和最小的步数组合,数据保证索引和最小的步数组合是唯一的。

注意:数组中的步数可以重复,但数组中的元素不能重复使用。

输入描述

第一行输入为房子总格数 count ,它是整数类型 int

第二行输入为每回合可能连续跳过的步数,它是整数数组类型。

输出描述

返回索引和最小满足要求的步数组合。

注意:顺序保持 steps 中的原有顺序。

备注

  • count <= 10000;
  • 3 <= steps.length <= 10000;
  • -100000 <= steps[i] <= 100000;

示例一

输入

1,4,5,2,0,2
9

输出

4,5,0

说明

示例二

输入

1,5,2,0,2,4
9

输出

5,2,2

示例三

输入

-1,2,4,9
12

输出

-1,4,9

解题思路

注意,本题和LeetCode15、三数之和基本完全一致。区别仅仅在于本题需要找到下标和最小的索引。

首先要理解题意。

输出描述的断句是这样的: 返回索引和最小(且)满足要求的步数组合

索引和 一词是一个整体,表示需要找到三个数的索引的总和。

数组排序

在LeetCode15、三数之和 中我们知道,要使用双指针算法,我们必须对原数组进行从小到大的排序。

但是在本题中,一旦对原数组进行排序,那么其原来的索引就丢失了。

因此,我们在排序的过程中,除了对已知数据 num 进行排序,还必须同时储存每一个元素在原输入数组 lst 中的索引 idx 。故排序过程如下

# 对原nums数组进行排序,同时需要记录在原数组中的下标idx,作为排序的第二个依据
nums = sorted((num, idx) for idx, num in enumerate(lst))

注意此处我们新建的 nums 是一个二维数组,其中的内层元素 (num, idx) 是一个二元组,其中 num 是元素值, idx num 在原输入数组 lst 中的索引。

nums 的中元素的排序,会先按照 num 的大小进行从小到大排序,当 num 相等时,则按照其出现在 lst 中的索引 idx 进行从小到大排序。

譬如对于例子

lst = [252514]

我们会得到

nums = [(14), (20), (22), (45), (51), (53)]

双指针过程

接下来是非常经典的双指针过程。

对于 nums 中的元素,我们遍历里面里面的每一组 (num, idx) (其在 nums 中的索引值为 i ),将其固定为选择的第一个元素。

而剩余的第二个元素和第三个元素,分别设置 left right i+1 n-1 的位置从两边往中间靠拢。

代码框架如下

# 考虑第一个数first
for i, (first, idx) in enumerate(nums[:-2]):
    # 如果发现第i、第i+1、第i+2个数的和加起来超过target
    # 则更靠后的的选择肯定和更大,直接退出循环
    if nums[i][0] + nums[i+1][0] + nums[i+2][0] > target:
        break
    # 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]所作的计算是一样的
    # 直接跳过nums[i]的计算
    if i != 0 and nums[i][0] == nums[i-1][0]:
        continue
    # 构建相向双指针left和right
    left, right = i+1, n-1
    # 双指针过程
    while left < right:
        pass

接下来就是填充双指针过程,即上述代码框架中的 while 循环。

我们令当前的三数之和为 sum3 = first + nums[left][0] + nums[right][0] 。当

  • sum3 > target 时,三数之和太大, sum3 需要减小, right 左移
  • sum3 < target 时,三数之和太小, sum3 需要增加, left 右移
  • sum3 == target 时,此时所选择的三个数的和为题目输入的目标和 target ,我们需要更新答案。同时只有 right 左移。

此处需要特别讨论,为什么在 sum3 == target 的时候,我们的代码中只有 right 左移,而不是 left 右移或者 left right 同时移动。

在LeetCode15、三数之和 中,由于我们要找的是 元素组合 ,而不是 元素的索引组合 ,因此我们无论写成 left 右移、 right 左移还是 left right 同时移动,答案都是正确的。

这三种写法,都能够避免 while 的死循环(也是这里指针移动的唯一作用)。

这是因为,只需要保证 while 中的每一个 if 分支中,至少有一个指针是在变化的,那么 while 中的 left < right 条件就一定会有不成立的时候,会顺利退出循环。

但是在本题中,由于我们要找的是元素的索引组合,并且要求是找到 索引和最小的索引组合

可能会存在一种情况,即 nums[right-1][0] == nums[right][0]

由于当 num 相等时元素按照在原数组中的索引 idx 进行从小到大排序,此时必然存在 nums[right-1][1] < nums[right][1]

譬如数组

nums = [(14), (20), (22), (45), (51), (53)]

假设 target = 9 ,当 i = 1 left = 2 right = 5 时,我们已经找到了 (2, 0) , (2, 2) , (5, 3) 这三个元素它们的数字和为 target ,索引和为 5

由于 nums[4][0] == nums[5][0] == 5

如果此时我们移动了 left ,我们将会错过索引和更小的 (2, 0) , (2, 2) , (5, 1) 这样的组合( i = 1 left = 2 right = 4 ,索引和为 3 )。

所以当我们找到一组答案的时候,不能够移动 left (即使这样在其他题目中的正确的),只能移动 right 。这样才能够 尽可能地找到更小的索引和

因此,整体的代码如下

# 考虑第一个数first
for i, (first, idx) in enumerate(nums[:-2]):
    # 如果发现第i、第i+1、第i+2个数的和加起来超过target
    # 则更靠后的的选择肯定和更大,直接退出循环
    if nums[i][0] + nums[i+1][0] + nums[i+2][0] > target:
        break
    # 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]所作的计算是一样的
    # 直接跳过nums[i]的计算
    if i != 0 and nums[i][0] == nums[i-1][0]:
        continue
    # 构建相向双指针left和right
    left, right = i+1, n-1
    # 双指针过程
    while left < right:
        # 计算三个数的和
        sum3 = first + nums[left][0] + nums[right][0]
        # 三数和太大,right左移
        if sum3 > target:
            right -= 1
        # 三数和太小,left右移
        elif sum3 < target:
            left += 1
        # 三数和等于target,如果三个下标和比之前记录过的三数下标和更小
        # 则更新ans_idx
        else:
            if sum(ans_idx) > idx + nums[left][1] + nums[right][1]:
                ans_idx = [idx, nums[left][1], nums[right][1]]
            # 注意此处,只需要写right -= 1就行,不需要加上left += 1
            # 1. 只写right -= 1就可以保证代码不会陷入死循环
            # 因为只需要保证每一个if分支中,至少有一个指针是在变化的(也就是我们选择的right)
            # 那么while中的left < right条件就一定会有不成立的时候
            # 2. 由于排序时先按照num大小排列,再按照num在原数组中的索引排列。
            # 如果此时存在 nums[right-1][0] == nums[right][0]
            # 那么nums[right-1][1]会小于nums[right][1]
            # i, nums[left][1]和nums[right-1][1]可以构成更小的索引和
            # 如果加上了left += 1,可能会漏算全局最小的索引和
            right -= 1

代码

Python

# https://www.algomooc.com/problem/P3004

from math import inf

# 输入原数组lst
lst = list(map(int, input().split(",")))
# 输入目标和
target = int(input())

# 获得原数组长度n
n = len(lst)
# 对原nums数组进行排序,同时需要记录在原数组中的下标i,作为排序的第二个依据
nums = sorted((num, idx) for idx, num in enumerate(lst))

# 初始化一个长度为3的数组,用于储存三个数的下标,初始化均为inf
ans_idx = [inf, inf, inf]

# 考虑第一个数first
for i, (first, idx) in enumerate(nums[:-2]):
    # 如果发现第i、第i+1、第i+2个数的和加起来超过target
    # 则更靠后的的选择肯定和更大,直接退出循环
    if nums[i][0] + nums[i+1][0] + nums[i+2][0] > target:
        break
    # 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]作为第一个数字
    # 所作的计算是一样的。直接跳过nums[i]的计算
    if i != 0 and nums[i][0] == nums[i-1][0]:
        continue
    # 构建相向双指针left和right
    left, right = i+1, n-1
    while left < right:
        # 计算三个数的和
        sum3 = first + nums[left][0] + nums[right][0]
        # 三数和太大,right左移
        if sum3 > target:
            right -= 1
        # 三数和太小,left右移
        elif sum3 < target:
            left += 1
        # 三数和等于target,如果三个下标和比之前记录过的三数下标和更小
        # 则更新ans_idx
        else:
            if sum(ans_idx) > idx + nums[left][1] + nums[right][1]:
                ans_idx = [idx, nums[left][1], nums[right][1]]
            # 注意此处,只需要写right -= 1就行,不需要加上left += 1
            # 1. 只写right -= 1就可以保证代码不会陷入死循环
            # 因为只需要保证每一个if分支中,至少有一个指针是在变化的(也就是我们选择的right)
            # 那么while中的left < right条件就一定会有不成立的时候
            # 2. 由于排序时先按照num大小排列,再按照num在原数组中的索引排列。
            # 如果此时存在 nums[right-1][0] == nums[right][0]
            # 那么nums[right-1][1]会小于nums[right][1]
            # i, nums[left][1]和nums[right-1][1]可以构成更小的索引和
            # 如果加上了left += 1,可能会漏算全局最小的索引和
            right -= 1


# 退出循环,ans_idx为在原数组中的三个数的下标
# 答案需要按照下标大小进行排列
ans_idx.sort()
ans = ",".join(str(lst[idx]) for idx in ans_idx)
print(ans)

Java

import java.util.*;

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

        // 读取一行包含逗号分隔数字的数组
        String line = scanner.nextLine();

        // 使用 StringTokenizer 分割数字
        StringTokenizer tokenizer = new StringTokenizer(line, ",");
        List lst = new ArrayList<>();
        while (tokenizer.hasMoreTokens()) {
            lst.add(Integer.parseInt(tokenizer.nextToken()));
        }

        // 读取下一行的数字
        int target = scanner.nextInt();

        // 获得原数组长度 n
        int n = lst.size();
        // 对原 nums 数组进行排序,同时需要记录在原数组中的下标 i,作为排序的第二个依据
        List nums = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            nums.add(new Pair(lst.get(i), i));
        }
        Collections.sort(nums);

        // 初始化一个长度为 3 的数组,用于储存三个数的下标,初始化均为 n
        List ansIdx = new ArrayList<>(Arrays.asList(n, n, n));

        // 考虑第一个数 first
        for (int i = 0; i < n - 2; i++) {
            // 如果发现第 i、第 i+1、第 i+2 个数的和加起来超过 target
            // 则更靠后的选择肯定和更大,直接退出循环
            if (nums.get(i).first + nums.get(i + 1).first + nums.get(i + 2).first > target) {
                break;
            }
            // 去重操作,如果遇到连续两个数相等,那么考虑 nums[i] 和考虑 nums[i-1] 所作的计算是一样的
            // 直接跳过 nums[i] 的计算
            if (i != 0 && nums.get(i).first == nums.get(i - 1).first) {
                continue;
            }
            // 构建相向双指针 left 和 right
            int left = i + 1, right = n - 1;
            while (left < right) {
                // 计算三个数的和
                int sum3 = nums.get(i).first + nums.get(left).first + nums.get(right).first;
                // 三数和太大,right 左移
                if (sum3 > target) {
                    right--;
                }
                // 三数和太小,left 右移
                else if (sum3 < target) {
                    left++;
                }
                // 三数和等于 target,如果三个下标和比之前记录过的三数下标和更小
                // 则更新 ansIdx
                else {
                    if (ansIdx.get(0) + ansIdx.get(1) + ansIdx.get(2) > nums.get(i).second + nums.get(left).second + nums.get(right).second) {
                        ansIdx = Arrays.asList(nums.get(i).second, nums.get(left).second, nums.get(right).second);
                    }
                    // 注意此处,只需要写 righ-- 就行,不需要加上 left++
                    // 1. 只写right -= 1就可以保证代码不会陷入死循环
                    // 因为只需要保证每一个if分支中,至少有一个指针是在变化的(也就是我们选择的right)
                    // 那么while中的left < right条件就一定会有不成立的时候
                    // 2. 由于排序时先按照num大小排列,再按照num在原数组中的索引排列。
                    // 如果此时存在 nums[right-1][0] == nums[right][0]
                    // 那么nums[right-1][1]会小于nums[right][1]
                    // i, nums[left][1]和nums[right-1][1]可以构成更小的索引和
                    // 如果加上了 left++ ,可能会漏算全局最小的索引和
                    right--;
                }
            }
        }

        // 退出循环,ansIdx 为在原数组中的三个数的下标
        // 答案需要按照下标大小进行排列
        Collections.sort(ansIdx);
        for (int i = 0; i < 3; i++) {
            System.out.print(lst.get(ansIdx.get(i)));
            if (i < 2) {
                System.out.print(",");
            }
        }
        System.out.println();
    }

    static class Pair implements Comparable<Pair{
        int first;
        int second;

        Pair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public int compareTo(Pair other) {
            return Integer.compare(this.first, other.first);
        }
    }
}

C++

#include 
#include 
#include 
#include 
#include 
using namespace std;

int main() {
    // 读取一行包含逗号分隔数字的数组
    string line;
    getline(cin, line);

    // 使用 stringstream 分割数字
    stringstream ss(line);
    vector<int> lst;
    string token;
    while(getline(ss, token,','))
    {
        lst.push_back(stoi(token));
    }

    // 读取下一行的数字
    int target;
    cin >> target;
    
    // 获得原数组长度 n
    int n = lst.size();
    // 对原 nums 数组进行排序,同时需要记录在原数组中的下标 i,作为排序的第二个依据
    vectorint, int>> nums;
    for (int i = 0; i < n; i++) {
        nums.push_back({lst[i], i});
    }
    sort(nums.begin(), nums.end());

    // 初始化一个长度为 3 的数组,用于储存三个数的下标,初始化均为 n
    vector<intansIdx(3, n);

    // 考虑第一个数 first
    for (int i = 0; i < n - 2; i++) {
        // 如果发现第 i、第 i+1、第 i+2 个数的和加起来超过 target
        // 则更靠后的选择肯定和更大,直接退出循环
        if (nums[i].first + nums[i + 1].first + nums[i + 2].first > target) {
            break;
        }
        // 去重操作,如果遇到连续两个数相等,那么考虑 nums[i] 和考虑 nums[i-1] 所作的计算是一样的
        // 直接跳过 nums[i] 的计算
        if (i != 0 && nums[i].first == nums[i - 1].first) {
            continue;
        }
        // 构建相向双指针 left 和 right
        int left = i + 1, right = n - 1;
        while (left < right) {
            // 计算三个数的和
            int sum3 = nums[i].first + nums[left].first + nums[right].first;
            // 三数和太大,right 左移
            if (sum3 > target) {
                right--;
            }
            // 三数和太小,left 右移
            else if (sum3 < target) {
                left++;
            }
            // 三数和等于 target,如果三个下标和比之前记录过的三数下标和更小
            // 则更新 ansIdx
            else {
                if (accumulate(ansIdx.begin(), ansIdx.end(), 0) > nums[i].second + nums[left].second + nums[right].second) {
                    ansIdx = {nums[i].second, nums[left].second, nums[right].second};
                }
                // 注意此处,只需要写 righ-- 就行,不需要加上 left++
                // 1. 只写right -= 1就可以保证代码不会陷入死循环
                // 因为只需要保证每一个if分支中,至少有一个指针是在变化的(也就是我们选择的right)
                // 那么while中的left < right条件就一定会有不成立的时候
                // 2. 由于排序时先按照num大小排列,再按照num在原数组中的索引排列。
                // 如果此时存在 nums[right-1][0] == nums[right][0]
                // 那么nums[right-1][1]会小于nums[right][1]
                // i, nums[left][1]和nums[right-1][1]可以构成更小的索引和
                // 如果加上了 left++ ,可能会漏算全局最小的索引和
                right--;
            }
        }
    }

    // 退出循环,ansIdx 为在原数组中的三个数的下标
    // 答案需要按照下标大小进行排列
    sort(ansIdx.begin(), ansIdx.end());
    for (int i = 0; i < 3; i++) {
        cout << lst[ansIdx[i]];
        if (i < 2) {
            cout << ",";
        }
    }
    cout << endl;

    return 0;
}

C

#include 
#include 
#include 
#include 

// 定义结构体用于存储数值和下标
typedef struct {
    int value;
    int index;
} Pair;

// 比较函数,用于排序
int compare(const void *a, const void *b) {
    Pair *p1 = (Pair *)a;
    Pair *p2 = (Pair *)b;
    if (p1->value != p2->value) {
        return p1->value - p2->value;
    }
    return p1->index - p2->index;
}

int main() {
    // 输入原数组
    char input[10000];
    if (!fgets(input, sizeof(input), stdin)) {
        fprintf(stderr"Error reading input\n");
        return 1;
    }

    int lst[1000], n = 0, target;
    char *token = strtok(input, ","






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