程序员老鬼

今天给一个候选人走背调,他33岁,P7,总包谈好了78万,一切顺利。结果我打给他前司的HR,对方说"他离职前跟直属leader吵过架。。

刚刷到这个,真挺职场的。

候选人33岁,做到P7,薪资都谈到78万了,前面一路绿灯。结果卡在背调。前公司HR提了一嘴,说他走之前跟直属领导闹得不太愉快,最后几个月绩效也一般,系统评价写的是协作沟通有问题。

Image

这话一传到用人部门,领导直接不敢接了:现在HC这么紧,何必招个“可能不好管”的人进来。

最狠的地方就在这。你能力够不够、活干得怎么样,到了背调这一步,可能还不如前领导一句评价管用。跟领导吵一架,当时觉得出口气,几年后换工作,人家系统里还给你留着呢。

今日面试题

Java 实现分数加减运算,这题最容易错的不是通分

输入一串分数:

1/2+1/3-1/6

程序输出:

2/3

这题看着就是小学数学,真写代码时,坑都藏在边角上:减号怎么解析、分母为零怎么拦、负数放在分子还是分母、每算一步要不要约分。

我第一眼不太信那种把表达式按 + 和 - 直接切开的写法。碰到:

-1/2+3/4

开头这个负号很容易被切成一个空字符串,后面全是补丁。

更稳一点的处理,是把减法看成“加上一个负分数”。这样整个计算过程只保留一种操作:分数相加。

两个分数:

a / b
c / d

相加后的结果是:

(a × d + c × b) / (b × d)

算完不能直接返回。像 2/4、15/20 这种结果,必须通过最大公约数约成最简分数,否则连续运算时,分子和分母会越滚越大。

Java 代码我会这样写:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

publicclassFractionCalculator{

privatestaticfinal Pattern ITEM =
            Pattern.compile("([+-]?\\d+)/(\\d+)");

staticfinalclassFraction{
privatefinallong numerator;
privatefinallong denominator;

        Fraction(long numerator, long denominator) {
if (denominator == 0) {
thrownew IllegalArgumentException("分母不能为 0");
            }

if (denominator < 0) {
                numerator = -numerator;
                denominator = -denominator;
            }

long divisor = gcd(Math.abs(numerator), denominator);
this.numerator = numerator / divisor;
this.denominator = denominator / divisor;
        }

Fraction add(Fraction other){
long common = gcd(denominator, other.denominator);
long leftFactor = other.denominator / common;
long rightFactor = denominator / common;

long newNumerator = Math.addExact(
                    Math.multiplyExact(numerator, leftFactor),
                    Math.multiplyExact(other.numerator, rightFactor)
            );
long newDenominator =
                    Math.multiplyExact(denominator, leftFactor);

returnnew Fraction(newNumerator, newDenominator);
        }

@Override
public String toString(){
return denominator == 1
                    ? String.valueOf(numerator)
                    : numerator + "/" + denominator;
        }
    }

publicstatic Fraction calculate(String expression){
        String text = expression.replaceAll("\\s+", "");
        Matcher matcher = ITEM.matcher(text);

        Fraction result = new Fraction(0, 1);
int cursor = 0;

while (matcher.find()) {
if (matcher.start() != cursor) {
thrownew IllegalArgumentException(
"表达式格式错误,位置:" + cursor
                );
            }

long numerator = Long.parseLong(matcher.group(1));
long denominator = Long.parseLong(matcher.group(2));

            result = result.add(new Fraction(numerator, denominator));
            cursor = matcher.end();
        }

if (cursor != text.length()) {
thrownew IllegalArgumentException(
"表达式存在未识别内容,位置:" + cursor
            );
        }

return result;
    }

privatestaticlonggcd(long a, long b){
while (b != 0) {
long remainder = a % b;
            a = b;
            b = remainder;
        }
return a == 0 ? 1 : a;
    }

publicstaticvoidmain(String[] args){
        System.out.println(calculate("1/2 + 1/3 - 1/6"));
        System.out.println(calculate("-1/2 + 3/4"));
        System.out.println(calculate("7/3 - 1/3"));
    }
}

运行结果:

2/3
1/4
2

这里有个细节:代码没有直接拿两个分母相乘,而是先求最大公约数,再构造最小公倍数。结果一样,但中间数字会小一些。

另外我用了 long 和 Math.multiplyExact。数据一旦溢出,程序会直接抛异常,不会悄悄算出一个看起来正常的错误答案。题目要是明确允许超大整数,就别继续硬撑 long,把分子、分母换成 BigInteger。

这道题真正要处理的不是加减公式,而是输入解析、符号统一、分母校验和持续约分。公式写对只占一小块,边界没收好,样例能过,换组数据照样出问题。