Java, LinkedList of Strings. Insert in alphabetical order - java

I have a simple linked list. The node contains a string (value) and an int (count).
In the linkedlist when I insert I need to insert the new Node in alphabetical order. If there is a node with the same value in the list, then I simply increment the count of the node.
I think I got my method really screwed up.
public void addToList(Node node){
//check if list is empty, if so insert at head
if(count == 0 ){
head = node;
head.setNext(null);
count++;
}
else{
Node temp = head;
for(int i=0; i<count; i++){
//if value is greater, insert after
if(node.getItem().getValue().compareTo(temp.getItem().getValue()) > 0){
node.setNext(temp.getNext());
temp.setNext(node);
}
//if value is equal just increment the counter
else if(node.getItem().getValue().compareTo(temp.getItem().getValue()) == 0){
temp.getItem().setCount(temp.getItem().getCount() + 1);
}
//else insert before
else{
node.setNext(temp);
}
}
}
}
Ok so this is inserting all my strings, but not in alphabetical order. Do you see any error?
public Node findIsertionPoint(Node head, Node node){
if( head == null)
return null;
Node curr = head;
while( curr != null){
if( curr.getValue().compareTo(node.getValue()) == 0)
return curr;
else if( curr.getNext() == null || curr.getNext().getValue().compareTo(node.getValue()) > 0)
return curr;
else
curr = curr.getNext();
}
return null;
}
public void insert(Node node){
Node newNode = node;
Node insertPoint = this.findIsertionPoint(this.head, node);
if( insertPoint == null)
this.head = newNode;
else{
if( insertPoint.getValue().compareTo(node.getValue()) == 0)
insertPoint.getItem().incrementCount();
else{
newNode.setNext(insertPoint.getNext());
insertPoint.setNext(newNode);
}
}
count++;
}

