If the title is not confusing enough maybe this will be. I have a linked list which contains people who have first and last names as well as a few other variables. The list must be sorted by last name first, then by first name. So far I insert the people into the list in alphabetical order by last name. I then try to traverse the list, if two last names are the same, I check the first names and swap. But I have a bug.
Inserted alphabetically into the list by last name, last,first
Acy,Mary
Acy,Clayton
Bob,Lonnie
Toni,Lonnie
After my so call "sort" of first names
Acy,Mary
Bob,Lonnie
Acy,Clayton
Toni,Lonnie
You can see it is sorted by last name. I am trying to sort each same last name by first name. And this is what I get as output from
public void sortFirstNames(){
System.out.println("here");
PeopleNode previous = null;
PeopleNode current = head;
PeopleNode temp;
if(head == null || head.next == null){
return;
}
while(current != null && current.next != null && current.lastName.compareTo(current.next.lastName) == 0){ //traverse the list
if((current.firstName).compareTo(current.next.firstName) > 0){ //see which first name goes first
temp = current.next.next;
current.next.next = temp.next;
temp.next = current.next;
current.next = temp;
current = temp.next;
}
current = current.next;
}
}
It doesn't change the list at all, I have taken advise of commenters but have yet to make it work. Does anyone have any idea?
Basically I am trying to say while two last names are the same check the first names, then swap them if need be.
The heart of your problem lies here:
if(previous.firstName.compareTo(current.firstName) >= 0){
temp = current;
current = previous;
previous = temp;
}
Firstly - >=. That's probably not so much a problem as just unnecessary - there's no need to swap elements that are equal - just make it > instead.
Next, that code doesn't change the linked-list at all. All it does is change the values of the local variables, so previous ends up pointing to the node after current in the actual linked-list, and then, because of the >=, if you have equal values, you'll just continuously process the same two nodes.
This post elaborates a bit on that: Is Java "pass-by-reference" or "pass-by-value"?
What you need to do is compare something.next and something.next.next (no need for 2 separate variables), and then you can swap these, which will change the linked-list.
Related
I am already aware of various answers to this question. But I have a very confusing bug in my code. The following is a series of println() calls to see if the list I created is correctly sorted.
ListNode list_b = new ListNode(3, new ListNode(-2));
System.out.println("Checking the string conversion: " +
sut.convertToString(list_b)); //output is 3,-2, as expected. Expected result of sorting is -2,3.
System.out.println("Now checking the
string conversion of the sorted list: " +
sut.convertToString(sut.sort(list_b, int_comparator))); //output is -2,3 as expected.
System.out.println("Now this is list_b following the sorting,
by calling the element and next directly: "
+ list_b.element + "," + list_b.next); //3,null. How the hell did that happen!?!??!!?
The convertToString method is as follows:
public String convertToString(ListNode head) {
if (head != null) {
String representation = "";
if (!head.element.equals(null))
representation += head.element.toString();
ListNode next = null;
if (head.next != null)
next = head.next;
if (next != null && !next.element.equals(null))
representation += "," + next.element.toString();
while (next != null) {
if (next.next != null) {
next = next.next;
if (!next.element.equals(null))
representation += "," + next.element.toString();
}
else
break;
}
return representation;
}
else
return "";
}
And the actual sort method is still a work in progress, albeit fairly simple:
public ListNode sort(ListNode head, Comparator comparator) {
if (head != null) {
ListNode next = null;
if (head.next != null)
next = head.next;
else
return head;
if (comparator.compare(head.element, next.element) > 0) {
head.next = next.next;
next.next = head;
head = next;
}
return head;
}
return null;
}
Would anyone care to explain how I've managed to do the seemingly impossible? I'm speechless at how this could happen! Many thanks to anyone who can explain!
EDIT: thank you to those for your answers and suggestions. I should clarify that the following tests are then performed on the list:
assertTrue(sut.deepEquals(list_a, sut.sort(list_a, int_comparator)));
assertFalse(sut.deepEquals(list_b, sut.sort(list_b, int_comparator)));
assertTrue(sut.deepEquals(new ListNode(-2, new ListNode(3)), sut.sort(list_b, int_comparator)));
assertTrue(sut.deepEquals(new ListNode(-14, new ListNode(-2, new ListNode(3))), sut.sort(list_c, int_comparator)));
Clearly, this implies that any updating of list_b (i.e. list_b = sut.sort(list_b)) is unnecessary. What I'm asking is how you would change the sort method itself so that updating is unnecessary.
Pretty simple: you sort the list in this piece of code:
sut.convertToString(sut.sort(list_b, int_comparator)))
The list is transformed this way:
3 -> -2 -> null ====> -2 -> 3 -> null
^ ^
| |
list_b list_b
sut.sort returns the new front (head) of the list, which should be the new list_b, but since you don't update the value, it points to the second node in the list, thus producing "3 , null"
Well ... you are changing the internals of the passed node inside your sort method. The variable list_b is still referring to the node "3" that after sorting does not have a successor anymore.
Your sort method is returning the new head of the sorted list. But you do not use that afterwards!
Change your code snippet to:
list_b = sut.sort(list_b, int_comparator);
System.out.println(sut.convertToString(list_b));
list_b references the node containing 3, and having the node 2 as next element.
Then you sort the list. That changes the node containing 3: its next node is now null, since it becomes the last element of the list. It also changes the node 2, which now has the node 3 as next element. But list_b continues to be a reference to the node containing 3.
When you print the node list_b, you thus get 3, null as a result.
EDIT:
to answer your last question:
how you would change the sort method itself so that updating is unnecessary
You should have an actual LinkedList class (exactly as the standard java.util.LinkedList class). The class you have doesn't represent a list. It represents a node inside a chain of nodes.
The LinkedList class would have a reference to the head node, and would contain all the methods needed to iterate through the values stored in the list, transform it to a String, sort it, etc. Of course, sorting the list would imply changing the reference to the head node inside the LinkedList object. But that would be transparent to the user of the LinkedList class, which would never manipulated nodes directly.
Of course, if you start caring so much about having a proper LinkedList class, you should simply use the standard one, which is standard, documented, and tested.
I am still a little confused on how to implement a doubly linked list. After researching and finding some information about them, I've got a good visual of how some of the basic functions work, such as add, remove etc. However, If I wanted to write a method for a DLList called countEquals (E check) that walks the entire list and counts the number of items in the list that are equal to the "Check" data item passed to the method, how would I write this. The instructions say I cannot use a for-each loop or an iterator, but a loop of some sort must be used. After the list is fully traversed, return the number of items that are equal to "check". Here is some code that I have tried to write for this:
internal references of DLList: Fields (next, data, prev) Members (size), head and tail.
public int countEquals(E Check){
currentNode = head;
int count = 0;
while (currentNode != null) {
if (currentNode.data == check) {
count ++;
else {
currentNode.next = currentNode;
}
return count;
}
I'm not too sure if this will correctly walk a doubly linked list, if I have to write more pointer assignments? I was thinking that because I'm not adding any nodes, but just walking a list, I wouldnt need to do the pointers.
Almost there, good effort. However, just a couple of things. First, you may want to look into using something.equals() rather than == since the latter is for object identity rather than value equality.
Second. the logic for traversing the list is slightly off. It needs to be done regardless of whether an item was found, and the sides of the assignment should be reversed.
Have a look at the following pseudo-code for guidance:
def countEquals(check):
currentNode = head
count = 0
while currentNode != null:
if currentNode.data == check:
count = count + 1
currentNode = currentNode.next
return count;
do it this way, if data is integer
while (currentNode != null) {
if (currentNode.data == check) {
count ++;
currentNode.next = currentNode;
}
else {
currentNode.next = currentNode;
}
}
return count;
I am attempting to insert people alphabetically into a linked list by lastName. I have one really weird issue. The code as you see it works fine, except the list is in reverse order. What I can not figure out is why when I use the line of code:
current != null && lastName.compareTo(current.lastName) >= 0)
to insert people into my list, instead of adding over 100 people I add 6. Yet like I said above I can do it in reverse order no problem. What is up with that?
public void insert(String firstName, String lastName, String time,String show, String command,int section){
PeopleNode newNode = new PeopleNode(lastName,firstName,time,show,command,section);
size++;
PeopleNode previous = null;
PeopleNode current = head;
while(current != null && lastName.compareTo(current.lastName) <= 0){
previous = current;
current = current.next;
}
if(previous == null){
head = newNode;
}else{
previous.next = newNode;
newNode.next = current;
}
}
I guess the compareTo method works the other way round for Strings, so maybe try
while(current != null && current.lastName.compareTo(lastName) <= 0)
But I recommend you to use just the Compareable interface and sort the list by using Collections.sort(yourlist)
Let's say you have "b", "c", "d" and "a" as the last names. If you insert in that order, for the first three nodes it will be:
b -> c -> d
When you try to insert a, it will not enter the while loop. So, "previous" will be null. It will enter the first if condition and set the head as the new node, i.e. "a", and it will exit the method. Next of the head is not set.
So you have a broken list with only the "a" node at the end.
I did not try this but this seems to be the problem.
Ok I figured it out!
if(previous == null){
head = newNode;
}else{
previous.next = newNode;
newNode.next = current;
}
The line:
newNode.next = current;
needs to be outside of the else statement!
I know it is a minute code. I can't understand why my linked list reversal is not working.
Can some one help fix me my reverse method in the below code.
//Reverse a single linked list
public Node reverse()
{
return reverse(root);
}
private Node reverse(Node node)
{
Node previous = null;
Node current = node;
Node forward;
while (current != null)
{
forward = current.next;
current.next = previous;
previous = current;
current = forward;
}
return previous;
}
Any input on this would be helpful
Thanks !!!
I'm pretty sure it should be
return root = reverse(root);
(Your reverse logic is correct, but if the root is still pointing at the old root of the list, you will end up with a 1-element linked list.)
Assuming homework...
Write simple tests: list of 0 items, list of 1 items, list of 2 items, list of 10 items. Than make sure each of them work - will narrow down error and learn to write unit tests.
Are you sure you're using the returned value of reverse() to do your iteration instead of root?
I am tring to insert string into doubly linked list in reverse order. But I am not sure how can I maintain the insertion order in reverse order.
This is my below code.
theList.insertReverseLexicographicalOrder("B");
theList.insertReverseLexicographicalOrder("A");
theList.insertReverseLexicographicalOrder("H");
theList.insertReverseLexicographicalOrder("D");
theList.insertReverseLexicographicalOrder("E");
public void insertReverseLexicographicalOrder(String dd) {
Link newLink = new Link(dd);
if (isEmpty()){
last = newLink;
}
first.previous = newLink;
}
newLink.next = first;
first = newLink;
}
Any suggestions will be appreciated with some code based on my solution..
Well you assume that its already in reverse order, so you're going to need some sort of loop through until you find where it should go.. i.e.
Z, Y, X, W, L, K, A
if you're inserting M, then you should loop until you find L, which is lexicographically larger than M, and therefore insert it there. Because the nodes have previous pointers, insertion shouldn't be too hard to figure out on your own
You would need to look through the list comparing each of the elements. Stop when you find the element that goes after the element you are trying to insert. I suggest you implement the compareTo method in your node class: http://www.javapractices.com/topic/TopicAction.do?Id=10
and use it to make the comparisons
Good Luck.
How to insert a node into a linked list:
If the list is empty, the new node will become the first, and if we keep track of that, also the last
Otherwise find the position where to insert, there are three possibilities,
a) the new node has to be inserted before the first
b) the new node has to be inserted after the last
c) the new node has to be inserted between two existing nodes
Update the appropriate references, that may be first, last and some next and previous fields, depending on where it has to be inserted
if (first == null) {
// the list is empty so far
} else
To find the position, first compare the data with the first node's data to see whether it has to be inserted before the first node.
if (newLink.iData.compareTo(first.iData) > 0) {
// put newLink before first
} else {
You have to keep a focus on some list node. Follow the list from the start until you reach the point of insertion:
Link focus = first; // focus first node
while(focus.next != null && newLink.iData.compareTo(focus.next.iData) < 0) {
focus = focus.next;
}
// now you have to insert the new value behind focus, left as exercise
if (focus.next == null) {
// newLink becomes the last node in the list
} else {
// newLink has to be inserted between focus and focus.next
}
}
Then insert. Beware of edge cases, inserting at the front and the end are slightly different.