MergeSort not giving correct output

73 views Asked by At

I have two classes:

public class List {
    public Node _head;
}

And:

public class Node {
    public String _word;
    public Node _next;
}

I have a constructor in "List", which gets a String as a parameter and puts every word in a seperate Node, like this:

public List(String text) {
    if (text.length() == 0)
        _head = null;
    createList(text);
}

private void createList(String text) {
    int lastIndex=0; 
    String temp;        
    for (int i=0;i<text.length();i++) {
        if (text.charAt(i)==' '|| i==text.length()-1) {
            if (i == 0) { // Space in the begining
                lastIndex=1;
                continue;
            }
            if (i == text.length()-1) { // If we reached to the last char of the string

                if (text.charAt(i) == ' ') { // If it's a space, don't include it
                    temp = text.substring(lastIndex,i);
                } else {
                    temp = text.substring(lastIndex,i+1);
                }
            } else {
                temp = text.substring(lastIndex,i);
            }
            addToBegining(temp);
            lastIndex=i+1;
        }
    }
}

Anyway, when I'm trying to use a mergesort on the linked list, I can't manage to get it working.

This is the sorting code:

public Node merge_sort(Node h) {
    if (h == null || h._next == null) { return h; }
    Node middle = getMiddle(h);      //get the middle of the list
    Node sHalf = middle._next; middle._next = null;   //split the list into two halfs

    return merge(merge_sort(h), merge_sort(sHalf));  //recurse on that
}

public Node merge(Node a, Node b) {
    Node dummyHead, curr; 
    dummyHead = new Node(); 
    curr = dummyHead;
    while (a !=null && b!= null) {
        if (a._word.compareTo(b._word) <= 0) { 
            curr._next = a; 
            a = a._next; 
        } else { 
            curr._next = b; 
            b = b._next; 
        }
        curr = curr._next;
    }
    curr._next = (a == null) ? b : a;
    return dummyHead._next;
}

public Node getMiddle(Node h) {
    if (h == null) { return h; }
    Node slow, fast; 
    slow = fast = h;
    while (fast._next != null && fast._next._next != null) {
        slow = slow._next; 
        fast = fast._next._next;
    }
    return slow;
}

Any idea what's wrong? I'm trying to make a new TextList with the String "Hello New World A Dawn Is There" and Output is: "There World"..

2

There are 2 answers

2
user3337714 On

Please change as below. I think you forgot to assign the curr node to the appropriate values after the if-else.

    public Node merge(Node a, Node b)
    {
        Node dummyHead, curr;
        dummyHead = new Node();
        curr = dummyHead;
        while (a != null && b != null)
        {
            if (a._word.compareTo(b._word) <= 0)
            {
                curr._next = a;
                curr = a;
                a = a._next;
            }
            else
            {
                curr._next = b;
                curr = b;
                b = b._next;
            }
            curr = curr._next;
        }
        curr._next = (a == null) ? b : a;
        return dummyHead._next;
    }
12
blekione On

Merge sort is not suitable for Linked List as you need acces to middle element of your list (you divide list into 2 sublists). With linked list you have access only to first element of the list (singly-linked list).

It is not feasible to try to use it, as you have to first find a link to middle element every time you divide your list. Better try insertion sort or quick-sort with 1st element of linked list as a pivot.

EDIT: Yesterday, I replied from my phone and had no way to analyse your code.

Today I tested your code.

I create input list with words "ant", "lion", "pig", "rat", "bat", "tiger", "cat", "dog" and use merge_sort() method. Output of it was nicely sorted list.

The only alternations to your code I did, was adding constructors to Node() class

Node () {
    _word = "";
    _next = null;
}

Node (String word) {
    _word = word;
}

but that was only for me to make it easier create new node.

Can you show how do you test your output list?

EDIT2: I didn't checked your code to separate words from string as you stated it works)

EDIT3: Your merge method have also other problems but this one is the main one.

Think how your merge() method is working when you try to merge 2 list: a = { "ant", "bat", "dog"} b = { "cat", "lion", "rat"}

while loop iterations:

  1. curr = {"ant"} a = { "bat", "dog" } b = { "cat", "lion", "rat"}

  2. curr = {"ant", "bat"} a = { "dog" } b = { "cat", "lion", "rat"}

  3. curr = {"ant" "bat", "cat"} a = { "dog"} b = { "lion", "rat" }

  4. curr = {"ant", "bat", "cat", "dog"} a = { } b = { "lion", "rat" }

  5. a == null, while loop terminated

what happened next? Your method assign only one time next element from list b and exit. Loosing 2nd element ("rat") from b.

I changed your code slightly, getMiddle() method is same as yours:

public Node mergeSort(Node h) {
    if (h._next != null) { // check for recursion if in list left at least 2 elements
        Node middle = getMiddle(h);
        Node sHalf = middle._next; middle._next = null;
        return merge(mergeSort(h), mergeSort(sHalf));
    }
    else { // if only 1 element then no need to sort
        return h;
    }
}

public Node merge(Node a, Node b) {
    Node dummyHead = new Node();
    Node curr = dummyHead; 
    while (a !=null && b!= null) {
        if (a._word.compareTo(b._word) <= 0) {
            curr._next= a; 
            a = a._next; 
        } else { 
            curr._next = b; 
            b = b._next;            
        }
        curr = curr._next;
    }
    // if list a still has some elements, insert them to the end of the curr
    while(a != null) {
        curr._next = a; 
        a = a._next; 
        curr = curr._next;
    }
    // if list b still has some elements, insert them to the end of the curr
    while(b != null) {
        curr._next = b; 
        b = b._next;
        curr = curr._next;
    }
    return dummyHead._next;
}