PAYATHON 2026

Java 多项式输出错误:为什么输入 2x^2+3x-4 得到 2x^1?

数据阿明

输入 2x^2+3x-4 得到 2x^1,原因出在构造方法的拆分逻辑:poly.split("\\+\\-") 匹配的是连续出现的 +-,没有把多项式拆成独立的项。随后,整个表达式按 x 或 x^ 拆成三个片段,parts.length == 2 为假,指数便被设为 1。

链表插入和打印方法也各有问题,需要一起修正,才能让其他输入正常工作。

为什么会得到 2x^1?

原代码是:

String[] component = poly.split("\\+\\-");

Java 字符串中的 \\+\\- 对应正则表达式 \+\-,表示字面量 + 后紧跟字面量 -。输入中没有连续的 +-,所以 component 只有一个元素:

["2x^2+3x-4"]

接下来执行:

String[] parts = term.split("x\\^?");

这个正则匹配 x,以及它后面可选的 ^。整个表达式被拆成:

["2", "2+3", "-4"]

然后,代码根据拆分结果设置系数和指数:

coe = Integer.parseInt(parts[0]); // 2
exp = (parts.length == 2) ? Integer.parseInt(parts[1]) : 1;

parts.length 为 3,所以 exp 取 1。链表只收到一个系数为 2、指数为 1 的节点,print() 因此输出 2x^1。

如何拆分多项式并保留负号?

直接使用 [+-] 拆分会丢掉符号。例如,3x-4 会变成 3x 和 4,常数项的正负就错了。

对于整数系数、非负整数指数的输入,可以使用零宽正向先行断言:

String[] terms = input.split("(?=[+-])");

它在 + 或 - 前面拆分,同时保留符号。示例输入的拆分结果为:

["2x^2", "+3x", "-4"]

下面的构造方法会检查每一项的格式,也能处理 x、-x、+x 这样省略系数的写法。用它替换原来的 PolyList(String poly):

public PolyList(String poly) {
    this.head = null;

    if (poly == null) {
        throw new IllegalArgumentException("Polynomial must not be null");
    }

    String input = poly.replaceAll("\\s+", "");
    if (input.isEmpty()) {
        throw new IllegalArgumentException("Polynomial must not be empty");
    }

    for (String term : input.split("(?=[+-])")) {
        if (!term.matches("[+-]?(?:[0-9]*x(?:\\^[0-9]+)?|[0-9]+)")) {
            throw new IllegalArgumentException("Invalid term: " + term);
        }

        int coe;
        int exp;
        int xIndex = term.indexOf('x');

        if (xIndex < 0) {
            coe = Integer.parseInt(term);
            exp = 0;
        } else {
            String coefficient = term.substring(0, xIndex);

            if (coefficient.isEmpty() || coefficient.equals("+")) {
                coe = 1;
            } else if (coefficient.equals("-")) {
                coe = -1;
            } else {
                coe = Integer.parseInt(coefficient);
            }

            exp = (xIndex == term.length() - 1)
                    ? 1
                    : Integer.parseInt(term.substring(xIndex + 2));
        }

        insertionSort(new PolyNode(coe, exp));
    }
}

代码会忽略空白字符。例如,2x^2 + 3x - 4 会按 2x^2+3x-4 解析。格式检查只接受小写变量 x,不接受小数、括号、乘号或负指数。

双向链表插入还需要修正哪些地方?

原来的 insertionSort() 有几处问题:

  • 插入新表头后,没有更新旧表头的 prev。
  • 普通插入时,没有完整维护节点的 prev。
  • 只比较 current.next.exp,会漏掉与表头指数相同的情况。
  • current.next.next 可能为 null,直接访问它的 prev 会抛出 NullPointerException。
  • 合并同类项后继续执行插入代码,可能让节点的 next 指向自身,导致打印时无法结束。

可以先找到插入位置:如果指数相同,就更新现有节点并立即返回;否则,连接新节点。下面的实现还会删除合并后系数为零的节点:

private void insertionSort(PolyNode newNode) {
    if (newNode.coe == 0) {
        return;
    }

    PolyNode previous = null;
    PolyNode current = head;

    while (current != null && current.exp > newNode.exp) {
        previous = current;
        current = current.next;
    }

    if (current != null && current.exp == newNode.exp) {
        int sum = Math.addExact(current.coe, newNode.coe);

        if (sum != 0) {
            current.coe = sum;
        } else {
            if (previous == null) {
                head = current.next;
            } else {
                previous.next = current.next;
            }

            if (current.next != null) {
                current.next.prev = previous;
            }

            current.next = null;
            current.prev = null;
        }
        return;
    }

    newNode.prev = previous;
    newNode.next = current;

    if (previous == null) {
        head = newNode;
    } else {
        previous.next = newNode;
    }

    if (current != null) {
        current.prev = newNode;
    }
}

这样可以保持链表按指数降序排列,每个指数最多对应一个节点。系数相加超出 int 范围时,Math.addExact() 会抛出 ArithmeticException,避免静默溢出。

如何正确输出正负号和常数项?

原来的打印方法先输出当前项,再根据当前项的系数决定后面的分隔符。但每一项前面的符号应由该项自身决定;系数已经带有负号时,额外输出分隔符也容易产生 +- 或重复负号。

可以先输出当前项的符号,再输出系数绝对值、变量和指数:

public void print() {
    StringBuilder result = new StringBuilder();

    for (PolyNode current = head;
         current != null;
         current = current.next) {

        if (current.coe < 0) {
            result.append('-');
        } else if (result.length() > 0) {
            result.append('+');
        }

        long magnitude = Math.abs((long) current.coe);

        if (current.exp == 0) {
            result.append(magnitude);
        } else {
            if (magnitude != 1) {
                result.append(magnitude);
            }

            result.append('x');

            if (current.exp != 1) {
                result.append('^').append(current.exp);
            }
        }
    }

    System.out.println(result.length() == 0 ? "0" : result.toString());
}

输出时,常数项不带 x^0,一次项不带 ^1,非常数项的系数为 1 或 -1 时省略数字 1。代码先将系数转为 long 再取绝对值,以便正确处理 Integer.MIN_VALUE 的绝对值。

如何检查修正后的行为?

保留原来的 PolyNode 类和无参构造方法,将上面三个方法放入 PolyList。现有的 main 方法可以继续用于检查解析与输出。

以下是按这套规则应得到的结果,可作为验证用例:

输入预期输出检查内容
2x^2+3x-42x^2+3x-4多项拆分与负常数
-x+1-x+1省略系数与首项负号
3+2x^22x^2+3按指数降序排列
x^2+2x^2-x+4-43x^2-x同类项合并与零项删除
x-x0合并后链表为空

系数和指数仍使用 int;输入的数字超出范围时,Integer.parseInt() 会抛出 NumberFormatException。这些修改解决了解析、存储和打印问题,两个多项式之间的相加方法还需要另行实现。

备注:内容仅供参考。