I'm trying to used recursive method to complete the addLast method in a singly linked list, however, the code gives me a wrong output of list.size() = 2 and list.getFirst() = 5. The reason should be due to the line
SLList p=this;
It seems changing p reference changes "this" reference as well, which is not so logic to me. Could anyone give some details about this? Thx
public class SLList {
public class IntNode {
public int item;
public IntNode next;
public IntNode(int i, IntNode n) {
item = i;
next = n;
}
}
private IntNode first;
public SLList(int x) {
first = new IntNode(x, null);
}
/** Adds an item to the front of the list. */
public void addFirst(int x) {
first = new IntNode(x, first);
}
/** Retrieves the front item from the list. */
public int getFirst() {
return first.item;
}
/** Adds an item to the end of the list. */
public void addLast(int x) {
SLList p = this;
if (p.first. next == null) {
p.first.next = new IntNode (x, null);
}
else {
p.first = p.first.next;
p.addLast(x);
}
}
/** Returns the number of items in the list using recursion. */
public int size() {
/* Your Code Here! */
SLList p = this;
if (p.first == null) {
return 0;
}
else if (p.first.next == null){
return 1;
}
else {
p.first = p.first.next;
return 1 + p.size();
}
}
public static void main (String[] args) {
SLList list=new SLList (5);
list.addFirst(10);
list.addFirst(15);
list.addLast(17);
System.out.println(list.getFirst());
System.out.println(list.size());
}
}
The problem is nothing to do with the assignment of this. Nothing can change this. Period.
(But things can change the state of the object that this refers to.)
The real problem is in your implementation of the size method. Your size method is causing the list to change. It shouldn't. In your case, the change causes:
the size() method to return the wrong value
subsequent getFirst() calls to return the wrong value.
I won't say exactly where the bug, but you should be able to spot it yourself by a process of elimination. (Or if that fails, use a debugger and try to observe where the list is changing.)
There are bigger problems with your algorithms than you think. size() is incorrect. You can fix this if you realize that you need to count the number of IntNode objects in the list. Similarly all other methods need to manipulate IntNode objects.
SLList p = this;
p reference to the same SLList object. if you make any changes to 'p' then it will also happened to 'this', becuase of reference type (not value type).
Here in the statement
p.first = p.first.next;
the reference to the first is changed when you call 'addLast' method. You loss the reference to the first item.
If you remove the line
list.addLast(17);
in main method you will see the correct answer. The problem is with this method.
Change the method as follow and add the new method below.
/** Adds an item to the end of the list. */
public void addLast(int x) {
addLast(x, this.first);
}
private void addLast(int x, IntNode node){
if(node.next == null){
node.next = new IntNode (x, null);
}else {
node = node.next;
addLast(x, node);
}
}
Then you will not lose the reference to first item and now it works fine,
Problem in your implementation is addLast and size method are changing the value of field variable first.
It don't matter whether you assignthis to some variable or use directly.
Because assigning this to some variable does not create new this object but assign's reference to that variable.
So you should first copy value of first field variable to some local variable then iterate on it.In this way your first will not change.
Hint: Don't change the first variable reference.
Your addLast() and size() changes value of first which is wrong.
Problem is in this line.
p.first = p.first.next;
Related
I have a list and want to try to use a search method. It works for values 3 digits or shorter, but will not work for longer numbers. Where am I going wrong?
class ListNode<T> {
T data; // data for this node
ListNode<T> nextNode; // reference to the next node in the list
// constructor creates a ListNode that refers to object
ListNode(T object) {this(object, null);}
// constructor creates ListNode that refers to the specified
// object and to the next ListNode
ListNode(T object, ListNode<T> node) {
data = object;
nextNode = node;
}
T getData() {return data;}
ListNode<T> getNext() {return nextNode;}
}
// class List definition
public class List<T> {
public ListNode<T> firstNode;
private ListNode<T> lastNode;
private String name; // string like "list" used in printing
// constructor creates empty List with "list" as the name
public List() {this("list");}
// constructor creates an empty List with a name
public List(String listName) {
name = listName;
firstNode = lastNode = null;
}
// insert item at end of List
public void insertAtBack(T insertItem) {
if (isEmpty()) { // firstNode and lastNode refer to same object
firstNode = lastNode = new ListNode<T>(insertItem);
}
else { // lastNode's nextNode refers to new node
lastNode = lastNode.nextNode = new ListNode<T>(insertItem);
}
}
public boolean search(ListNode <T> node,T data)
{
if (node == null){
System.out.println("Not found");
return false;}
if (node.data == data){
System.out.println("Found");
return true;}
return search(node.nextNode,data);
}
}
Main class:
import java.util.Scanner;
public class ListTest {
public static void main(String[] args) {
List <Integer> list=new List<>();
// insert integers in list
list.insertAtBack(11);//is found
list.insertAtBack(10);
list.insertAtBack(111);
list.insertAtBack(1234); //value that is not being found
list.insertAtBack(123);
//prep scanner
Scanner input = new Scanner(System.in);
System.out.print("Please enter a value:");
int searchInt = input.nextInt();
//search
list.search(list.firstNode,searchInt);
}
}
As shown, it works with 10,11, and 123, but not 1234. Any help or direction is appreciated. I think it could be due to not having enough nodes, but I am not sure.
Your problem is in how you are testing for a match between the search term and each value in the list. To test if the integer value of two Integer objects are the same, you have to use Integer.equals() rather than ==.
Change this line of your code:
if (node.data. == data) {
to:
if (node.data.equals(data)) {
and you'll get the result that you expect. The reason that == doesn't work is that it is testing if the two values are the exact same object. This generally won't be the case for two Integer objects that contain the same primitive integer value.
The reason that your code seems to work for many of your test values is that Java caches the first N Integer objects (I don't know what N is) for memory efficiency figuring that these values are very commonly used. So when you create a new Integer object with a small integer value, Java returns the same exact object that represents that value every time you seem to be creating a new object. So it isn't that you're adding more than 3 items to your list. It's that one of your values is larger than the largest value Integer object that Java caches.
UPDATE: I googled and found that Java caches Integer objects with values in the range –128 to +127.
When given an array of integers, I'm trying to change each element with the product of the integers before it.
For example, int[] array = {2,2,3,4}; is now: {2, 4, 12, 48};
I added each element to a LinkedList, and I'm trying to do this recursively.
This is what I have:
Node curr = list.getFirst();
product(curr);
public static void product(Node curr)
{
if(curr == null)
{
return;
}
else
{
int data = curr.getData() * curr.getNext().getData();
Node newNode = new Node(data);
curr.setNext(newNode);
// product(curr);
}
}
The first product works: {2,4}, but when I try to put in the recursion, I get a stackoverflow. Any suggestions??
Edit: So the reason that I'm either getting a stackoverflow or null pointer exception is because I'm updating the list, and then trying to get the next integer(but since there's only two elements in the list, there isn't a getNext()). I'm not sure how to fix this.
It looks like you were getting a bit tied up in the recursion. I modified your method to accept a Node along with the product from the previous iteration. At each step of the iteration I update the value in the already-existing List, so there is no need for using the new operator.
public static void product(Node curr, int value) {
if (curr == null) {
return;
}
else {
int data = value * curr.getData(); // compute current product
curr.setData(data); // update Node
product(curr.getNext(), data); // make recursive call
}
}
There are actually two issues with the code.
The recursion never ends, i.e. it is not actually moving to a smaller "subproblem" as the recursion is calling the same node again
and again.
After creating a new node and modifying the next we also need to connect the node "after" the next node otherwise the link will be
lost. Please check the below method which addresses both the issues.
Although I didn't do an excessive testing it is working for simple dataset.
Original List:
2->4->5->6->8->null
Multiplied List:
2->8->40->240->1920->null
public void product(Node curr) {
if (curr.getNext() == null) {
return;
} else {
int data = curr.getData() * curr.getNext().getData();
Node newNode = new Node();
newNode.setData(data);
Node nodeAfterNextNode = curr.getNext().getNext();
newNode.setNext(nodeAfterNextNode);
curr.setNext(newNode);
product(newNode);
}
}
It is because you call recursive method on the current node, so it is actually never move forward in the LinkedList. You can simply update the next node's data and call the recursive method on it. See the code below:
Node curr = list.getFirst();
product(curr);
public static void product(Node curr)
{
Node next = curr.getNext();
if(next == null)
{
return;
}
else
{
int data = curr.getData() * next.getData();
next.setData(data);
product(next);
}
}
How come when I create a linked list node, append some data, then move the head in another method, the head stays the same in the callee method?
For example:
public static void append(Node n, int d) {
while (n.next != null) {
n = n.next;
}
n.next = new Node(d);
}
public static void test(Node n) {
n = n.next;
}
public static void main(String[] args) {
Node head = new Node(5);
append(head, 4);
append(head, 3);
test(head); //this moves the head to head.next
//why is head still = 5 after we get here?
}
In the method append, the line n = n.next won't affect the original node passed as argument, in your case head. Why? Because Java is pass by value. This means that if inside the method, you modify the reference of head (which is received as n inside the method), it won't affect the original reference. Therefore, head will still refering to the same location in memory (the same object).
Also, your method test doesn't do anything because you are creating a local variable:
Node next = ...;
and then assigning n.next to it. But this variable only exists inside that method, so it won't affect anything outside it.
next is a property, not a method. Your test method is just grabbing the reference to n.next, it is not "moving the head."
I am trying to implement the deletion function in
public static boolean delete(Object d, ListElement head){
ListElement p=find(d,head);
if(p.data==null){return false;}
else {
System.out.println("Delete Successfully!");
if(head.data==d){head=head.next;}
else{
while(head.next.data!=d){
head=head.next;
}
head.next=head.next.next;}
return true;}
}
This function basically check if the element d is in the list,
-if not->return false;
-else check whether the element is the first element of the list, if true, change the head to its next,
-else traverse to the list element in front of it.
The problem is case the element to delete is the first element, such as boolean s=ListElement.delete(1,d); I cannot use "head=head.next;" to assign new value to head. But java is passed by reference, why cannot I change that?
//actually I found my question is whether we can change the reference passed to the function inside the function
like:
void fun(dog a){
a=new dog("Po");
}
main()
{dog b=new dog("Jo");fun(b);}
//so will b be changed?
The reference to the first list element is either held by the list object itself or by an "invisible" root element (in case of single linked list).
So you either have to pass the entire list to the method or, if you have that invisible root, pass the root as head.
public static boolean delete(Object d, MyLinkedList<ListElement> list) {
ListElement head = list.getHead();
if (head.data.equals(d)) { // <- ALWAYS use equals, never == to compare objects!!
list.setHead(head.next);
} else {
ListElement element = head.next;
// ... the rest is similiar to your algorithm
}
}
The Java Pass by Reference idea means, that when you call a method, and give some object as an argument, you'll get a new reference pointing to the same object.
Altering values, will change the object, in turn also affecting other references. But if you give a new value to the parameter, only that will be changed, to point to some different object. (It's worth mentioning that there are languages that do allow changing the argument, to change the first passed parameter.)
void delete(visit_ptr_node_type this_is_the_node)
{
visit_ptr_node_type one_back;
if(anchor == NULL)
printf("\n The list is empty");
else
{
if(this_is_the_node==anchor)
anchor=anchor->next_ptr;
else
{
one_back=anchor;
while(one_back->next_ptr != this_is_the_node)
one_back=one_back->next_ptr;
one_back->next_ptr = (this_is_the_node) ->next_ptr;
}
free(this_is_the_node);
}
}
I have a question regarding the output of the following program. The output is null. This is what I thought as well. Im thinking its because the methods called before display simply modify a copy of head and not head itself. Im assuming that I could get around this using a this.head= something right?
Heres the code:
public class List {
private Node head;
public List (){
int max=3;
int i;
head=null;
Node aNode=new Node(0);
for (i=0; i<max; i++) {
aNode.setNum(i);
add (aNode);
aNode.setNext(null);
}
}
public void add(Node aNode) {
Node temp;
if(head==null)
head=aNode;
else {
temp=head;
while(temp.getNext()!=null)
temp=temp.getNext();
temp.setNext(aNode);
}
}
public void display() {
Node temp=head;
while(temp!=null) {
System.out.println(temp.getNext());
temp=temp.getNext();
}
}
}
public class Node {
private int num;
private Node next;
public Node (int n) {num=n; next=null;}
public int getNum() {return num;}
public void setNum(int n) {num=n;}
public void setNext(Node n) {next=n;}
public Node getNext() {return next;}
}
public class Driver {
public static void main(String args[]) {
List aList=new List();
aList.display();
}
}
The add relies on receiving a new Node with next being null. So move Node aNode = new Node(); inside the for-loop.
Some sanitary remarks.
(Unimportant) Use current instead of temp, or anything else.
Fields in classes are by default null/0/0.0/false.
Before I answer your question, here is a side note...
Im thinking its because the methods called before display simply modify a copy of head and not head itself.
This is NOT correct.
Here is why...
public void display() {
// Basically, this says, make temp a REFERENCE of head...NOT A COPY!!!!
Node temp=head;
while(temp!=null) {
System.out.println(temp.getNext());
temp=temp.getNext();
}
}
Now, to answer your question, the reason temp is null is because head is null. And the reason head is null is because you never initialize it.
From you constructor...
public List (){
int max=3;
int i;
// Here you're saying "set head to null".
// So when you call display, head is NULL. You MUST initialize this.
head=null;
Node aNode=new Node(0);
for (i=0; i<max; i++) {
aNode.setNum(i);
add (aNode);
aNode.setNext(null);
}
}
Look at this code from the constructor:
Node aNode=new Node(0);
for (i=0; i<max; i++) {
aNode.setNum(i);
add (aNode);
aNode.setNext(null);
}
You create one new node, and then keep trying to add that node to the list. You need the first line to be inside the for loop, so you create lots of nodes. After the constructor completes, your list only contains one node, with the value 3. Then later, in display():
System.out.println(temp.getNext());
You start by calling getNext() on the first node. Since there's only one node, getNext() returns null, which is what you print out. You should replace this line with
System.out.println(temp);
The error is nothing to do with the this keyword at all. You only need this.foo (when foo is some data member of your class) to disambiguate when you have a both a member and a local variable or parameter with the same name.