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-4 | 2x^2+3x-4 | 多项拆分与负常数 |
-x+1 | -x+1 | 省略系数与首项负号 |
3+2x^2 | 2x^2+3 | 按指数降序排列 |
x^2+2x^2-x+4-4 | 3x^2-x | 同类项合并与零项删除 |
x-x | 0 | 合并后链表为空 |
系数和指数仍使用 int;输入的数字超出范围时,Integer.parseInt() 会抛出 NumberFormatException。这些修改解决了解析、存储和打印问题,两个多项式之间的相加方法还需要另行实现。
备注:内容仅供参考。
