引入:

偶尔看到有道算法题是要输入任意2个一元多项式(所谓一元多项式就是只有一个自变量,比如X),要求计算其和。


分析

(1)考虑到多项式的加法就是合并同类项的过程,刚开始考虑到时候可能用顺序表来实现,但是考虑到给定的多项式输入可能指数差别很大,比如某个项最高指数为1000,最低指数为-1000,我们没必要给出一个长度为2000的顺序表,这样太浪费内存了,所以我们考虑到用单向链表来实现,其中每个项作为单向链表的某个节点,然后多项式就是一个单向链表。


(2)这里还必须考虑到用户的输入,比如可以输入正指数,可能输出负指数,而且给的项的幂可能是乱的,我们必须按照通常的降幂排列这个多项式,然后才可以计算。


(3)最后,在输出多项式时候还必须要注意特殊的幂,比如一般的项的输出形式可能是X^2这种形式,但是1阶则不用输出^, 0阶则既不用输出X,也不用输出0,还有就是如果阶为负数,则应该用括号括出来。



实践:

掌握了上述的方法后,这个很显而易见就能实现出来了。

首先定义一个多项式的项Item类,它是一个单向链表:

package com.charles.algo.ploy;
/**
 * @author charles.wang
 * 这是一个多项式的项
 */
public class Item {
                                                                                                                                                                                         
    //项的系数
    private int xishu;
    //项的指数
    private int pos;
    //下一个项
    private Item next;
                                                                                                                                                                                         
    public Item(int xishu,int pos,Item next){
        this.xishu=xishu;
        this.pos = pos;
        this.next= next;
    }
                                                                                                                                                                                         
    public int getXishu() {
        return xishu;
    }
    public void setXishu(int xishu) {
        this.xishu = xishu;
    }
    public int getPos() {
        return pos;
    }
    public void setPos(int pos) {
        this.pos = pos;
    }
    public Item getNext() {
        return next;
    }
    public void setNext(Item next) {
        this.next = next;
    }
                                                                                                                                                                                         
                                                                                                                                                                                         
}



然后我们就来定义多项式了,它应该包括许多基本操作,比如如果构造一个多项式,就是添加一个新项,如果找到和参数的项匹配的位于该多项式的项,如果把该多项式和一个单项做加法,如果按照降幂顺序打印出当前多项式。我的代码有了非常详尽的注释,所以这不再罗嗦了。

package com.charles.algo.ploy;
/**
 * 这里用一个单向链表来表示一个多项式, 因为多项式的指数的跨度可能很多,所以不适合用顺序表来表示
 *
 * @author charles.wang
 *
 */