There are a few bugs with your code:
Inserting at/before head actually needs to happen in two different scenarios:
If the list is empty, head becomes node
If the list is not empty, but node is less than the first element, head also becomes node
In either case, node links to whatever head was pointing to before (null or a real node), and head now points to node.
If you're not inserting before head, then you must be inserting after some node. We just need to find where this place is. There are two scenarios:
node.getValue() > temp.getValue(), and node.getValue() < temp.getNext().getValue()
node.getValue() > temp.getValue() and temp.getNext() == null
In either case, node is inserted between temp and temp.getNext()
I suggest encapsulating the after insertion point search in its own function. That is, given the list and a value, it needs to return a node. If that node has the same value as the search value, then simply increment; otherwise, insert after. As a special case, return null to indicate that the insertion point is before head.
In pseudocode, it'll look like this:
FUNCTION findInsertionPoint(Node head, V value) RETURNS Node
// return null if value needs to be inserted before head
IF head == null OR value < head.getValue()
RETURN null;
// otherwise, either return a node with the given value,
// or return a node after which value should be inserted
Node curr = head;
REPEAT
IF curr.value == value
RETURN curr;
ELSEIF curr.getNext() == null OR curr.getNext().getValue() > value
RETURN curr;
ELSE
curr = curr.getNext();
PROCEDURE insert(V value) {
Node newNode = NEW Node(value);
Node insertPoint = findInsertionPoint(this.head, value);
IF insertPoint == null // insert before head
newNode.setNext(this.head);
this.head = newNode;
ELSE
IF insertPoint.getValue() == value
insertPoint.incrementCounter();
ELSE // insert after insertPoint
newNode.setNext(insertPoint.getNext());
insertPoint.setNext(newNode);
Update: I see that you've translated my pseudocode to Java, but for some reason you've omitted codes that deals with inserting before head when head is not empty. Specifically, you have inexplicably omitted this part:
IF head == null OR value < head.getValue()
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
and this part:
IF insertPoint == null
newNode.setNext(this.head); // <<<<<<<<<<<
this.head = newNode;
Both of these are essential; it's what allows "A" to be inserted before the head in [ "B", "C", "D" ].
You need to understand why they're important, and really ask yourself why you chose to remove them. Explain to us, to me, to yourself, why you did that; realize the mistake and learn from it.

For making this, instead of developing from scratch my own sorted list I would implement the Queue interface or extend the already existing PriorityQueue (or any other sorted collection that may apply better). I would define the Node class as an implementation of the Comparable interface or instantiate my queue with a Comparator instance and override the PriorityQueue add method to add the new Node only if another object is not already in the queue, incrementing the counter otherwise. If using java >5.0 for type safety I would use generic to allow just Node objects in the Queue.

I think you want to use one of Multiset implementations from Google Collections.
A Multiset works similar to a Set, but allows for duplicates (and counts them!). Look at TreeMultiset:
A multiset which maintains the
ordering of its elements, according to
either their natural order or an
explicit Comparator.

Without seeing the complete code it's hard to do debugging.
I think the problem is that you set
Node temp = head;
before the loop, but you need to reassign temp while while traversing the list to the current element. In this case you continue to compare against head.

You've taken care of the case when
the list is initially empty. You
should also take care of the special
case when the new node goes at be
beginning of the list. If your list
is B->C->D and you are inserting A.
Its good to set node.next to null(If not already done). So
that if the node gets inserted at the
end, we have null as the next of the
last node.
You need to update the temp to move
to the next node if no insertion is
possible. So you are missing a temp = temp.next;

Since this is homework I'm not going to give you any source code. There is one big issue I see with the code:
Suppose your list already has two distinct items, and you're inserting a new item. In your code, you are checking whether node is greater than head, and if so inserting it immediately after, ignoring the rest of the items in the list.
You code do something like this. There are some missing details which you can fill in yourself.
If list is empty, set head = node, head->next = NULL, and you're done.
Otherwise if node->value < head->value, set node->next = head, head = node.
Otherwise, if node->value == head->value, head->count++;
Otherwise, set tmp = head. While tmp->next->value < node->value, set tmp=tmp->next. (check for nulls!).
If tmp->next == NULL, (i.e. you reached end of list) then set tmp->next = node, and you're done.
Otherwise if tmp->next->value == node->value, (i.e. you reached a node with same value) tmp->next->count++.
Otherwise, if node->next = tmp->next, tmp->next = node, and exit

Related

Reversed Linked List only prints as one node

I was coding on gfg practice to test whether a linked list is a palindrome, and after I reversed the linked list, it showed only one node.
This is the code below (the linked list passed is 1->2->1) :-
class Solution
{
//Function to check whether the list is palindrome.
boolean isPalindrome(Node head)
{
Node temp=head;
Node reversed= reverseList(temp);
Node cur = head;
while(cur!=null)
{
System.out.println(cur.data+" inloop");
cur=cur.next;
}
return true;
}
Node reverseList(Node node)
{
Node prev = null;
Node current = node;
Node next = null;
while (current != null) {
next = current.next;
current.next = prev;
prev = current;
current = next;
}
node = prev;
return node;
}
}
The output of this code is ( the linked list passed is 1->2->1) : -
1 inloop
But if I comment out the Node reversed = reverseList(temp); line, the output is as expected: -
1 inloop
2 inloop
1 inloop
Where is my mistake?
When the list is reversed, the previous head of the list has become the tail. Concretely, after that reversal, head references the tail of the now reversed list. A tail has (by definition) a next member that is null, and so your while loop will only iterate once.
Realise that when you reverse the list, you don't have the original order anymore: all next members have lost their original value and now (only) have a reference that reflects the reversed order.
You have a few options to do it right:
Have your reverse method create a new list, so that caller will then have both the original and the reversed list. This will allow you to iterate both lists in tandem and compare the node values as intended
Create an array from the list. Now check whether the array is a palindrome.
The above options need O(n) extra space. You can solve this without O(n)
auxiliary space: only reverse half of the list. Here you don't need to create new nodes, but by only reversing half of the list, you can traverse the reversed part and the non-reversed part in tandem and make value comparisons

Questions about traversing, inserting, and deleting nodes

Just started learning nodes and I have a few questions. Let's say I have a node class that looks like this:
private E number;
private Node next;
/**
* Constructor
*/
Node(E e){
number = e;
next = null;
}
and I have a series of linked nodes with the starting node named first like so:
first->(1)->(2)->(3)->(5)->(6)
Assuming the list is not empty, to traverse the list, I would do something like so:
Node curr = first;
while(curr != null){
System.out.print(curr);
curr = curr.next;
}
I understand that you cannot traverse a link list like this backwards, so does this mean that whenever I call curr.next the previous elements are just lost?
I was also wondering if first, my original list, will ever be affected by a temporary node list like curr? For example, if I was to insert or delete a node on the list with codes similar to these:
Insert:
Node curr = first;
Node newNode = new Node(4);
while(curr != null){
if(curr.number == 3){
newNode.next = curr.next;
curr.next = newNode;
}
curr = curr.next;
}
Delete:
Node curr = first;
Node parent = first;
while(curr != null){
if(curr.number == 3){
parent.next = curr.next;
}
parent = curr;
curr = curr.next;
}
Would the above code modify first or would I have to set first = curr; after the insertion or deletion for the changes take place? If they do modify first, how come curr = curr.next; doesn't modify first? What if I wanted to return the deleted node? Would I just do something like curr.next = null; and then return curr;?
does this mean that whenever I call curr.next the previous elements are just lost?
As you traversed down the list, you never modified first and you never modified the next link of any Node. So no node is ever really lost. Previously visited nodes just become a bit less convenient to access because you don't have a "handle" on them, but you could always find them again by starting back at the head of the list (first) and following the next links.
I was also wondering if first, my original list, will ever be affected by a temporary node list like curr?
The variable first will never be affected by anything other than an assignment statement having first on the left-hand side of the =. On the other hand, the list to which first refers might change quite dramatically or be completely unaffected, depending on what you are using curr for, but first itself can only be affected by assigning a new value to first.
Would the above code modify first or would I have to set first = curr;
The insert and delete code, as given, never modifies first. But it should, if it is going to be able to handle inserting a new node before the current first node, or deleting the current first node. Maybe not with first = curr specifically, but first will definitely need to be updated to reflect a new head of the list. I leave that, as the textbooks say, "as a exercise for the reader".

Iterative method: Delete Linked List node using only one reference variable

SOLVED: CODE REFLECTS SOLUTION
I have been working on a custom Linked List and need to delete a node with a given key only using one reference to the list.
I have managed to do this with two references (Node previous, Node current), but am a bit confused as how to approach this using only one.
My code works for cases except deleting the head node, and nodes that are not in the list ( I get nullpointer exception) when trying to delete '88' or a node that doesn't exist '100'.
Here is my test data from the list:
0) 88
1) 2
2) 1
3) 8
4) 11
// Iterative method to delete a node with a given integer key
// Only uses ONE reference variable to traverse the list.
private void delete (int key, Node x) {
// Check if list is empty
if (isEmpty()) {
System.out.println("Cannot delete; the list is empty.");
}
// Check if we're deleting the root node
if (key == head.getKey()) {
// Now the first in the list is where head was pointing
removeFromHead();
}
// General case: while the next node exists, check its key
for (x = head; x.getNext() != null; x = x.getNext()) {
// If the next key is what we are looking for, we need to remove it
if (key == x.getNext().getKey()) {
// x skips over the node to be deleted.
x.putNext(x.getNext().getNext());
}
} // End for
}
Try this:
public Value delete (int key) {
//check if list is empty
if (head == null)
//the key does not exist. return null to let the method caller know
return null;
//check if we're deleting the root node
if (key == head.getKey()) {
//set the value of what we're deleting
Value val = head.getNode().getValue();
//now the first in the list is where head was pointing
head = head.getNext();
//there is now one less item in your list. update the size
total--;
//return what we're deleting
return val;
}
// general case: while the next node exists, check its key
for (Node x = head; x.getNext() != null; x = x.getNext()) {
//check if the next node's key matches
if (key == x.getNext().getKey()) {
//set value of what we're deleting
Value val = x.getNext().getNode().getValue();
//x now points to where the node we are deleting points
x.setNext(x.getNext().getNext());
//there is now one less item in the list. update the size
total--;
//return what we're deleting
return val;
}
}
//if we didn't find the key above, it doesn't exist. return null to let the
// method caller know.
return null;
}
This is for LinkedList<Value>. General idea is there, but you'll have to tailor this to how you've set everything up.
Well, you have problems with the head and the tail of the list because you are not checking them properly.
You should compare the key the the head before entering the while loop. The first value that the loop checks is of the second node (temp.getNext().getKey()), so you never actually test the head.
Moreover, after the loop has ended and you check to see if the key is in the last node, you are also calling getNext(). If it was indeed the last node, then the next node is null and it has no getKey() method.

