I have a method that adds elements into a doubly linked list, and swaps elements into ascending order if the first element is greater than the second element. If I input 23, 24, 16, it skips swapping 23 and 24 because those are good, and then it swaps 23 and 16 fine. Then when it goes back into the for loop I get an error saying that first.getNext() is null. I can see that next is getting lost in the swapping, and I tried wrestling with some fixes but have not been able to get it right.
#Override
public void add(T element) throws NullPointerException{
DoubleLinearNode<T> newNode = new DoubleLinearNode<T>(element); // Calls constructor to create a new node with data set to parameter.
if(element == null)
throw new NullPointerException("Cannot add a null element"); // Throws exception when parameter is null.
if(isEmpty()) { // Add a single node.
head = newNode;
tail = newNode;
count++;
modChange++;
}
else { // Add node to the back.
tail.setNext(newNode);
newNode.setPrevious(tail);
tail = newNode;
count++;
modChange++;
}
//}
DoubleLinearNode<T> hold = null;
DoubleLinearNode<T> first = null; // First element in list.
DoubleLinearNode<T> second = null; // Second element in list.
if(size() > 1) { // If there are 2 elements to compare then swap.
for(first = head; first.getNext() != null; first = first.getNext()) {
for(second = first.getNext(); second != null; second = second.getNext()) {
if(first.getData().compareTo(second.getData()) > 0) {
hold = first;
first = second;
second = hold;
System.out.println(first.getData());
System.out.println(second.getData());
}
}
}
}
}
I tried using this code to swap nodes but then I get an error saying that hold is null.
hold.setData(first.getData());
first.setData(second.getData());
second.setData(hold.getData());
Something like this might work. As mentioned in the comments, you do not need to swap, just find the insertion point and relink from both sides:
#Override
public void add(T element) {
DoubleLinearNode<T> newNode = new DoubleLinearNode<>(element);
if (element == null)
throw new UnsupportedOperationException("Cannot add a null element");
//Add first node
if (isEmpty()) {
head = newNode;
tail = newNode;
} else {
//I would suggest to extract the following logic to
// a dedicated methods to find the insertion point and another to do the insertion
//The head needs to be replaced
if (head.getData().compareTo(newNode.getData()) > 0) {
newNode.setNext(head);
head.setPrevious(newNode);
head = newNode;
} else {
DoubleLinearNode<T> node = head.getNext();
while (node != null) {
if (node.getData().compareTo(newNode.getData()) > 0) {
DoubleLinearNode<T> previous = node.getPrevious();
newNode.setNext(node);
node.setPrevious(newNode);
previous.setNext(newNode);
newNode.setPrevious(previous);
break;
}
node = node.getNext();
}
//If no insertion point found add it to the tail
if (node == null) {
tail.setNext(newNode);
newNode.setPrevious(tail);
tail = newNode;
}
}
}
count++;
modChange++;
}
Few more notes, throws NullPointerException it's not needed at all. It is a runtime exception. It's also incorrectly used, you should throw UnsupportedOperationException or IllegalArgumentException in such cases. But is it really needed to throw an exception? It's a bad practice to control the program flow by exceptions. If I was implementing this, I would maybe just do nothing in case of null element or change the method add to return a boolean indicating whether the addition was successful or not and return false in case of null element.
You used for loops where while loop is a better fit, it works with for loop as well, but it's less readable.
Related
I am a beginner, below is the best that I could come up so far. The objective of the method .Split() is that I am supposed to rearrange (rearrange only, no adding or removing nodes nor switching data of the nodes) the elements of the LinkedIntList <- This is not that standard LinkedList class, its a modified class that I have to use, there is no Java method that I can use for this problem unfortunately.
For example, if a linkedlist is [1, 5, -9, 7, -5, 78], the result should be [-9,-5, 1, 5, 7, 78]. The order of the negative values do not matter.
There is only one private field, which is ListNode front;
I am trying to use current.data < 0 to pick out the negative values and put them at the front of the list, iterating from the front, and start over. Making sure that it selects all the negative values from the list. But my output keeps getting stuck in a endless loop, unable to break. I am not sure what to do here.
Here is my best attempt at this problem.
public void Split()
{
ListNode current = front;
ListNode head = front;
while (current.next != null)
{
if (current.data < 0)
{
current.next = front; // set the pointer of current's node
front = current; // update the head with a negative value
current = current.next; // iterate from the front
}
else if (current.data >= 0)
{
current = current.next; // if positive, skip the process and keep iterating
}
else
{
break; // break if null or anything else
}
if (current.next == null || current == null)
{
break; // another attempt to break since it keeps getting stuck
}
}
}
hi llda: in order to move negative numbers ahead, I suggest get it done via two adjacent pointers. See the demo idea picture attached.
The code shows below:
public void Split(){
ListNode current = front.next;
ListNode prior = front;
ListNode head = front;
do {
if(current.data < 0 && current.next != null){
prior.next = current.next;
head = current;
head.next = front;
current = prior.next;
}
current = current.next;
prior = prior.next;
} while(current.next != null)
}
You have to update all related references to keep the linked list intact.
As a homemade implementation, I would suggest a full sort (a kind of bubblesort) this way:
...
public void sort() {
ListNode item = front;
while (item.next != null) {
if (needSwapping(item, item.next)) {
item = swapItems(item, item.next);
if (item.prev != null) {
// one step back to check next against prev in next loop
item = item.prev;
}
} else {
item = item.next;
}
}
front = firstNode(item);
}
private ListNode swapItems(ListNode item, ListNode next) {
ListNode outerPrev = item.prev;
ListNode outerNext = next.next;
// change the refs of outer elements
if (outerPrev != null) {
outerPrev.next = next;
}
if (outerNext != null) {
outerNext.prev = item;
}
// change refs to outer elements
item.next = next.next;
next.prev = item.prev;
// change inner refs
item.prev = next;
next.next = item;
return next;
}
private boolean needSwapping(ListNode item, ListNode next) {
return item.data > next.data;
// here is the point for optimizations of sort accuracy eg. negatives order doesn't matter
// return item.data >0 && item.data > next.data;
}
...
But much more modern Java Style would be to implement the Comparable interface or to provide a Comparator and make use of the Collections.sort method as described here or at stackoverflow.
I'm trying to write a method that takes to elements and delete all element between them and if one of the two elements doesn't exist no element will be removed so I wrote a method but it doesn't work it's stuck in a loop , see the code :
note : 1- I can't call any method .
2- I can't use use any auxiliary data structure.
public void removeBetween(T e1, T e2) {
if (head == null) {
return;
}
Node<T> tmp1 = current;
current = head;
while (current != null) {
if (current.data.equals(e1)) {
current = head;
while (current != null) {
if (current.data.equals(e2)) {
current = head;
while (current != null) {
if (current.data.equals(e1)) {
while (current.data != e2) {
if (current == head) {
head = head.next;
} else {
Node<T> tmp = head;
while (tmp.next != current) {
tmp = tmp.next;
tmp.next = current.next;
}
if (current.next == null)
current = head;
else
current = current.next;
}
current = current.next;
}
}
current = current.next;
}
}
current = current.next;
}
}
current = current.next;
}
current = tmp1;
}
You would be better off starting from scratch. You do not need nested loops at all, let alone five levels of nesting for a task that is, essentially, linear.
Solving the task at hand has a simple plan:
First, you need to find out if the first element is present. Do it with a single while loop. If you reach the end, exit.
Store the reference to the first element in a separate variable first, and continue to finding the second element
Again, use a separate while loop. If you reach the end of the list before finding the second element, exit.
If you do find the second element, assign it to first.next.
That's it, you are done. You could do it with two consecutive while loops, or even with a single while loop and a couple of boolean variables. No nesting is necessary.
Made a code, that actually does pretty much what dasblinkenlight already stated.
Node<T> current = head; // set the current to head
do {
current = current.next; // go to next
if (current == head) { // if it's head, we have reached the end -> return
return;
}
} while (!current.data.equals(e1)); // if current.data is wrong, continue loop
Node<T> node1 = current; // match found
do { // do the same again, starting from current, to find the second node
current = current.next;
if (current == head) {
return;
}
} while (!current.data.equals(e2));
Node<T> node2 = current;
Then you can remove the elements between node1 and node2.
I am trying to store the strings in a LinkedList. I am not allowed to pre-sort, but find the place and pass the string to the linked list. When i pass the strings through text file, the string do not go through the last else condition.
My input file has
joe
appy
appz
zebra
cat
When it reaches appz, it doesn't go through any statement. It is supposed to insert the last else condition and print 5, but doesn't do that.
/**
* Gets the string and arranges them in order
* #param newString
*/
public void store(String newString) {
LinkedListNode current = head;
System.out.println(newString);
// if no element in the list
if (current==null){
System.out.println("1");
makeNode(newString);
}
// if only 1 elements in the list
else if(current.getNext()==null ){
System.out.println("2");
if(newString.compareTo(current.getName())<0){
insertBefore(current.getName(),newString);
} else{
insertAfter(current.getName(),newString);
}
}
// if the element is smaller than the head in the list
else if(newString.compareTo(current.getName()) < 0){
System.out.println("3");
LinkedListNode temp = makeNode(newString);
temp.setNext(current);
head=temp;
}
// if the element is greater than the tail in the list
else if(newString.compareTo(findTail().getName()) > 0){
System.out.println("4");
insertAfter(findTail().getName(),newString);
}
// for more than two elements in the list
else{
System.out.println("5");
while(!(newString.compareTo(current.getName())>0 && newString.compareTo(current.getNext().getName())<0 ) && current.getNext()!=null){
current=current.getNext();
}
if(newString.compareTo(current.getName())<0){
insertBefore(current.getName(),newString);
}
else{
insertAfter(current.getName(),newString);
}
}
} // end of store()
You have some issue with the insertBefore. I updated it.
public void insertBefore(String later, String name){
if(head==null){
head = new LinkedListNode(name,null);
}
else if(head.getName()==later){
LinkedListNode newNode = makeNode(name);
newNode.setNext(head);
head=newNode;
}
else{
LinkedListNode current = head;
while(current.getNext().getName()!=later){
current=current.getNext();
}
LinkedListNode newNode = makeNode(name); // create the new node
newNode.setNext(current.getNext());
current.setNext(newNode);
}
} // end of insertBefore()
When you are traversing, you are not supposed to change the head reference. To traverse, simply do this:
Node tmp = head;
while(tmp != null) tmp = tmp.next;
This will become very handy to figure out where to insert new nodes or where to go to remove existing nodes.
Your class should also have methods to addFirst, addLast, insertBefore, insertAfter. In the code below, Object is whatever data type your need (in your case, String)
public void addLast(Object item)
{
if(head == null)
{
addFirst(item);
}
else
{
Node<Object> tmp = head;
while(tmp.next != null)
{
tmp = tmp.next;
}
tmp.next = new Node<Object>(item, null);
}
}
public void addFirst(Object item)
{
head = new Node<Object>(item, head);
}
public void insertAfter(Object key, Object item)
{
Node<Object> tmp = head;
while(tmp != null && !tmp.data.equals(key))
{
tmp = tmp.next;
}
if(tmp != null)
{
tmp.next = new Node<Object>(item, tmp.next);
}
}
public void insertBefore(Object key, Object item)
{
if(head == null)
{
return null;
}
if(head.data.equals(key))
{
addFirst(item);
return;
}
Node<Object> previous = null;
Node<Object> current = head;
while(current != null && !current.data.equals(key))
{
previous = current;
current = current.next;
}
//insert between current and previous
if(current != null)
{
previous.next = new Node<Object>(item, current);
}
}
In my opinion, you should not have a nested if/else construct to figure out where to insert. That should be up to the method you are invoking.
Secondly, the conditions you are using to control the flow of execution in your code are disparate. Your IF condition is if the list is empty. If it is, create a new node and add it to the list. That condition is followed by checking for a list containing only one node. After that, you are not checking for the length of the list. The expected logic is that you should be checking for a list size greater than one; and yet this is your fall through case (the last else). If you are going to be doing that kind of check outside the insert methods, then do something like this (stubbing your code):
if (current==null){
System.out.println("1");
makeNode(newString);
}
// if only 1 elements in the list
else if(current.getNext()==null ){
System.out.println("2");
if(newString.compareTo(current.getName())<0){
insertBefore(current.getName(),newString);
} else{
insertAfter(current.getName(),newString);
}
}
// if the list has more than one element
else
{
// figure out where it goes (before or after) and insert
}
If you notice, the else/if and else blocks do basically the same thing. Therefore, your code can (and should) be simplified as follows:
if (current==null){
System.out.println("1");
makeNode(newString);
}
// if the list has one or more elements
else
{
// figure out where it goes (before or after) and insert
}
public void deleteItem(int target)
{
int index = 0;
CarNode item = head;
while(item != null)
{
CarNode next = (item.node).node;
CarNode previous = item;
if (index == target)
{
previous.setNode(next);
}
item = element.node
index++;
}
}
Yeah, I don't know if I understood well, but I got told that you can use reference and don't have to directly refer to an object of the linked list in order to perform changes on the linked list.
A node contains the Car object and the node of another element of the LinkedList, right, so the reference is basically a clone that points to the same object as the original, but how come that the original is ignored and the reference takes precedence over the original when we modify the node of the reference? Sorry, it didn't make any sense to me and I've been scratching my head for hours over this.
The code should look like this:
public void deleteItem(int target)
{
int index = 0;
CarNode item = head;
CarNode prev = null;
while(item != null)
{
if (index == target) {
if (prev == null) {
head = item.getNode();
return; // We've removed the target.
} else {
prev.setNode(item.getNode());
return; // We've removed the target.
}
}
prev = item;
item = item.getNode();
index++;
}
}
So let's break this down:
int index = 0;
CarNode item = head;
CarNode prev = null;
We need two variables: one to store the element we're looking at, the other to store the previous element (which we will use to reconnect the list once we remove an element). To start, our current is the head, and our previous doesn't exist. index will let us know when we reach the target.
while(item != null)
We want to iterate until we've hit the end of the list, marked by a null node.
if (index == target) {
if (prev == null) {
head = item.getNode();
return; // We've removed the target.
} else {
prev.setNode(item.getNode());
return; // We've removed the target.
}
}
If we've found the target, we remove it. If previous is null, then the target was the head, so we move the head to the 2nd element. Otherwise, we make the previous node's reference to be the current node's reference, which cuts the current node out of the list. Once we remove the target, we are done, so we return.
prev = item;
item = item.getNode();
index++;
Update the previous and current nodes. Both are moved forward one node. Index is incremented.
How about an illustrated example:
Take a list of size 3. It looks like this:
We now call list.deleteItem(1); This instantiates a prev and a next node. next points to the first node, and prev is null.
Our target is 1, so we move to the next node. Now prev points to what next used to point to, and next points to the 2nd object in the list (the one we want to remove).
We remove it by setting the prev node's reference to be the next node's reference.
When we return from the method, java garbage collection does its job, and we're left with:
Tada! Node removed from the list!
public void deleteItem(int target)
{
int index = 0;
CarNode item = head;
CarNode next = null;
CarNode previous = null;
// stop when the linked-list ends
while(item != null)
{
// the tail has no next node
if (item.node != null)
next = item.node.node;
else
next = null;
// if targetIndex exist, remove it
// "logically" from the linekd-list
if (index == target)
{
previous.setNode(next);
break;
}
// today is tomorrow's yesterday
previous = item;
item = item.node;
index++;
}
}
I previously needed help debugging my deleteNode method. It works now (updated version posted below) but I want it to provide for the case when it has to delete the head node. At the moment, it returns a NullPointerException where I've inserted * in deleteNode. I don't know how any of my variables can be null at that point, seeing as my while loop requires both position and head to not be null in the first place.
public class LinkedList
{
private class Node
{
int item;
Node link;
#SuppressWarnings("unused")
public Node()
{
item = Integer.MIN_VALUE;
link = null;
}
public Node(int x, Node p)
{
item = x;
link = p;
}
}
private Node head;
public LinkedList()
{
head = null;
}
public boolean deleteNode (int target)
{
Node position = head;
boolean isGone = false;
while(position != null && head != null)
{
if(position.link == head && position.link.item == target)
{
head = head.link;
isGone = true;
return isGone;
}
*** else if(position.link.item == target && position.link != head)
{
position.link = position.link.link;
isGone = true;
return isGone;
}
position = position.link;
}
return isGone;
}
public void printList()
{
System.out.println("Your list is: ");
Node position = head;
while(position != null)
{
System.out.println(position.item + " ");
position = position.link;
}
System.out.println();
}
}
LinkedList.deleteNode(int) never modifies any node's link, so it doesn't remove any element from the list.
Suppose that nodeA.link == nodeB, and nodeB.item == target. Then you need to set nodeA.link = nodeB.link, so that nothing is pointing to nodeB anymore.
Here is a list of the problems I see:
The enumerator you actually want to use, position, is never updated. The enumerator that is updated, counter is not needed.
You are never actually removing the node. In order to remove the node, you need to set the previous node's link to the matching node's link, thus removing it out of the chain.
You aren't dealing with special cases. What happens if the list passed is null? What happens if the matching node is the first node? The last node?
You should be returning the head of the linked list from the calling function. This is required for when removing the head node of the linked list.
Since this is a homework question, try to work it out for yourself but hopefully those points will help.
Look at your deleteNode() while loop code.
while(position != null && counter != null)
{
itemAtPosition = position.item;
if(itemAtPosition == target)
{
position = position.link;
isGone = true;
}
counter = counter.link;
}
you update counter, but never refer to it. position never changes, so the
if(itemAtPosition == target)
line never returns true. I suspect somewhere you need to check on counter.item!
First, you didn't write code for the case where the target item is located at the beginning, in which the field head should be updated accordingly. Second, the compared item is never updated during traversing the list.