public class Poly {
    // 最高项
    private Item highestItem;
    /**
     * 构造器
     */
    public Poly() {
        highestItem = new Item(0, 0, null);
    }
    /**
     * 插入一个新项到已有的多项式,这个项必须和已知多项式的任何一个项都不相同
     *
     * @return
     */
    public Poly insertItemToPoly(int xishu, int pos) {
        // 如果当前的Poly什么都没有,那么直接构造一个项并且赋值给highestItem
        if (highestItem.getXishu() == 0) {
            Item newItem = new Item(xishu, pos, null);
            highestItem = newItem;
            return this;
        }
        // 如果当前的pos(幂)和当前Poly的某一个相同了,那么则无法插入这个Item
        if (searchSamePosItem(pos) != null) {
            return this;
        }
        // 如果当前指数高于最高项,那么新创建这个项,并且设为最高项
        if (highestItem.getPos() < pos) {
            Item newItem = new Item(xishu, pos, null);
            // 吧新创建的项设为最高项
            newItem.setNext(highestItem);
            // 更新当前最高项
            highestItem = newItem;
            return this;
        }
        // 如果当前指数低于最高项,那么新创建这个项,并且按照降序排列找到合适的位置插入
        Item newItem = new Item(xishu, pos, null);
        Item currentItem = highestItem;
        Item currentItemNextItem = null;
        // 跳出循环条件是遍历到了多项式的最低项
        while (currentItem != null) {
            // 获取当前项的下一个项
            currentItemNextItem = currentItem.getNext();
            // 如果当前项的下一个项为null,则说明已经到了尾部,则直接插入,并且跳出循环
            if (currentItemNextItem == null) {
                currentItem.setNext(newItem);
                break;
            }
            // 如果当前项的下一个项不为null,则比较当前的指数是否在2个项的指数之间,如果是,则插入,并且跳出循环,否则遍历下一个
            else if (pos < currentItem.getPos()
                    && pos > currentItemNextItem.getPos()) {
                newItem.setNext(currentItemNextItem);
                currentItem.setNext(newItem);
                break;
            }
            // 否则,更新currentItem为currentItemNextItem,并且进行下一次循环
            currentItem = currentItemNextItem;
        }
        return this;
    }
    /**
     * 为当前多项式做加法运算,则会找到指数相同的项,然后合并同类项
     */
    public Poly addOperatorWithSingleItem(int xishu, int pos) {
        // 如果当前的Poly什么都没有,那么直接构造一个项并且赋值给highestItem
        if (highestItem.getXishu() == 0) {
            Item newItem = new Item(xishu, pos, null);
            highestItem = newItem;
            return this;
        }
        // 如果当前的pos(幂)和当前Poly的某一个项的指数相同,则合并同类项
        Item matchingItem = searchSamePosItem(pos);
        if (matchingItem != null) {
            // 吧系数相加获得新的系数
            int newXishu = matchingItem.getXishu() + xishu;
            // 遍历多项式来找到匹配的,更新前后引用对象
            Item currentItem = highestItem;
            Item currentItemNextItem;
            while (currentItem != null) {
                // 如果是最高项,并且匹配指数
                if (currentItem == highestItem && currentItem.getPos() == pos) {
                    // 创建新的项,并且让其成为最高项
                    Item newItem = new Item(newXishu, pos, null);
                    newItem.setNext(highestItem.getNext());
                    highestItem = newItem;
                    return this;
                }
                // 如果最高项的下一个项为null,这不可能,
                // 因为我们已经search到matchingItems了,所以这里蕴含着最高项的下一个项一定不为null,因此不用判断
                currentItemNextItem = currentItem.getNext();
                // 找到匹配的了,执行替换
                if (currentItemNextItem.getPos() == pos) {
                    Item newItem = new Item(newXishu, pos, null);
                    newItem.setNext(currentItemNextItem.getNext());
                    currentItem.setNext(newItem);
                    return this;
                }
                // 找不到匹配的了,则继续往更低的项上找
                currentItem = currentItemNextItem;
            }
            return this;
        }
        // 如果当前的pos(幂)和当前Poly的任意一个项的指数都不同,那么做insertItemToPloy操作
        else
            return insertItemToPoly(xishu, pos);
    }
    /**
     * 在当前多项式中搜索与给定的指数相同的项
     */
    private Item searchSamePosItem(int pos) {
        // 如果当前的Poly什么都没有,当然也就没有匹配指数的项了
        if (highestItem.getXishu() == 0) {
            return null;
        }
        // 如果给定的指数高于最高指数,那么肯定没有匹配
        if (highestItem.getPos() < pos) {
            return null;
        }
        // 否则,从最高项开始遍历,一直遍历到最低项,找到满足条件的项
        Item searchingItem = highestItem;
        while (searchingItem != null) {
            // 获取当前的查阅的项的指数
            int posOfSearchingItem = searchingItem.getPos();
            if (posOfSearchingItem == pos)
                return searchingItem;
            // 否则,将当前搜索的Item移到下一个低于当前Item指数的项
            searchingItem = searchingItem.getNext();
        }
        // 如果一直搜到searchingItem为null了(也就是一直搜索到多项式的最后一个项了依然找不到),那么返回null表明找不到匹配指数的项
        return null;
    }
    /**
     * 按照降幂顺序打印出当前的多项式
     */
    public void printPoly() {
        Item currentItem = highestItem;
        Item currentItemNextItem;
        int xishu;
        int pos;
        // 从最高项开始遍历
        while (currentItem != null) {
            // 获取当前的要打印项的系数和指数,以及下一个项目
            xishu = currentItem.getXishu();
            pos = currentItem.getPos();
            currentItemNextItem = currentItem.getNext();
            // 打印系数
            System.out.print(xishu);
            // 如果当前指数为0阶,则不用打印X
            // 如果当前指数是1阶,那么只打印X,不打印^
            if (pos == 1)
                System.out.print("X");
            // 如果当前指数既不是0阶又不是1阶,则打印出 X^ ,后面跟上指数
            else if (pos != 0 && pos != 1) {
                // 如果指数小于0,则用()括号括出来
                if (pos < 0)
                    System.out.print("X^" + "(" + pos + ")");
                else
                    System.out.print("X^" + pos);
            }
            // 如果下个项存在并且下个项系数大于0,则打印加号,否则就不打印符号。
            // 因为 (1)如果项已经达到项尾则不用带符号
            // (2)如果系数小于0自带减号,
            if (currentItemNextItem != null
                    && currentItemNextItem.getXishu() > 0)
                System.out.print("+");
            currentItem = currentItemNextItem;
        }
    }
    /**
     * 返回多项式的最高项Item
     *
     */
    public Item getHighestItem() {
        return highestItem;
    }
    public static void main(String[] args) {
        Poly poly1 = new Poly();
        // 构造多项式 5X+3X^4-7X^2+6+12X^(-2) ,它应该被正确输入
        poly1.insertItemToPoly(5, 1).insertItemToPoly(3, 4)
                .insertItemToPoly(-7, 2).insertItemToPoly(6, 0)
                .insertItemToPoly(12, -2);
        System.out.print("原始多项式为:");
        poly1.printPoly();
        System.out.println();
        // 执行多项式与某个项的相加
        // 第一个被加的项6X^3和其中任何一个项的指数都不同
        poly1.addOperatorWithSingleItem(6, 3);
        System.out.print("加上6X^3后,新多项式为:");
        poly1.printPoly();
        System.out.println();
        // 执行多项式与某个项的相加
        // 第二个被加的项7X^4和其中最高项指数相同
        poly1.addOperatorWithSingleItem(7, 4);
        System.out.print("加上7X^4后,新多项式为:");
        poly1.printPoly();
        System.out.println();
        // 执行多项式与某个项相加
        // 第三个被加的项9X^2和其中非最高项-7X^2指数相同
        poly1.addOperatorWithSingleItem(9, 2);
        System.out.print("加上9X^2后,新多项式为:");
        poly1.printPoly();
        System.out.println();
    }
}