How can I implement append and deleteNode methods for a LinkedList implementation in Java?

I am improving my data structures skills. I am trying to implement a LinkedList from scratch.
This is what I have done so far:
class Node {
Node next = null; //reference to null
Object data; //value of the node
public Node(Object d) {
data = d; //constructor that assigns the node's value to the node
}
void append(Object d) {
Node end = new Node(d); //create the new node
Node n = this;
while (n.next != null) { //keep moving the reference until we reach null which is the reference of the last node
n = n.next;
}
n.next = end; //Assign the null reference to the node end which is the node to be added
}
Node deleteNode(Node head, Object d){
Node n = head; //Call the pointer to the head n;
if (n.data == d) { //Check if the data of the current node is same as node to be deleted
return head.next; //Here I got lost, I don't know why we're returning head.next
}
while (n.next != null) { //as long as we have reached the last node
if (n.next.data == d) {
n.next = n.next.next; //I don't get this
return head; //Why are we returning head
}
n = n.next;
}
return head;
}
}
The problem is I don't understand the deleteNode method. I have found it in the book Cracking the Code interview. Could someone please clarify for me what is actually happening? The whole reference thing is getting me confused.
The deleteNode method seems to return the linked list. Here's what it does:
If the first element of the linked list is the item that we seek (its data matches d), then we just return the list starting from the second element (head.next). There's nothing linking back to that first element, so the first element is gone. Just what we wanted.
Look through all nodes. They do this by looking at n.next. If its data matches d, then this node should be removed. So then let the list skip that element: Set n.next to be n.next.next (which is the element after it) rather than the element that matched d.
Normally these kinds of methods tend to return the element that was removed. Instead, this seems to return the list itself, which is represented by its head. That's why the method keeps returning head.

