Got an assignment to create a class that links elements together.
Methods get(), add() and remove() were predefined by the assignment.
I actually managed to write code that creates such linked list,
but with the exception that the instance with value "Yo!" gets overwritten, when
adding new elements to the list and also I can't get the remove method working.
I really can't wrap my head around referencing object this way.
Could you help me correct my code ?
/**
*
* Represents a linked list of elements.
*
* #param <T>
*/
class LinkedElement<T> {
/**
* Adds a new linked element holding the given value at the end of the linked
* elements.
*
* #param newVal the new value.
*/
public void add(T newVal) {
if (head == null) {
head = new Element(newVal);
}
Element next = new Element(newVal);
Element current = head;
if (current != null) {
while (current.getNext() != null) {
current = current.getNext();
}
current.setNext(next);
}
increaseListSize();
}
/**
* Removes the i-th element from the linked elements. If {#code i == 0}, this
* will effectively remove the head element. Thus, this method returns the
* linked element that is the new head element.
*
* #param i index of the element to remove.
* #return the new head element.
*/
public LinkedElement<T> remove(int i) {
if (i < 1 || i > getListSize())
return null;
Element current = head;
if (head != null) {
for (int e = 0; e < i; i++) {
if (current.getNext() == null)
return null;
current = current.getNext();
}
current.setNext(current.getNext().getNext());
decreaseListSize();
}
return null;
}
Your get method is slightly wrong, you need to start current with head and not head.next()
public T get(int i) {
if (i < 0)
return null;
Element current = head;
if (current != null) {
for (int e = 0; e < i; e++) {
if (current.getNext() == null)
return null;
current = current.getNext();
}
return current.getValue();
}
return null;
}
You have a mistake in your get method: Element current = head; is correct.
There are several things that doesn't work in your remove method. This one should be working:
public void remove(int i) {
if (i==0) {
head = head.getNext();
decreaseListSize();
return;
}
if (i < 1 || i > getListSize()) {
return;
}
Element current = head;
if (head != null) {
for (int e = 1; e < i; e++) {
if (current.getNext() == null)
return ;
current = current.getNext();
}
current.setNext(current.getNext().getNext());
decreaseListSize();
}
}
Note that I changed return type to void since your method returned null in any case and return of head is no neccessary. If you want to return the head element you can easily adapt it and return head instead of nothing.
Furthermore, a note that the count - as so often in computer science - starts at 0. To remove the first element you have to write headElement.remove(0);.
Related
I am trying to implement the set method where you pass in the position in a linked list that you want and the value and the set function adds that value into the position specified in the linked list. I have implemented the set function but for some reason The last element disappears in my implementation. I would greatly appreciate any help. Thanks in advance. I would appreciate any expert eyes that will see what I am missing.
/**
* A basic singly linked list implementation.
*/
public class SinglyLinkedList<E> implements Cloneable, Iterable<E>, List<E> {
//---------------- nested Node class ----------------
/**
* Node of a singly linked list, which stores a reference to its
* element and to the subsequent node in the list (or null if this
* is the last node).
*/
private static class Node<E> {
E value;
Node<E> next;
public Node(E e)
{
value = e;
next = null;
}
}
//----------- end of nested Node class -----------
// instance variables of the SinglyLinkedList
private Node<E> head = null; // head node of the list (or null if empty)
private int size = 0; // number of nodes in the list
public SinglyLinkedList() {
} // constructs an initially empty list
// access methods
/**
* Returns the number of elements in the linked list.
*
* #return number of elements in the linked list
*/
public int size() {
return size;
}
/**
* Adds an element to the end of the list.
*
* #param e the new element to add
*/
public void addLast(E e) {
// TODO
}
/**
* Tests whether the linked list is empty.
*
* #return true if the linked list is empty, false otherwise
*/
public boolean isEmpty() {
return size == 0;
}
#Override
public E get(int i) throws IndexOutOfBoundsException {
Node<E> a = head;
if(i<=this.size()) {
int count = 0;
while(count < i) {
count ++;
a = a.next;
}
return a.value;
}
return null;
}
#Override
public E set(int i, E e) throws IndexOutOfBoundsException {
Node<E> current = head;
Node<E> setNode = new Node<E>(e);
if(i==0) {
this.addFirst(e);
}
else if(i==this.size){
this.addLast(e);
}
else {
for(int j=0; current != null && j < (i-1);j++) {
current = current.next;
}
Node<E> temp = current.next;
current.next = setNode;
setNode.next = temp;
}
return setNode.value;
}
// update methods
/**
* Adds an element to the front of the list.
*
* #param e the new element to add
*/
public void addFirst(E e) {
Node<E> first = new Node<>(e);
first.next = this.head;
this.head = first;
this.size++;
}
#SuppressWarnings({"unchecked"})
public boolean equals(Object o) {
// TODO
return false; // if we reach this, everything matched successfully
}
#SuppressWarnings({"unchecked"})
public SinglyLinkedList<E> clone() throws CloneNotSupportedException {
// TODO
return null;
}
/**
* Produces a string representation of the contents of the list.
* This exists for debugging purposes only.
* #return
*/
public String toString() {
for(int i=0;i<this.size();i++) {
System.out.println(this.get(i));
}
return "end of Linked List";
}
public static void main(String[] args) {
SinglyLinkedList <Integer> ll =new SinglyLinkedList <Integer>();
ll.addFirst(5);
ll.addFirst(4);
ll.addFirst(3);
ll.addFirst(2);
ll.set(1,0);
System.out.println(ll);
}
}
Assumptions
addLast method is missing in the code
The error could be there too
Known cause
This code is not incrementing the size inside the set method's for loop.The size is incremented in addLast(possibly) and addFirst and not incremented in other case (final else part)
It is not clear what is planned to do with set method. The way it is implemented now, it behaves more like insert, meaning that it will add new node, bit it does not increase the total count of elements (size).
It the set method is changing the value of the node, which name indicates, then this part is wrong:
else {
for(int j=0; current != null && j < (i-1);j++) {
current = current.next;
}
Node<E> temp = current.next;
current.next = setNode;
setNode.next = temp;
}
It should replace the value instead of adding the new one:
else {
for(int j=0; current != null && j < (i-1);j++) {
current = current.next;
}
current.value=e
}
Also, it looks like the index i is 1-based, while everything else is 0 based. I didn't check the code above, but the concept should be like shown.
package linkedlists;
public class SinglyLinkedList<E> implements Cloneable {
//---------------- nested Node class ----------------
/**
* Node of a singly linked list, which stores a reference to its
* element and to the subsequent node in the list (or null if this
* is the last node).
*/
private static class Node<E> {
/** The element stored at this node */
private E element; // reference to the element stored at this node
/** A reference to the subsequent node in the list */
private Node<E> next; // reference to the subsequent node in the list
/**
* Creates a node with the given element and next node.
*
* #param e the element to be stored
* #param n reference to a node that should follow the new node
*/
public Node(E e, Node<E> n) {
element = e;
next = n;
}
// Accessor methods
/**
* Returns the element stored at the node.
* #return the element stored at the node
*/
public E getElement() { return element; }
/**
* Returns the node that follows this one (or null if no such node).
* #return the following node
*/
public Node<E> getNext() { return next; }
// Modifier methods
/**
* Sets the node's next reference to point to Node n.
* #param n the node that should follow this one
*/
public void setNext(Node<E> n) { next = n; }
} //----------- end of nested Node class -----------
// instance variables of the SinglyLinkedList
/** The head node of the list */
private Node<E> head = null; // head node of the list (or null if empty)
/** The last node of the list */
private Node<E> tail = null; // last node of the list (or null if empty)
/** Number of nodes in the list */
private int size = 0; // number of nodes in the list
/** Constructs an initially empty list. */
public SinglyLinkedList() { } // constructs an initially empty list
// access methods
/**
* Returns the number of elements in the linked list.
* #return number of elements in the linked list
*/
public int size() { return size; }
/**
* Tests whether the linked list is empty.
* #return true if the linked list is empty, false otherwise
*/
public boolean isEmpty() { return size == 0; }
/**
* Returns (but does not remove) the first element of the list
* #return element at the front of the list (or null if empty)
*/
public E first() { // returns (but does not remove) the first element
if (isEmpty()) return null;
return head.getElement();
}
/**
* Returns (but does not remove) the last element of the list.
* #return element at the end of the list (or null if empty)
*/
public E last() { // returns (but does not remove) the last element
if (isEmpty()) return null;
return tail.getElement();
}
// update methods
/**
* Adds an element to the front of the list.
* #param e the new element to add
*/
public void addFirst(E e) { // adds element e to the front of the list
head = new Node<>(e, head); // create and link a new node
if (size == 0)
tail = head; // special case: new node becomes tail also
size++;
}
/**
* Adds an element to the end of the list.
* #param e the new element to add
*/
public void addLast(E e) { // adds element e to the end of the list
Node<E> newest = new Node<>(e, null); // node will eventually be the tail
if (isEmpty())
head = newest; // special case: previously empty list
else
tail.setNext(newest); // new node after existing tail
tail = newest; // new node becomes the tail
size++;
}
/**
* Removes and returns the first element of the list.
* #return the removed element (or null if empty)
*/
public E removeFirst() { // removes and returns the first element
if (isEmpty()) return null; // nothing to remove
E answer = head.getElement();
head = head.getNext(); // will become null if list had only one node
size--;
if (size == 0)
tail = null; // special case as list is now empty
return answer;
}
#SuppressWarnings({"unchecked"})
public boolean equals(Object o) {
if (o == null) return false;
if (getClass() != o.getClass()) return false;
SinglyLinkedList other = (SinglyLinkedList) o; // use nonparameterized type
if (size != other.size) return false;
Node walkA = head; // traverse the primary list
Node walkB = other.head; // traverse the secondary list
while (walkA != null) {
if (!walkA.getElement().equals(walkB.getElement())) return false; //mismatch
walkA = walkA.getNext();
walkB = walkB.getNext();
}
return true; // if we reach this, everything matched successfully
}
#SuppressWarnings({"unchecked"})
public SinglyLinkedList<E> clone() throws CloneNotSupportedException {
// always use inherited Object.clone() to create the initial copy
SinglyLinkedList<E> other = (SinglyLinkedList<E>) super.clone(); // safe cast
if (size > 0) { // we need independent chain of nodes
other.head = new Node<>(head.getElement(), null);
Node<E> walk = head.getNext(); // walk through remainder of original list
Node<E> otherTail = other.head; // remember most recently created node
while (walk != null) { // make a new node storing same element
Node<E> newest = new Node<>(walk.getElement(), null);
otherTail.setNext(newest); // link previous node to this one
otherTail = newest;
walk = walk.getNext();
}
}
return other;
}
public int hashCode() {
int h = 0;
for (Node walk=head; walk != null; walk = walk.getNext()) {
h ^= walk.getElement().hashCode(); // bitwise exclusive-or with element's code
h = (h << 5) | (h >>> 27); // 5-bit cyclic shift of composite code
}
return h;
}
/**
* Produces a string representation of the contents of the list.
* This exists for debugging purposes only.
*/
public String toString() {
StringBuilder sb = new StringBuilder("(");
Node<E> walk = head;
while (walk != null) {
sb.append(walk.getElement());
if (walk != tail)
sb.append(", ");
walk = walk.getNext();
}
sb.append(")");
return sb.toString();
}
public void swapNodes(Node<E> num1, Node<E> num2) {
Node<E> num1Prev = this.head;
Node<E> num2Prev = this.head;
if (num1 == num2 )
return ;
while((num1Prev != null)&&(num1Prev.getNext() != num1)){
num1Prev = num1Prev.getNext();
}
while((num2Prev != null)&&(num2Prev.getNext() != num2)){
num2Prev = num2Prev.getNext();
}
if(num2Prev == num1) {
num1.setNext(num2.getNext());
num2.setNext(num1);
num1Prev.setNext(num2);
}
else if(num1Prev == num2) {
num2.setNext(num1.getNext());
num1.setNext(num2);
num2Prev.setNext(num1);
}
else {
Node<E> tmp = num1.getNext();
num1.setNext(num2.getNext());
num2.setNext(tmp);
num1Prev.setNext(num2);
num2Prev.setNext(num1);
}
}
//main method
public static void main(String[] args)
{
SinglyLinkedList<String> list = new SinglyLinkedList<String>();
list.addFirst("MSP");
list.addLast("ATL");
list.addLast("BOS");
//
list.addFirst("LAX");
System.out.println(list);
//
SinglyLinkedList<String> swap = new SinglyLinkedList<String>();
swap.addFirst("1");
swap.addLast("2");
swap.addLast("3");
swap.addLast("4");
swap.addLast("5");
System.out.println("Original list: " + swap);
swap.swapNodes("2","5");
System.out.println("After Swapping list: " + swap);
}
}
Task: In this exercise, you will add a method swapNodes to SinglyLinkedList class from week 2 lecture examples. This method should swap two nodes node1 and node2 (and not just their contents) given references only to node1 and node2. The new method should check if node1 and node2 are the same nodes, etc. Write the main method to test the swapNodes method. Hint: You may need to traverse the list.
I made this method
public void swapNodes(Node<E> num1, Node<E> num2) {
Node<E> num1Prev = this.head;
Node<E> num2Prev = this.head;
if (num1 == num2 )
return ;
while((num1Prev != null)&&(num1Prev.getNext() != num1)){
num1Prev = num1Prev.getNext();
}
while((num2Prev != null)&&(num2Prev.getNext() != num2)){
num2Prev = num2Prev.getNext();
}
if(num2Prev == num1) {
num1.setNext(num2.getNext());
num2.setNext(num1);
num1Prev.setNext(num2);
}
else if(num1Prev == num2) {
num2.setNext(num1.getNext());
num1.setNext(num2);
num2Prev.setNext(num1);
}
else {
Node<E> tmp = num1.getNext();
num1.setNext(num2.getNext());
num2.setNext(tmp);
num1Prev.setNext(num2);
num2Prev.setNext(num1);
}
}
And then, created an instance to check if it's work, but it is showing me an error on here swap.swapNodes("2", "5");
Does anyone know what is the problem? Thank you
It's throwing error, because in your swapNodes function, you expect two parameters of type Node<E> passed, but you are passing E (String). So you have to change signature of the function or pass Node<E> that you added to your List.
Here's how you could do your swapNodes function with parameters of type E:
public void swapNodes(E element1, E element2) {
if (element1 == element2 || element1 == null || element2 == null || isEmpty()) return;
Node<E> num1 = findNode(element1);
Node<E> num2 = findNode(element2);
if (num1 == null || num2 == null) return;
Node<E> num1Prev = this.head;
Node<E> num2Prev = this.head;
while(num1Prev != null && num1Prev.getNext() != num1){
num1Prev = num1Prev.getNext();
}
while(num2Prev != null && num2Prev.getNext() != num2){
num2Prev = num2Prev.getNext();
}
if (num1Prev.getNext() == null || num2Prev.getNext() == null) return;
if (num2Prev.equals(num1)) {
num1.setNext(num2.getNext());
num2.setNext(num1);
num1Prev.setNext(num2;
} else if (num1Prev.equals(num2)) {
num2.setNext(num1.getNext());
num1.setNext(num2);
num2Prev.setNext(num1);
} else {
Node<E> tmp = num1.getNext();
num1.setNext(num2.getNext());
num2.setNext(tmp);
num1Prev.setNext(num2);
num2Prev.setNext(num1);
}
}
The findNode function could look like this:
public Node<E> findNode(E element) {
if (isEmpty()) return null;
Node<E> node = this.head;
while(node != null) {
if (node.getElement() == element) return node;
}
return null;
}
I am learning data structures current and below is my implementation for linkedlist.I have kept it as simple as possible as my aim here is to understand the logic.
/*
* Singly linked list
*/
package linkedlisttest;
class Node {
int data;
Node next;
public Node(int data)
{
this.data = data;
}
}
class LinkedList {
Node head;
public void add(int data)
{
if (head == null)
{
head = new Node(data);
return;
}
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = new Node(data);
}
public int getSize() {
int i = 0;
Node current = head;
while (current != null) {
i += 1;
current = current.next;
}
return i;
}
public void add(int data, int index)
{
if (head == null && index == 0)
{
head = new Node(data);
return;
} else if (head == null && index != 0) {
return; // invalid position
} else if ( index > getSize() ) {
return;
}
Node current = head;
//iterate through whole list
int pos = -1;
Node previous = null;
Node next = null;
Node newNode = new Node(data);
//find next and previous nodes with relation to position
while (current != null) {
if (pos == index - 1) {
previous = current;
} else if (pos == index + 1) {
next = current;
}
pos++;
current = current.next;
}
previous.next = newNode;
newNode.next = next;
}
public void print()
{
Node current = head;
while (current.next != null) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.print(current.data);
}
}
public class LinkedListTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
LinkedList lt = new LinkedList();
lt.add(3);
lt.add(5);
lt.add(6);
lt.add(4,1);
lt.print();
}
}
The bug happens for lt.add(4,1) and i suspect its an off by one error.
Expected output: 3->4->6
Actual output: 3->5->4
Thanks for the help guys...
Edit
Thanks to #StephenP and #rosemilk for their help.Indeed the code above has a logical bug as it replaces the value at index and not add it.
Here is the new optimized code
/*
* Singly linked list
*/
package linkedlisttest;
class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
}
}
class LinkedList {
Node head;
int size;
/**
*
* #param data element to add to list
* Time Complexity : O(n)
*/
public void add(int data) {
if (head == null) {
head = new Node(data);
size += 1;
return;
}
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = new Node(data);
size += 1;
}
/**
*
* #return size of list
* Time Complexity: O(1)
* This is because we use a class
* variable size to keep track of size of linked list
*/
public int getSize() {
return size;
}
/**
*
* #param data element to insert
* #param index position at which to insert the element (zero based)
* Time Complexity : O(n)
*/
public void add(int data, int index) {
if (index > getSize()) {
return; // invalid position
}
Node current = head; //iterate through whole list
int pos = 0;
Node newNode = new Node(data);
if (index == 0) // special case, since its a single reference change!
{
newNode.next = head;
head = newNode; // this node is now the head
size += 1;
return;
}
while (current.next != null) {
if (pos == index - 1) {
break;
}
pos++;
current = current.next;
}
// These are 2 reference changes, as compared to adding at index 0
newNode.next = current.next; // here we are changing a refernce
current.next = newNode; // changing a reference here as well
size += 1;
}
/**
* Prints the whole linked list
* Time Complexity : O(n)
*/
public void print() {
if(getSize() == 0) { //list is empty
return;
}
Node current = head;
while (current.next != null) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.print(current.data + "\n");
}
}
public class LinkedListTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
LinkedList lt = new LinkedList();
lt.print();
lt.add(3);
lt.add(5);
lt.add(6);
lt.print();
lt.add(4, 1);
lt.print();
lt.add(4, 7);// 7 is an invalid index
lt.add(8, 3);
lt.print();
}
}
Your add (int , int ) function has a logical bug and can be made better. You don't need previous current and next references, and can cleverly manipulate the list using just the reference to current node, handling inseration at index 0 separately. I would write the add function as follows
public void add(int data, int index)
{
if ( index > getSize() ) {
return; // invalid position
}
Node current = head; //iterate through whole list
int pos = 0;
Node newNode = new Node(data);
if (index == 0) // special case, since its a single reference change!
{
newNode.next = head;
head = newNode; // this node is now the head
return;
}
while (current.next != null) {
if (pos == index - 1) {
break;
}
pos++;
current = current.next;
}
// These are 2 reference changes, as compared to adding at index 0
newNode.next = current.next; // here we are changing a refernce
current.next = newNode; // changing a reference here as well
}
Also, your print function gives a NullPointerException when you try to print an empty list. I would write the print function like this,
public void print()
{
Node current = head;
while (current != null) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.println("null"); // this is just to say last node next points to null!
}
Hope this helps :)
Currently, if you print out pos in your loop, the indices are -1, 0, 1 (instead of 0, 1, 2), so it'll never "find" the correct next. Replace int pos = -1; with int pos = 0; and it'll work.
I do agree with #StephenP that the output should (arguably) be 3->4->5->6, but that's a design decision.
On my quest to understand data structures , i started implementing them in java.The time complexity for deleteAll would be O(n + n^2).How can i improve the deleteAll method ?
/*
* Singly linked list
*/
package linkedlisttest;
class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
}
}
class LinkedList {
Node head;
int size;
/**
*
* #param data element to add to list
* Time Complexity : O(n)
*/
public void add(int data) {
if (head == null) {
head = new Node(data);
size += 1;
return;
}
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = new Node(data);
size += 1;
}
/**
*
* #return size of list
* Time Complexity: O(1)
* This is because we use a class
* variable size to keep track of size of linked list
*/
public int getSize() {
return size;
}
/**
*
* #param data element to insert
* #param index position at which to insert the element (zero based)
* Time Complexity : O(n)
*/
public void add(int data, int index) {
if (index > getSize()) {
return; // invalid position
}
Node current = head; //iterate through whole list
int pos = 0;
Node newNode = new Node(data);
if (index == 0) // special case, since its a single reference change!
{
newNode.next = head;
head = newNode; // this node is now the head
size += 1;
return;
}
while (current.next != null) {
if (pos == index - 1) {
break;
}
pos++;
current = current.next;
}
// These are 2 reference changes, as compared to adding at index 0
newNode.next = current.next; // here we are changing a refernce
current.next = newNode; // changing a reference here as well
size += 1;
}
/**
* Find the first occurrence of an element
* #param data element to find
* #return index at which element is found , -1 if not exist
* Time Complexity: O(n)
*/
public int find(int data) {
Node current = head;
int pos = 0;
int index = -1;
if(head == null) { //empty list
return index;
}
while(current != null) {
if (current.data == data) {
index = pos;
break;
}
pos++;
current = current.next;
}
return index;
}
/**
* Delete the first occurrence of data
* #param data element to delete
* Time complexity : O(n)
*/
public void delete(int data) {
Node current = head;
if (head == null) { // list is empty
return;
}
if(head.data == data) { // if we want to delete the head , make next node head
head = head.next;
size -= 1;
return;
}
while(current.next != null) {
if (current.next.data == data) {
current.next = current.next.next;
size -= 1;
return;
}
current = current.next;
}
}
/**
* Delete all occurrences of data
* #param data element to delete
*
*/
public void deleteAll(int data) {
Node current = head;
if (head == null) { // list is empty
return;
}
//while loop to delete consecutive occurances of data
while(head.data == data) { // if we want to delete the head , make next node head
head = head.next;
size -= 1;
}
while(current.next != null) {
//while loop to delete consecutive occurances of data
while (current.next.data == data) {
current.next = current.next.next;
size -= 1;
}
current = current.next;
}
}
public void reverse() {
}
/**
* Prints the whole linked list
* Time Complexity : O(n)
*/
public void print() {
if(head == null) { //list is empty
return;
}
Node current = head;
while (current.next != null) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.print(current.data + "\n");
}
}
public class LinkedListTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
LinkedList lt = new LinkedList();
lt.print();
lt.add(3);
lt.add(5);
lt.add(6);
lt.print();
lt.add(4, 1);
lt.print();
lt.add(4, 7);// 7 is an invalid index
lt.add(8, 3);
lt.add(8, 4);
lt.print();
System.out.println("Position : " + lt.find(8));
lt.delete(5);
lt.print();
lt.deleteAll(8);
lt.print();
System.out.println("Size : " + lt.getSize());
}
}
The time complexity of your implementation is O(n), not O(n + n^2) as you believed.
Although a nested loop is a common sign of O(n^2) it's not always the case.
The important point is the number of iterations.
In your case,
if you take k steps in the nested loop,
that reduces the remaining steps in the outer loop by k.
Overall,
you will still be taking n steps to reach the end.
But your implementation has some bugs,
and can also be improved:
Bug: you assign current = head, but if head.data == data then it will be deleted, and current will still point to it
There is no need for a nested loop. After the special treatment for the head, you can simply follow the nodes and delete the matching items
Like this:
public void deleteAll(int data) {
while (head != null && head.data == data) {
head = head.next;
size -= 1;
}
if (head == null) {
return;
}
Node current = head;
while (current.next != null) {
if (current.next.data == data) {
current.next = current.next.next;
size -= 1;
} else {
current = current.next;
}
}
}
By the way, the special treatment of the head can be a bit annoying.
An elegant alternative is to create a dummy that points to head:
public void deleteAll(int data) {
Node dummy = new Node();
dummy.next = head;
Node current = dummy;
while (current.next != null) {
if (current.next.data == data) {
current.next = current.next.next;
size -= 1;
} else {
current = current.next;
}
}
head = dummy.next;
}
Note that everything in Java is reference except those primitive types. So current still stays at the original head.
It is unnecessary to treat consecutive occurrences as special cases. A simple iterate&judge through the linked list with O(n) - linear complexity will do exactly what you want.
Besides, a nested loop doesn't necessarily means it will definitely cost O(n^2). If you move current every times, it still walk through the linked list in linear time.
Another personal suggestion: If you need to write something like node.next.next when dealing with linked list, it's time to set another reference or to refactor your code.
I'm implementing a linked list for a course online, and there seems to be something wrong with my add function. When I try to add the first element, the Eclipse prints null, and for a second element Eclipse shows an error. (I'm assuming because the first element was never added, so there can't be a second one.)
This is the implementation of my linked list:
package textgen;
import java.util.AbstractList;
public class MyLinkedList<E> extends AbstractList<E> {
LLNode<E> head;
LLNode<E> tail;
int size;
/** Create a new empty LinkedList */
public MyLinkedList() {
size = 0;
head = new LLNode<E>();
tail = new LLNode<E>();
head.next = tail;
tail.prev = head;
}
/**
* Appends an element to the end of the list
* #param element The element to add
*/
public boolean add(E element )
{
add(size, element);
return false;
}
/** Get the element at position index
* #throws IndexOutOfBoundsException if the index is out of bounds. */
public E get(int index) throws IndexOutOfBoundsException
{
if(index >= this.size){
throw new IndexOutOfBoundsException("Your index is out of bounds!");
}
LLNode<E> lGet = head;
for(int i = 0; i < index + 1; i++){
lGet = lGet.next;
}
return lGet.data;
}
public void printList(){
LLNode lTemp = head;
while(lTemp.next != tail){
System.out.println(lTemp.next.data);
lTemp = lTemp.next;
}
}
/**
* Add an element to the list at the specified index
* #param The index where the element should be added
* #param element The element to add
*/
public void add(int index, E element ) throws IndexOutOfBoundsException
{
if(index > this.size){
throw new IndexOutOfBoundsException("Oops! Out of bounds!");
}
else{
LLNode<E> nAdd = new LLNode<E>(element);
LLNode<E> nIt = null;
if(index <= size/2) // if the index is closer to the start from the beginning of the list
{
nIt = head;
for(int i = 0; i < index + 1; i++){
nIt = nIt.next;
}
}
else {
nIt = tail;
for(int i = this.size; i > index; i--){
nIt = nIt.prev;
}
}
nIt.prev.next.prev = nAdd;
nAdd.next = nIt.prev.next;
nIt.prev.next = nAdd;
nAdd.prev = nIt.prev;
size++;
}
}
/** Return the size of the list */
public int size()
{
return size;
}
/** Remove a node at the specified index and return its data element.
* #param index The index of the element to remove
* #return The data element removed
* #throws IndexOutOfBoundsException If index is outside the bounds of the list
*
*/
public E remove(int index)
{
// TODO: Implement this method
size--;
return null;
}
/**
* Set an index position in the list to a new element
* #param index The index of the element to change
* #param element The new element
* #return The element that was replaced
* #throws IndexOutOfBoundsException if the index is out of bounds.
*/
public E set(int index, E element)
{
// TODO: Implement this method
return null;
}
}
class LLNode<E>
{
LLNode<E> prev;
LLNode<E> next;
E data;
public LLNode(){
this.data = null;
this.prev = null;
this.next = null;
}
public LLNode(E e)
{
this.data = e;
this.prev = null;
this.next = null;
}
}
This is the main:
package textgen;
public class fixAdd {
public static void main(String [] Arg){
MyLinkedList<String> ll = new MyLinkedList<String>();
ll.add(0, "happy");
ll.add(1, "gilda");
System.out.println(ll);
}
}
And this is the error printed:
Exception in thread "main" java.lang.NullPointerException
at textgen.MyLinkedList.get(MyLinkedList.java:57)
at java.util.AbstractList$Itr.next(Unknown Source)
at java.util.AbstractCollection.toString(Unknown Source)
at java.lang.String.valueOf(Unknown Source)
at java.io.PrintStream.println(Unknown Source)
at textgen.fixAdd.main(fixAdd.java:11)
I've gone over my add method a number of times, and compared it to other implementations I found online, and everything seems in order. I'm totally confused and would appreciate any help. Thanks!
You should try to implement a simpler add first, instead of doing the size / 2 optimization.
There are several problems with your code:
don't create dummy nodes at initialization, initialize them with null
your loop in get method should be for(int i = 0; i < index; i++)
your aren't updating the size in your add method
EDIT: Changed add method to cover all cases:
public void add(int index, E element)
{
if (index > size)
{
throw new IndexOutOfBoundsException("Oops! Out of bounds!");
}
LLNode<E> node = new LLNode<E>(element);
//Add first and last
if(size == 0)
{
head = tail = node;
}
else
{
//Add first
if(index == 0)
{
node.next = head;
head.prev = node;
head = node;
}
//Add last
else if(index == size)
{
node.prev = tail;
tail.next = node;
tail = node;
}
//Add between
else
{
LLNode<E> current = this.head;
for(int i = 0; i < index; i++)
{
current = current.next;
}
node.next = current;
node.prev = current.prev;
current.prev.next = node;
}
}
size++;
}
Multiple issues with this implementation:
You're setting both head and tail to be empty Nodes when you initialize the list (constructor). You shouldn't do that, it makes your list to be of size two with two null elements.
The exception is thrown from the printing in the main which, in turn, calls your get() method (by calling AbstractList.toString())
In add(E element) you're calling add(size, element); and you pass the size as the index, later on you check if if(index > this.size) which is practically asking if size > size - makes no sense.
In remove() implementation you just decrease the size - this is not good enough, you should also move the tail backwards (and if the list is of size 1 - handle the head as well).
The is the main issue: the logic of insertion in the following lines is incorrect:
nIt.prev.next.prev = nAdd;
nAdd.next = nIt.prev.next;
nIt.prev.next = nAdd;
nAdd.prev = nIt.prev;
Two suggestions:
Don't inherit from AbstractList - after all you're creating your own implementation
Start simple, forget about tail or about trying to insert from the end for better efficiency. Only after you have a simple implementation working - try to improve it.
Try the following code:
public void insert(int index, T item)
{
if(index == size())
{
add(item);
}
else if(index == 0)
{
MyNode<T> temp = new MyNode<T>();
temp.data = item;
temp.next = head;
head.previous = temp;
head = temp;
count++;
}
temp = head;
for(int i = 0; i < index-1; i++)
{
temp = temp.next;
MyNode<T> myNode = new MyNode<T>();
myNode.data = item;
myNode.next = temp.next;
temp.next = myNode;
count++;
}
}