我们来测试这个多项式的有效性,为此我们先构造多项式 5X+3X^4-7X^2+6+12X^(-2),然后依次与3个项分别相加,这3个项分别都很典型,一个是不和多项式任意项指数匹配的项,一个是与最高项指数匹配的项,一个是不和最高项指数匹配的项。我们运行上面的main方法,运行结果果然与我们所料。

wKiom1LKddGDCPZMAABWMsGEpS4518.jpg


因为我们最终的目的是执行多项式的加法,所以我们写了一个工具类,2个多项式的加法可以看成是吧多项式A和多项式B中的所有项依次相加,这也就是我们的方法实现了:

package com.charles.algo.ploy;
/**
 * @author charles.wang
 * 工具类,执行多项式相加
 */
public class PolyUtil {
                                                                      
                                                                      
    private PolyUtil(){}
                                                                      
                                                                      
    /**
     * 执行2个多项式相加
     * @param poly1
     * @param poly2
     * @return
     */
    public static Poly  polyAdd (Poly poly1,Poly poly2){
                                                                          
        Poly newPoly1 = poly1;
                                                                          
        //很简单,就是把poly2从最高多项式进行遍历,然后把每个项依次和poly1相加
                                                                          
        Item currentItemInPoly2=poly2.getHighestItem();
        while(currentItemInPoly2!=null){
            //获取poly2中当前的项,将其加到poly1多项式中
            newPoly1.addOperatorWithSingleItem(currentItemInPoly2.getXishu(), currentItemInPoly2.getPos());
            currentItemInPoly2=currentItemInPoly2.getNext();
        }
                                                                          
        return newPoly1;
                                                                          
    }
                                                                      
    public static void main(String [] args){
                                                                          
        //构造多项式1  7X^4+5X^2-6X^(-4)+13X+5
        Poly poly1 = new Poly();
        poly1.insertItemToPoly(7, 4).insertItemToPoly(5, 2).insertItemToPoly(-6, -4)
             .insertItemToPoly(13, 1).insertItemToPoly(5, 0);
        System.out.println("多项式1为:");
        poly1.printPoly();
                                                                          
                                                                          
        //构造多项式2  5X^5-3X^8+3X^2+7X+9X^(-3)+4X^(-4)
        Poly poly2= new Poly();
        poly2.insertItemToPoly(5, 5).insertItemToPoly(-3,8).insertItemToPoly(3,2)
             .insertItemToPoly(7, 1).insertItemToPoly(9, -3).insertItemToPoly(4,-4);
        System.out.println("多项式2为:");
        poly2.printPoly();
                                                                          
        //执行多项式加法
        Poly newPloy = polyAdd(poly1,poly2);
        System.out.println("执行多项式加法后,结果为:");
        newPloy.printPoly();
    }
}


我们执行上面的main()方法进行测试,果然2个多项式相加的结果是正确的。

wKiom1LKdzPQctQlAABHRIvijus211.jpg



总结

其实很多算法题目,如果能找到合适的数据结构作为切入点之后,就不难了,当然这个需要经验的积累,还有需要多编程,像我现在经常写应用代码,算法这块疏忽了,所以写起来还是有点吃力的。