I can't add a node into an empty list

I stumbled upon a weird problem with adding two linked lists into a third one in Java, the first linked list "myList1", the second linked list "myList2" and the third one "myList3".
The combining method is supposed to to add the first LinkedList "myList1" then the second "myList2" into the third LinkedList "myList3", but I faced a problem with adding them to third list while it's empty, but if the third list has at least one element every thing goes smoothly.
The code:
Node current = myList1.head;
while (current != null) {
Node newcurrent = myList3.head;
int h1 = current.getData();
Node newNode = new Node(h1);
if (newcurrent == null)
//the problem is with this code
newcurrent = newNode;
else {
if (newcurrent.getLink() == null) {
newNode.setLink(newcurrent.getLink());
newcurrent.setLink(newNode);
} else {
Node current11 = newcurrent;
while (current11.getLink() != null) {
current11 = current11.getLink();
}
current11.setLink(newNode);
}
}
current = current.getLink();
}
The node is not added to the third LinkedList if the third list is empty, and I tried many other codes but it didn't work either, but if I entered at least one element to the third LinkedList the list is added normally.
other codes I tried :
newcurrent.setLink(newNode);
and
newNode = newcurrent;
newcurrent = newNode;
and
newNode.setLink(newcurrent);
newcurrent.setLink(newNode);
and
newNode.link = newcurrent;
newcurrent.link = newNode;
You're overcomplicating this a bit, I think. The links within the list are already there. You only need to link myList3.tail to myList1.head, no need to loop through adding each node independently. Since you don't look like you are storing a tail, you'll need to iterate to the end of myList3 to find it.
if (myList3.head == null)
myList3.head = myList1.head;
else {
Node list3iter = myList3.head;
while (list3iter.getLink() != null) {
list3iter = list3iter.getLink();
}
list3iter.setLink(myList1.head);
}
}
One further note, I find it painful to try to keep track of names like current, current11, newcurrent, etc. They all mean just about the same thing to my brain. If you're like me, a bit more descriptive naming might help you keep track of what your variables are meant to be doing here.
Node newcurrent = myList3.head;
....
if (newcurrent == null)
//the problem is with this code
newcurrent = newNode;
Not sure why you have two lists, but the last line above is just assigning to local variable. Should it be as follows instead?
myList3.head = newNode

Categories

Resources