华为不同职级 薪资待遇一览表。。。
大家好,我是吴师兄。
最近看到一张华为不同职级的薪资待遇表,不得不说,真的是让人看了心动又焦虑。
对于我们这些职场小白来说,华为的职级体系和薪资待遇就像是职业生涯的一个“终极目标”,但这也意味着:有多少人能从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;
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) foridx, num inenumerate(lst))
注意此处我们新建的nums是一个二维数组,其中的内层元素(num, idx)是一个二元组,其中num是元素值,idx是num在原输入数组lst中的索引。
nums的中元素的排序,会先按照num的大小进行从小到大排序,当num相等时,则按照其出现在lst中的索引idx进行从小到大排序。
譬如对于例子
lst = [ 2, 5, 2, 5, 1, 4]
我们会得到
nums = [( 1, 4), ( 2, 0), ( 2, 2), ( 4, 5), ( 5, 1), ( 5, 3)]
双指针过程
接下来是非常经典的双指针过程。
对于nums中的元素,我们遍历里面里面的每一组(num, idx)(其在nums中的索引值为i),将其固定为选择的第一个元素。
而剩余的第二个元素和第三个元素,分别设置left和right从i+1和n-1的位置从两边往中间靠拢。
代码框架如下
# 考虑第一个数first
fori, (first, idx) inenumerate(nums[: -2]):
# 如果发现第i、第i+1、第i+2个数的和加起来超过target
# 则更靠后的的选择肯定和更大,直接退出循环
ifnums[i][ 0] + nums[i+ 1][ 0] + nums[i+ 2][ 0] > target:
break
# 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]所作的计算是一样的
# 直接跳过nums[i]的计算
ifi != 0andnums[i][ 0] == nums[i -1][ 0]:
continue
# 构建相向双指针left和right
left, right = i+ 1, n -1
# 双指针过程
whileleft < 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 = [( 1, 4), ( 2, 0), ( 2, 2), ( 4, 5), ( 5, 1), ( 5, 3)]
假设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
fori, (first, idx) inenumerate(nums[: -2]):
# 如果发现第i、第i+1、第i+2个数的和加起来超过target
# 则更靠后的的选择肯定和更大,直接退出循环
ifnums[i][ 0] + nums[i+ 1][ 0] + nums[i+ 2][ 0] > target:
break
# 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]所作的计算是一样的
# 直接跳过nums[i]的计算
ifi != 0andnums[i][ 0] == nums[i -1][ 0]:
continue
# 构建相向双指针left和right
left, right = i+ 1, n -1
# 双指针过程
whileleft < right:
# 计算三个数的和
sum3 = first + nums[left][ 0] + nums[right][ 0]
# 三数和太大,right左移
ifsum3 > target:
right -= 1
# 三数和太小,left右移
elifsum3 < target:
left += 1
# 三数和等于target,如果三个下标和比之前记录过的三数下标和更小
# 则更新ans_idx
else:
ifsum(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
frommath importinf
# 输入原数组lst
lst = list(map(int, input.split( ",")))
# 输入目标和
target = int(input)
# 获得原数组长度n
n = len(lst)
# 对原nums数组进行排序,同时需要记录在原数组中的下标i,作为排序的第二个依据
nums = sorted((num, idx) foridx, num inenumerate(lst))
# 初始化一个长度为3的数组,用于储存三个数的下标,初始化均为inf
ans_idx = [inf, inf, inf]
# 考虑第一个数first
fori, (first, idx) inenumerate(nums[: -2]):
# 如果发现第i、第i+1、第i+2个数的和加起来超过target
# 则更靠后的的选择肯定和更大,直接退出循环
ifnums[i][ 0] + nums[i+ 1][ 0] + nums[i+ 2][ 0] > target:
break
# 去重操作,如果遇到连续两个数相等,那么考虑nums[i]和考虑nums[i-1]作为第一个数字
# 所作的计算是一样的。直接跳过nums[i]的计算
ifi != 0andnums[i][ 0] == nums[i -1][ 0]:
continue
# 构建相向双指针left和right
left, right = i+ 1, n -1
whileleft < right:
# 计算三个数的和
sum3 = first + nums[left][ 0] + nums[right][ 0]
# 三数和太大,right左移
ifsum3 > target:
right -= 1
# 三数和太小,left右移
elifsum3 < target:
left += 1
# 三数和等于target,如果三个下标和比之前记录过的三数下标和更小
# 则更新ans_idx
else:
ifsum(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]) foridx inans_idx)
print(ans)
Javaimportjava.util.*;
publicclassMain{
publicstaticvoidmain(String[] args){
Scanner scanner = newScanner(System.in);
// 读取一行包含逗号分隔数字的数组
String line = scanner.nextLine;
// 使用 StringTokenizer 分割数字
StringTokenizer tokenizer = newStringTokenizer(line, ",");
List<Integer> lst = newArrayList<>;
while(tokenizer.hasMoreTokens) {
lst.add(Integer.parseInt(tokenizer.nextToken));
}
// 读取下一行的数字
inttarget = scanner.nextInt;
// 获得原数组长度 n
intn = lst.size;
// 对原 nums 数组进行排序,同时需要记录在原数组中的下标 i,作为排序的第二个依据
List<Pair> nums = newArrayList<>;
for( inti = 0; i < n; i++) {
nums.add( newPair(lst.get(i), i));
}
Collections.sort(nums);
// 初始化一个长度为 3 的数组,用于储存三个数的下标,初始化均为 n
List<Integer> ansIdx = newArrayList<>(Arrays.asList(n, n, n));
// 考虑第一个数 first
for( inti = 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
intleft = i + 1, right = n - 1;
while(left < right) {
// 计算三个数的和
intsum3 = nums.get(i).first + nums.get(left).first + nums.get(right).first;
// 三数和太大,right 左移
if(sum3 > target) {
right--;
}
// 三数和太小,left 右移
elseif(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( inti = 0; i < 3; i++) {
System.out.print(lst.get(ansIdx.get(i)));
if(i < 2) {
System.out.print( ",");
}
}
System.out.println;
}
staticclassPairimplementsComparable< Pair> {
intfirst;
intsecond;
Pair( intfirst, intsecond) {
this.first = first;
this.second = second;
}
@Override
publicintcompareTo(Pair other){
returnInteger.compare( this.first, other.first);
}
}
}
C++# include<iostream>
# include<sstream>
# include<vector>
# include<algorithm>
# include<numeric>
usingnamespacestd;
intmain{
// 读取一行包含逗号分隔数字的数组
stringline;
getline( cin, line);
// 使用 stringstream 分割数字
stringstreamss(line);
vector< int> lst;
stringtoken;
while(getline(ss, token, ','))
{
lst.push_back(stoi(token));
}
// 读取下一行的数字
inttarget;
cin>> target;
// 获得原数组长度 n
intn = lst.size;
// 对原 nums 数组进行排序,同时需要记录在原数组中的下标 i,作为排序的第二个依据
vector<pair< int, int>> nums;
for( inti = 0; i < n; i++) {
nums.push_back({lst[i], i});
}
sort(nums.begin, nums.end);
// 初始化一个长度为 3 的数组,用于储存三个数的下标,初始化均为 n
vector< int> ansIdx( 3, n) ;
// 考虑第一个数 first
for( inti = 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
intleft = i + 1, right = n - 1;
while(left < right) {
// 计算三个数的和
intsum3 = nums[i].first + nums[left].first + nums[right].first;
// 三数和太大,right 左移
if(sum3 > target) {
right--;
}
// 三数和太小,left 右移
elseif(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( inti = 0; i < 3; i++) {
cout<< lst[ansIdx[i]];
if(i < 2) {
cout<< ",";
}
}
cout<< endl;
return0;
}
C# include<stdio.h>
# include<stdlib.h>
# include<limits.h>
# include<string.h>
// 定义结构体用于存储数值和下标
typedefstruct{
intvalue;
intindex;
} Pair;
// 比较函数,用于排序
intcompare( constvoid*a, constvoid*b) {
Pair *p1 = (Pair *)a;
Pair *p2 = (Pair *)b;
if(p1->value != p2->value) {
returnp1->value - p2->value;
}
returnp1->index - p2->index;
}
intmain{
// 输入原数组
charinput[ 10000];
if(!fgets(input, sizeof(input), stdin)) {
fprintf( stderr, "Error reading input\n");
return1;
}
intlst[ 1000], n = 0, target;
char*token = strtok(input, ",");
while(token != NULL) {
if(n >= 1000) {
fprintf( stderr, "Input exceeds maximum array size\n");
return1;
}
lst[n++] = atoi(token);
token = strtok( NULL, ",");
}
// 输入目标和
if( scanf( "%d", &target) != 1) {
fprintf( stderr, "Error reading target\n");
return1;
}
// 检查是否有足够的元素
if(n < 3) {
fprintf( stderr, "Not enough elements in input\n");
return1;
}
// 构建一个Pair数组用于存储数值和下标
Pair nums[ 1000];
for( inti = 0; i < n; i++) {
nums[i].value = lst[i];
nums[i].index = i;
}
// 对nums数组按值和下标排序
qsort(nums, n, sizeof(Pair), compare);
// 初始化用于存储答案的下标数组
intans_idx[ 3] = {INT_MAX, INT_MAX, INT_MAX};
// 遍历第一个数
for( inti = 0; i < n - 2; i++) {
// 如果三数之和超过目标和,退出循环
if(nums[i].value + nums[i + 1].value + nums[i + 2].value > target) {
break;
}
// 去重操作
if(i > 0&& nums[i].value == nums[i - 1].value) {
continue;
}
// 双指针初始化
intleft = i + 1, right = n - 1;
while(left < right) {
intsum3 = nums[i].value + nums[left].value + nums[right].value;
if(sum3 > target) {
right--;
} elseif(sum3 < target) {
left++;
} else{
// 如果当前索引和更小,则更新答案
if(nums[i].index + nums[left].index + nums[right].index <
ans_idx[ 0] + ans_idx[ 1] + ans_idx[ 2]) {
ans_idx[ 0] = nums[i].index;
ans_idx[ 1] = nums[left].index;
ans_idx[ 2] = nums[right].index;
}
// 注意此处,只需要写 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--;
}
}
}
// 如果没有找到答案,直接退出
if(ans_idx[ 0] == INT_MAX) {
return1;
}
// 按下标大小排序
for( inti = 0; i < 2; i++) {
for( intj = 0; j < 2- i; j++) {
if(ans_idx[j] > ans_idx[j + 1]) {
inttemp = ans_idx[j];
ans_idx[j] = ans_idx[j + 1];
ans_idx[j + 1] = temp;
}
}
}
// 输出答案
printf( "%d,%d,%d\n", lst[ans_idx[ 0]], lst[ans_idx[ 1]], lst[ans_idx[ 2]]);
return0;
}
Node Java// 导入必要的模块
constreadline = require( "readline");
// 创建输入接口
constrl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question( "", (line1) => {
rl.question( "", (line2) => {
constlst = line1.split( ",").map( Number); // 读取并解析输入数组
consttarget = parseInt(line2); // 读取目标和
constn = lst.length; // 获取数组长度
// 对原数组进行排序,同时记录下标
constnums = lst
.map( ( num, i) => ({ num, index: i }))
.sort( ( a, b) => a.num - b.num || a.index - b.index);
// 初始化用于存储三个数下标的数组,初始值为正无穷
letansIdx = [ Infinity, Infinity, Infinity];
// 遍历第一个数
for( leti = 0; i < n - 2; i++) {
constfirst = nums[i].num;
constidx = nums[i].index;
// 如果当前三个数的最小和已经超过目标值,直接退出循环
if(nums[i].num + nums[i + 1].num + nums[i + 2].num > target) {
break;
}
// 去重操作,跳过连续相同的数字
if(i > 0&& nums[i].num === nums[i - 1].num) {
continue;
}
// 双指针初始化
letleft = i + 1,
right = n - 1;
while(left < right) {
constsum3 = first + nums[left].num + nums[right].num;
if(sum3 > target) {
right--;
} elseif(sum3 < target) {
left++;
} else{
// 如果当前索引和更小,更新答案
constcandidateIdx = [idx, nums[left].index, nums[right].index];
constcandidateSum = candidateIdx.reduce( ( a, b) => a + b, 0);
constcurrentSum = ansIdx.reduce( ( a, b) => a + b, 0);
if(candidateSum < currentSum) {
ansIdx = candidateIdx;
}
// 注意此处,只需要写 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( ( a, b) => a - b);
// 输出结果
console.log(ansIdx.map( ( idx) => lst[idx]).join( ","));
rl.close;
});
});
Gopackagemain
import(
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
)
typePair struct{
Num int
Index int
}
funcmain{
reader := bufio.NewReader(os.Stdin)
line1, _ := reader.ReadString( '\n')
line1 = strings.TrimSpace(line1)
line2, _ := reader.ReadString( '\n')
line2 = strings.TrimSpace(line2)
// 解析输入
strNums := strings.Split(line1, ",")
lst := make([] int, len(strNums))
fori, s := rangestrNums {
lst[i], _ = strconv.Atoi(s)
}
target, _ := strconv.Atoi(line2)
n := len(lst)
// 对原数组进行排序,同时记录下标
nums := make([]Pair, n)
fori := 0; i < n; i++ {
nums[i] = Pair{Num: lst[i], Index: i}
}
sort.Slice(nums, func(i, j int) bool{
ifnums[i].Num == nums[j].Num {
returnnums[i].Index < nums[j].Index
}
returnnums[i].Num < nums[j].Num
})
// 初始化用于存储三个数下标的数组,初始值为正无穷
ansIdx := [] int{ 1<< 30, 1<< 30, 1<< 30}
// 遍历第一个数
fori := 0; i < n -2; i++ {
first := nums[i].Num
idx := nums[i].Index
// 如果当前三个数的最小和已经超过目标值,直接退出循环
ifnums[i].Num+nums[i+ 1].Num+nums[i+ 2].Num > target {
break
}
// 去重操作,跳过连续相同的数字
ifi > 0&& nums[i].Num == nums[i -1].Num {
continue
}
// 双指针初始化
left, right := i+ 1, n -1
forleft < right {
sum3 := first + nums[left].Num + nums[right].Num
ifsum3 > target {
right--
} elseifsum3 < target {
left++
} else{
// 如果当前索引和更小,更新答案
candidateIdx := [] int{idx, nums[left].Index, nums[right].Index}
candidateSum := candidateIdx[ 0] + candidateIdx[ 1] + candidateIdx[ 2]
currentSum := ansIdx[ 0] + ansIdx[ 1] + ansIdx[ 2]
ifcandidateSum < currentSum {
ansIdx = candidateIdx
}
// 注意此处,只需要写 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--
}
}
}
// 按照下标排序
sort.Ints(ansIdx)
// 输出结果
fmt.Printf( "%d,%d,%d\n", lst[ansIdx[ 0]], lst[ansIdx[ 1]], lst[ansIdx[ 2]])
}
时空复杂度
时间复杂度:O(``n^2+nlogn``)。O(n^2)为双重循环所需时间复杂度,O(nlogn)为排序所需的时间复杂度。
空间复杂度:O(``n``)。列表nums所占空间。