【问题标题】:MergeSorting LinkedList in Java recursively在Java中递归合并排序LinkedList
【发布时间】:2014-01-14 00:48:27
【问题描述】:

所以任务是实现一个链表和对链表进行排序的合并排序。我完全意识到在工业中我很可能不需要实现这些,但我觉得这是练习 Java 的好方法。以下是我到此为止的内容:

节点类:

public class Node<E extends Comparable<E>>
    {

    public E data;
    public Node<E> next;

    public Node(E data)
    {
        this.data = data;
        next = null;
    }

    public void printData()
    {
        System.out.print(data + " ");
    }
}

LinkedList 类:

public class LinkedList<E extends Comparable<E>>
{

    protected Node<E> root;
    protected int size = 0;

    public LinkedList()
    {
        root = null;
    }


    public void addBeg(E e)
    {

        Node<E> newNode = new Node<E>(e);
        newNode.next = root;
        root = newNode;

        size++;
    }

    public Node deleteBeg()
    {
        Node<E> temp = root;
        if(!isEmpty())
        {
            root = root.next; 
            size--;
        }
        return temp;
    }

    public void setRoot(Node<E> newRoot)
    {
        root = newRoot;
    }

    public boolean isEmpty()
    {
        return root == null;
    }

    public Node<E> getRoot()
    {
        return root;
    }

    public void printList()
    {
        Node<E> cur = root;   
        while(cur!=null)
            {
                cur.printData();
                cur=cur.next;
            }
        System.out.println();        
    }
}

MergeSorter 类:

public class MergeSorter<E extends Comparable<E>>
{

    public MergeSorter()
    {

    }

    private void split(LinkedList<E> list, LinkedList<E> firHalf, LinkedList<E> secHalf)
    {
        //if 0 or only 1 elements in the list - it doesn't seem to work, however
        if(list.getRoot() == null || list.getRoot().next == null)firHalf = list;
        else{
            Node<E> slow = list.getRoot(); 
            Node<E> fast = list.getRoot().next; 
            while(fast!=null)
            {
                fast = fast.next;
                if(fast!=null)
                {
                    fast = fast.next;
                    slow = slow.next;
                } 
            }
            //If I use the following line firHalf list is empty when in the caller of this method (it's not in this method, however). Don't understand why ):
            //firHalf = list; 
            firHalf.setRoot(list.getRoot());
            secHalf.setRoot(slow.next);
            slow.next = null;
        }

    }



    private LinkedList<E> merge(LinkedList<E> a, LinkedList<E> b)
     {
        LinkedList<E> mergedList = new LinkedList<E>();    
        Node<E> dummy = new Node<E>(null);
        Node<E> tail = dummy;

        while(true)
        {         
            if(a.getRoot() == null){
                tail.next = b.getRoot();
                break;
            }
            else if(b.getRoot() == null){
                tail.next = a.getRoot();
                break;
            }

            else 
            {
                if(a.getRoot().data.compareTo(b.getRoot().data) <= 0)
                {
                    tail.next = a.getRoot();
                    tail = tail.next;
                    a.setRoot(a.getRoot().next);
                }

                else
                {   
                    tail.next = b.getRoot();
                    tail = tail.next;
                    b.setRoot(b.getRoot().next);
                }
                tail.next = null;
            }

        }
        mergedList.setRoot(dummy.next);
        return mergedList;
    }

    public void mergeSort(LinkedList<E> list)
    {
        Node<E> root = list.getRoot();
        LinkedList<E> left  = new LinkedList<E>();
        LinkedList<E> right = new LinkedList<E>();

        if(root == null || root.next == null) return; //base case
        split(list, left, right); //split

        mergeSort(left);
        mergeSort(right);

        list = merge(left, right); // when this mergeSort returns this list should be  
                                   // referenced by the left or right variable of the 
                                   // current mergeSort call (but it isn't!)
    }
}

我对 Java 相当陌生(来自 C 背景),所以如果我的代码完全错误,我提前表示诚挚的歉意。当我独立测试 MergeSorter 类中的拆分和合并方法时,一切似乎都正常(拆分由 0 或 1 个元素组成的列表不起作用,这让我发疯,但合并排序不需要这样做)。然而,mergeSort 方法不起作用,我似乎无法找到方法。我尝试自己调试它,当两半合并到一个列表中然后递归返回时似乎存在问题。新合并的列表应该由当前 mergeSort 调用的 left 或 right 变量引用,但我只得到最后一个元素而不是整个列表。

【问题讨论】:

    标签: java linked-list mergesort singly-linked-list


    【解决方案1】:

    Java 中的方法参数总是按值传递。

    这可能有点令人困惑,因为对象总是通过引用访问,所以您可能认为它们是通过引用传递的;但他们不是。相反,引用是按值传递的。

    这意味着,像这样的方法:

    public void methodThatDoesNothing(Object dst, Object src) {
        src = dst;
    }
    

    实际上什么都不做。它修改其局部变量src 以引用与局部变量dst 相同的对象,但这些只是在函数返回时消失的局部变量。它们与传递给方法的任何变量或表达式完全分开。

    所以,在你的代码中,这个:

    firHalf = list;
    

    并没有真正做任何事情。我猜你想要的是:

    while (! firHalf.isEmpty()) {
        firHalf.deleteBeg();
    }
    if (! list.isEmpty()) {
        firHalf.addBeg(list.root().data);
    }
    

    它修改了firHalf 引用的对象,因此它与list 具有相同的零或一元素。

    【讨论】:

    • 啊,我明白你的意思了。非常感谢!但是,在我看来,对于这样一个简单的任务来说,代码太多了。这不是吗:firHalf.setRoot(list.getRoot());做同样的工作?而且我认为按值传递也是递归返回时合并我的mergeSort方法无法正常工作的原因? (:
    • 所以在我的mergeSort方法中我添加了一个新的LinkedListmergedList来存储左右的合并列表:mergedList = merge(left, right),然后我将列表的根设置为通过 list.setRoot(mergedList.getRoot()) 与这个合并列表的根相同,它起作用了!但是,我觉得我的解决方案过于复杂(例如,与用 C 编写的相比)。我的代码有什么可以改进的吗?再次感谢! (:
    • @user3192253: firHalf.setRoot(list.getRoot()) 将设置firHalflist 具有相同的实际根节点,而不仅仅是相同的根值。这意味着如果有人写list.getRoot().value = ...list.getRoot().next = ...,这将改变两个列表。所以 。 . .是的,您应该这样做,但只有在 之后 您将您的 Node 类更改为不可变的,以便可以在多个列表之间安全地共享它。 (即:Node.valueNode.next 都应声明为 final 并在构造函数中初始化。)
    • (或者,您可以给您的Node 类提供递归复制列表的方法,然后编写firHalf.setRoot(list.getRoot().clone())。但我认为不变性路线更好。)
    • 好的,再次感谢您。但是,如果我将Node 更改为不可变,那么某些算法(例如反转链表或附加两个列表)将更难实现(因为两者都需要更改Node.next)。如果这被声明为最终的,那么我需要创建一个全新的新节点链表,而不是仅仅更改当前节点的引用):
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-26
    • 2015-03-02
    • 2013-03-15
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多