Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singly linked list removing element using head and tail reference

I have to implement a singly linked list for my project and I'm having trouble getting the remove method to work. I have searched on here for answers but I can't find any that incorporate the tail reference. My project needs to have a head and tail reference in the list and needs to be updated wherever necessary. This is my class and the remove method:

public class BasicLinkedList<T> implements Iterable<T> {
public int size;

protected class Node {
    protected T data;
    protected Node next;

    protected Node(T data) {
        this.data = data;
        next = null;
    }
}

protected Node head;
protected Node tail;

public BasicLinkedList() {
    head = tail = null;
}

public BasicLinkedList<T> addToEnd(T data) {
    Node n = new Node(data);
    Node curr = head;
    //Check to see if the list is empty
    if (head == null) {
        head = n;
        tail = head;
    } else {
        while (curr.next != null) {
            curr = curr.next;
        }
        curr.next = n;
        tail = n;

    }
    size++;
    return this;
}

public BasicLinkedList<T> addToFront(T data) {
    Node n = new Node(data);
    if(head == null){
        head = n;
        tail = n;
    }
    n.next = head;
    head = n;
    size++;
    return this;
}

public T getFirst() {
    if (head == null) {
        return null;
    }
    return head.data;
}

public T getLast() {
    if(tail == null){
        return null;
    }
    return tail.data;
}

public int getSize() {
    return size;
}

public T retrieveFirstElement() {
    // Check to see if the list is empty
    if (head == null) {
        return null;
    }
    Node firstElement = head;
    Node curr = head.next;
    head = curr;
    size--;
    return firstElement.data;

}

public T retrieveLastElement() {
    Node curr = head;
    Node prev = head;
    // Check to see if the list is empty
    if (head == null) {
        return null;
    } else {
        // If there's only one element in the list
        if (head.next == null) {
            curr = head;
            head = null;
        } else {
            while (curr.next != null) {
                prev = curr;
                curr = curr.next;
            }

            tail = prev;
            tail.next = null;
        }
    }
    size--;
    return curr.data;
}

public void remove(T targetData, Comparator<T> comparator) {
    Node prev = null, curr = head;
    while (curr != null) {
        if (comparator.compare(curr.data, targetData) == 0) {
            //Check to see if we need to remove the very first element
            if (curr == head) {
                head = head.next;
                curr = head;
            } 
            //Check to see if we need to remove the last element, in which case update the tail
            else if(curr == tail){
                curr = null;
                tail = prev;
                prev.next = null;
            }
            //If anywhere else in the list
            else {
                prev.next = curr.next;
                curr = curr.next;
            }
            size--;
        } else {
            prev = curr;
            curr = curr.next;
        }
    }
}

public Iterator<T> iterator() {
    return new Iterator<T>() {

        Node current = head;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public T next() {
            if(hasNext()){
                T data = current.data;
                current = current.next;
                return data;
            }
            return null;
        }

        @Override
        public void remove(){
            throw new UnsupportedOperationException("Remove not implemented.");
        }

    };
}

}

I have went through many iterations of this method and each time I either lose the head reference, the tail reference or I don't remove the element and I am stumped trying to figure it out. For reference here is the test I'm running on it. I don't even fail the test, it just says failure trace.

public void testRemove(){
            BasicLinkedList<String> basicList = new BasicLinkedList<String>();
    basicList.addToEnd("Blue");
    basicList.addToEnd("Red");
    basicList.addToEnd("Magenta");
    //Blue -> Red -> Magenta -> null
    basicList.remove("Red", String.CASE_INSENSITIVE_ORDER);
    //Blue -> Magenta -> null
    assertTrue(basicList.getFirst().equals("Blue"));
    //getLast() returns the tail node
    assertTrue(basicList.getLast().equals("Magenta"));
    }

EDIT: Forgot to mention that the remove method should be removing all instances of the target data from the list.

like image 501
samz_manu Avatar asked Oct 12 '15 23:10

samz_manu


1 Answers

I see only 1 bug. If your list is initially empty the following method will cause a loop where you have one node whose next refers to itself:

public BasicLinkedList<T> addToFront(T data) {
    Node n = new Node(data);
    // The list was empty so this if is true
    if(head == null){
        head = n;
        tail = n;
    }
    n.next = head;
    // now head == n and n.next == head == n so you've got a circle
    head = n;
    size++;
    return this;
}

You can fix this like so:

public BasicLinkedList<T> addToFront(T data) {
    Node n = new Node(data);
    if(head == null){
        tail = n;
    }
    n.next = head;
    head = n;
    size++;
    return this;
}
like image 104
Oliver Dain Avatar answered Oct 13 '22 00:10

Oliver Dain