Linked List in Java method implementation - java

Now Im preparing for coding interview and I have 1 question regarding linked list in Java. Can you tell me some reliable sources from which I can learn and practice the basic linked list methods. I liked this one: www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/code/LinkedList.java but I'm confused with some method implementations. For example the method E get(int pos) returns NOT node but the data E contained in the node at position pos. While here http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/util/LinkedList.java#LinkedList the method Node node(int index) returns the node at that position (not the data contained in it). Which implementation should I follow?

Well the Data Structures is a very conceptual and context based discipline. The various implementations that exist for each data structure are based on the requirements and scope of the data structure.
One can even argue that the LinkedList implementation of the Collection API is buggy as it doesn't work well if multiple threads work on it concurrently. Then a synchronizedList from the Collections class need to be made or at least an implementation which works well with multiple threads need to be used.
Follow the minimum viable convention that gets you going, as the interviewer won't just ask you a LinkedList implementation. What the interviewer wants to know is if your concepts and coding skills are up to a certain mark or not.
Think of what you can do with a Linked List. For that you'll have to think as to what type of LinkedList you are actually considering as there are many different kinds of LinkedLists, like SinglyLinkedList, DoublyLinkedList, SkipList, etc.
Considering a SinglyLinkedList, your LinkedList implementation should at least have the following methods: add(), remove(), contains(), clear(), size().
Following is my implementation of a SinglyLinkedList:
import java.util.Iterator;
import java.util.StringJoiner;
public class LinkedList<T> implements Iterable<T>
{
private Node head;
private Node tail;
private int size;
private class Node
{
private T value;
private Node next;
public Node(T value)
{
this.value = value;
}
}
public void add(T value)
{
Node node = new Node(value);
if (head == null)
{
head = node;
}
else
{
tail.next = node;
}
tail = node;
++size;
}
public boolean remove(T value)
{
Node previous = null;
Node current = head;
while (head != null)
{
if (current.value.equals(value))
{
if (previous != null)
{
previous.next = current.next;
if (current.next == null)
{
tail = previous;
}
}
else
{
head = current.next;
if (head == null)
{
tail = null;
}
}
--size;
return true;
}
previous = current;
current = current.next;
}
return false;
}
public boolean contains(T value)
{
Node current = head;
while (current != null)
{
if (current.value.equals(value))
{
return true;
}
current = current.next;
}
return false;
}
public void clear()
{
head = null;
tail = null;
size = 0;
}
public int size()
{
return size;
}
#Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
private Node current = head;
#Override
public boolean hasNext()
{
return current != null;
}
#Override
public T next()
{
Node next = current;
current = current.next;
return next.value;
}
};
}
#Override
public String toString()
{
StringJoiner joiner = new StringJoiner(", ");
for (T value : this)
{
joiner.add(value.toString());
}
return joiner.toString();
}
}
As you can see, my implementation may be different from the implementations that you may find elsewhere. Small differences are not any issue as long as the data structure's concepts aren't radically changed and as long as it works properly with its defined interface.
For disciplines like Data Structures, you have to think for yourself and based on your requirements, use or implement data structures that fit your needs. As far as interviews go, an implementation of a minimum viable data structure is all that you need to show and all that is required. Just make sure that such minimum viable data structure isn't buggy in its context.

Related

Linked list and adding elements to the end of the list

I need to create linked list of rectangles. Im given the class files of rectangleandpointclasses, and need to writeRectNodeandRectList`.
This is RectNode:
public class RectNode
{
private RectangleA _rect;
private RectNode _next;
public RectNode(RectangleA r)
{
_rect = r;
_next = null;
}
public RectNode(RectangleA r,RectNode n)
{
_rect = r;
_next = n;
}
public RectNode(RectNode other)
{
_rect = other._rect;
_next =other._next;
}
public RectangleA getRect()
{
return _rect;
}
public RectNode getNext()
{
return _next;
}
public void setRect(RectangleA r)
{
_rect = r;
}
public void setNext (RectNode next)
{
_next = next;
}
}
And this is part of RecList which I have a problem with:
public class RectList
{
private RectNode _head;
public RectList()
{
_head = null;
}
public void addRect(RectangleA t)
{
RectNode value = new RectNode(t,null);
RectNode next = new RectNode(t,_head);
while(next.getNext() != null)
{
next = next.getNext();
}
}
I want the order to be first-in first-out, but managed only first-in last-out by this:
public void addRect(RectangleA t)
{
RectNode next = new RectNode(t,_head);
_head = next;
}
So how I do that the way I need? I tried running trough the list by next.getNext() until the
next is null, but can`t figure out by code and implement it.
These are the issues in addRect:
There is no assignment of value to anything that relates to the list, so the node is not added.
There is no provision to update _head when it was null
If it was intended to create a dummy node that sits in from of _head (which can be a valid approach), then this idea is destroyed by walking away from that node in the loop: you have for ever lost the reference to that dummy node, yet you would need it to update _head (in case it was null)
There are several ways to do this, and I understand from your code that you want to introduce a dummy node, that sits in front of the current _head. But then you would need another reference to stay there, one that is not modified by the loop.
I would also use different variable names. value gives the impression that it is about a node's value, but this is not the case: it is a node -- the one to be added -- so I'd just call it newNode if you really need the variable at all. The name next suggests that it refers to something that precedes it, but that seems irrelevant here. As its purpose is to identify the tail of the list, why not call it tail?
1. Solution using a dummy node
public void addRect(RectangleA t)
{
final RectNode preHead = new RectNode(t, _head); // This reference will stay here
RectNode tail = preHead; // This reference will traverse the list
while (tail.getNext() != null)
{
tail = tail.getNext();
}
// Now create and append the new node immediately after the tail node:
tail.setNext(new RectNode(t, null));
// Set the new head (in case it was null, this is relevant)
_head = preHead.getNext();
}
2. Solution without dummy node
Although the solution with the dummy node does not have to distinguish between the case of an empty and non-empty list, you can avoid the use of the dummy node when you do deal separately with these cases:
public void addRect(RectangleA t)
{
if (_head == null) { // Special case, the only one where _head must change
_head = new RectNode(t, null);
} else { // The list is not empty, and _head will not change
RectNode tail = _head;
while (tail.getNext() != null)
{
tail = tail.getNext();
}
// Now create and append the new node immediately after the tail node:
tail.setNext(new RectNode(t, null));
}
}

Constructor Node cannot be applied to given types while working with nodes

I am working on a code that puts new elements on MyStack if they are unique. I had to copy and paste the node starting code, so I'm having a bit of trouble with an issue. I keep getting two error messages, even after trying various workarounds and I'm not really understanding why. I've even tried using some helper functions I've previously made that have worked before so I'm extra confused.
The two errors I consistently get are:
-cannot infer type arguments for MyStack.Node (actual and formal arguments differ in length)
-constructor node cannot be applied to given types. Required, no arguments, found: anything,
Here's my code:
public class MyStack<Anything>
{
private Node first, last;
private class Node<Anything>
{
Anything item;
Node next;
}
public boolean contains(Anything value)
{
for (Node curr = first; curr != null; curr = curr.next)
{
if (value.equals(curr.item)) {
return true;
}
}
return false;
}
public void add(Anything value)
//method that adds a new value to the end of the list
//COMPLETE
{
Node temp = first;
while(temp.next!=null){ //finds the end
temp=temp.next;
}
temp.next=new Node(value, null); //assigns new value
}
public void enqueue(Anything info){
if (this.contains(info)==true) { //if the info is already present
System.out.println("the stack already contains this value");
return;
}
//if we actually need to add the info
if (first == null) { //if there is nothing in the stack
Node temp= first;
first = new Node<>(info,temp);
first = temp;
return;
}
if (first != null) { //if there is already stuff
Node temp = first;
while (temp.next == null)
{ Node newNode= new Node<>(info, temp);
temp.next = newNode;
}
return;
}
}
}
As #Andreas already pointed out, Node needs a constructor.
There are a few other flaws in your Code:
Use Generics
With your Code, you can only store Objects of the class Anything, what strongly limits its reusability. Use a generic instead and you can reuse this class for many more purposes.
Linked List
I suggest, you use the paradigm of a double-linked-list. That way you do not need to find the last Node to add something to the Stack. Node now has a pointer to its previous and next element.
Use the last Object
You have the object last but never use it. To find out, whether the current object is the last one you compare the value to null. This has the effect, that storing a null value will break your List. Instead compare to the Object last, this object is unique and guarantees you, that you are at the end of the list. Both first and last are Nodes that do not contain a value and are simply used to mark the start/end of your List.
Adding elements
Using the changes above, the code in the Method enqueue(T value) becomes significantly simpler: You just check whether contains(value) and decide whether you add the value to the List or not.
All these changes applied result in following code:
public class MyStack<T extends Object> {
private Node first, last;
public MyStack() {
first = new Node(null, null, null);
last = new Node(null, null, first);
first.next = last;
}
private class Node {
T item;
Node next;
Node previous;
public Node(T item, Node next, Node previous) {
this.item = item;
this.next = next;
this.previous = previous;
}
}
public boolean contains(T value) {
for (Node curr = first.next; curr != last; curr = curr.next) {
if (value.equals(curr.item)) {
return true;
}
}
return false;
}
/**
* method that adds a new value to the end of the list
*/
public void add(T value)
{
Node secondLast = last.previous;
Node added = new Node(value, last, secondLast);
secondLast.next = added;
last.previous = added;
}
/**
* only adds value if it is not already contained by the Stack
*/
public void enqueue(T value) {
if (this.contains(value) == true) { // if the info is already present
System.out.println("the stack already contains this value");
}
else {
add(value);
}
}
public static void main(String[] args) {
MyStack<String> test = new MyStack<>();
test.add("foo");
test.add("bar");
test.add("baz");
System.out.println(test.contains("bar"));
System.out.println(test.contains("new"));
test.enqueue("baz");
test.enqueue("MyStack");
}
}
Naming
As you may have noticed, in my explanation I called this class a List. This is because it fulfills more of the characteristics of a List. A Stack usually only provides the methods push to put something at the top of the Stack and pop to remove and return the topmost Object. Optionally peek can return the topmost Object, without removing it from the Stack.
Also consider renaming the method enqueue: enqueue is used in Queues (obviously) and Queues do not forbid to add two equal Objects. So the name is misleading. I would call this method something like addIfNotContaining.
In my Opinion you should name this class to be a List and add a method get(int i) to get a specific element at a position. Naturally adding some other methods like size ect. to comply with a standard List. But I assume you already had, but did not post them because they are not related to your problem.
Multithreading
This Class is far from threadsave. But I let you figure out yourself how to make it threadsave if needed.

Java parallel streams: there's a way to navigate a binary tree?

I'm struggling to find a proper way to get a speedup from this stream:
StreamSupport.stream(new BinaryTreeSpliterator(root), true)
.parallel()
.map(node -> processor.onerousFunction(node.getValue()))
.mapToInt(i -> i.intValue())
.sum()
onerousFunction() is just a function that makes the thread work for a bit and returns the int value of the node.
No matter how many cpus i use, the execution time always remains the same.
I think the problem stands in the Spliterator i wrote:
public class BinaryTreeSpliterator extends AbstractSpliterator<Node> {
private LinkedBlockingQueue<Node> nodes = new LinkedBlockingQueue<>();
public BinaryTreeSpliterator(Node root) {
super(Long.MAX_VALUE, NONNULL | IMMUTABLE);
this.nodes.add(root);
}
#Override
public boolean tryAdvance(Consumer<? super Node> action) {
Node current = this.nodes.poll();
if(current != null) {
action.accept(current);
if(current.getLeft() != null)
this.nodes.offer(current.getLeft());
if(current.getRight() != null)
this.nodes.offer(current.getRight());
return true;
}
return false;
}
}
But i really can't find a good solution.
To process data in parallel, you need a trySplit implementation to return partial data as a new Spliterator instance. The spliterator instances are traversed by a single thread each. So you don’t need a thread safe collection within your spliterator, by the way. But your problem is that you are inheriting the trySplit implementation from AbstractSpliterator which does attempt to provide some parallel support despite not knowing anything about your data.
It does so, by requesting some items sequentially, buffering them into an array and returning a new array based spliterator. Unfortunately, it does not handle “unknown size” very well (the same applies to the parallel stream implementation in general). It will buffer 1024 elements by default, buffering even more the next time, if there are as much elements. Even worse, the stream implementation will not use the array based spliterator’s good splitting capabilities, because it treats “unknown size” like the literal Long.MAX_VALUE, concluding that your spliterator has much more elements than the 1024 elements in the array, hence, will not even try to split the array based spliterator.
Your spliterator can implement a much more suitable trySplit method:
public class BinaryTreeSpliterator extends AbstractSpliterator<Node> {
/**
* a node that has not been traversed, but its children are only
* traversed if contained in this.pending
* (otherwise a different spliterator might be responsible)
*/
private Node pendingNode;
/** pending nodes needing full traversal */
private ArrayDeque<Node> pending = new ArrayDeque<>();
public BinaryTreeSpliterator(Node root) {
super(Long.MAX_VALUE, NONNULL | IMMUTABLE);
push(root);
}
private BinaryTreeSpliterator(Node pending, Node next) {
super(Long.MAX_VALUE, NONNULL | IMMUTABLE);
pendingNode = pending;
if(next!=null) this.pending.offer(next);
}
private void push(Node n) {
if(pendingNode == null) {
pendingNode = n;
if(n != null) {
if(n.getRight()!=null) pending.offerFirst(n.getRight());
if(n.getLeft() !=null) pending.offerFirst(n.getLeft());
}
}
else pending.offerFirst(n);
}
#Override
public boolean tryAdvance(Consumer<? super Node> action) {
Node current = pendingNode;
if(current == null) {
current = pending.poll();
if(current == null) return false;
push(current.getRight());
push(current.getLeft());
}
else pendingNode = null;
action.accept(current);
return true;
}
#Override
public void forEachRemaining(Consumer<? super Node> action) {
Node current = pendingNode;
if(current != null) {
pendingNode = null;
action.accept(current);
}
for(;;) {
current = pending.poll();
if(current == null) break;
traverseLocal(action, current);
}
}
private void traverseLocal(Consumer<? super Node> action, Node current) {
do {
action.accept(current);
Node child = current.getLeft();
if(child!=null) traverseLocal(action, child);
current = current.getRight();
} while(current != null);
}
#Override
public Spliterator<Node> trySplit() {
Node next = pending.poll();
if(next == null) return null;
if(pending.isEmpty()) {
pending.offer(next);
next = null;
}
if(pendingNode==null) return next==null? null: new BinaryTreeSpliterator(next);
Spliterator<Node> s = new BinaryTreeSpliterator(pendingNode, next);
pendingNode = null;
return s;
}
}
Note that this spliterator would also qualify as ORDERED spliterator, maintaining a top-left-right order. An entirely unordered spliterator could be implemented slightly simpler.
You may implement a more efficient forEachRemaining method than the inherited default, e.g.
#Override
public void forEachRemaining(Consumer<? super Node> action) {
Node current = pendingNode;
if(current != null) {
pendingNode = null;
action.accept(current);
}
for(;;) {
current = pending.poll();
if(current == null) break;
traverseLocal(action, current);
}
}
private void traverseLocal(Consumer<? super Node> action, Node current) {
do {
action.accept(current);
Node child = current.getLeft();
if(child!=null) traverseLocal(action, child);
current = current.getRight();
} while(current != null);
}
but this method might cause stackoverflow errors, if your application has to deal with unbalanced trees (specifically, very long left paths in this example).

Add to end of a doubly linked list

I am really struggling in adding a node at the end of a double linked list, adding to the head was easy, now I really can't find a way to add to the end. It seems stupid but I'm losing a lot of time, so if anyone can help that would be appreciated. Here's my partial implementation in Java.
public class DoublyLinkedList implements Iterable<Node>, Iterator<Node> {
private Node head = new Node();
private Node tail = head;
private Node current;
private Node previous;
#Override
public Iterator<Node> iterator() {
current = head;
previous = null;
return this;
}
public void addToHead(Node node) {
Node next = head.getNext(null);
Node previous = head.getNext(next);
head.setNext(previous, node);
node.setNext(null, head);
head = node;
}
#Override
public boolean hasNext() {
return current != tail;
}
#Override
public Node next() {
Node tmp = previous;
previous = current;
current = current.getNext(tmp);
return previous;
}
And here's Node Class
public class Node {
Node next = null;
Node previous = null;
Node getNext(Node node){
if(node == next){
return previous;
}
if(node == previous){
return next;
}
throw new IllegalStateException("something went wrong");
}
void setNext(Node node, Node next){
if(node == previous){
previous = next;
}
else if(node == this.next){
this.next = next;
}
else{
throw new IllegalStateException("something went wrong");
}
}
}
I hope you will understand the code above, basically I will need the following :
public void addToEnd(Node node) {
// implementation
}
What interface are you aiming to implement on this double-linked list? The way you've implemented getNext and setNext is not a clear way of doing it. If you only have one node, and you attempt to setNext with a null value for node, there is no way of telling unless you look at the code where your new node is going to end up. Along with that, I'm not sure if there is a nice way for you to set the previous node of a new tail node with the way you've implemented it. It's very ambiguous.
Can I recommend that you instead implement getNext(), setNext(Node), getPrevious() and setPrevious(Node) methods in your Node class? This would greatly simplify and clear up your code.
You would then be able to implement your addToEnd(Node) method very simply.
public void addToEnd(Node node) {
node.setPrevious(tail);
tail.setNext(node);
tail = node;
}
If you want to be able to iterate through the list in both directions using 'getNext()' and 'hasNext()', you could provide a forward or reverse Iterator implementation. There are examples of how to create Iterators floating around. The first answer on this question has an example of a reverse iterator.
The basic idea is just the same as adding at the beginning of the list.
We can split it up into two parts:
adding the item to the list
updating the head/tail-node of the list
In pseudocode:
node to_insert
//step 1
tail.setNext(to_insert)
to_insert.setPrevious(tail)
//step 2
tail = to_insert

Confusion over how Java's Garbage Collector works (Nodes + Queue) [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
So I been trying to implement LinkedList, Stack, Queue in Java.
For each one i'm using a node class such that, now I don't really want to discuss how my implementation is since I am aware there are better ways to do it, I just want to focus on my question.
public class Node<E> {
private E data;
private Node<E> next;
private Node<E> prev;
public Node(E data) {
this.data = data;
this.next = null;
this.prev = null;
}
public E getData() {
return this.data;
}
public Node<E> getNext() {
return this.next;
}
public Node<E> getPrev() {
return this.prev;
}
public void setPrev(Node<E> prev) {
this.prev = prev;
}
public void setData(E data) {
this.data = data;
}
public void setNext(Node<E> next) {
this.next = next;
}
}
Now with the node class there, I keep getting mixed up on how the garbage collector works, so lets say this is my queue class
public class Queue<E> {
private int size;
private Node<E> head, tail;
public Queue() {
this.size = 0;
this.head = this.tail = null;
}
public Queue(E data) {
Node<E> temp = new Node<E>(data);
this.tail = this.head = temp;
this.size = 0;
}
public boolean enqueue(E data) {
Node<E> temp = new Node<E>(data);
if (this.head == null) {
this.tail = temp;
this.head = temp;
} else {
temp.setNext(this.head);
this.head.setPrev(temp);
this.head = temp;
}
this.size++;
return true;
}
public E dequeue() {
if (this.tail == null)
throw new IndexOutOfBoundsException();
else {
E data = this.tail.getData();
this.tail.setPrev(null);
this.tail = temp;
this.tail.setNext(null);
this.size--;
return data;
}
}
public int getSize() {
return this.size;
}
public E peak() {
if (this.tail == null)
throw new IndexOutOfBoundsException();
else
return this.tail.getData();
}
public boolean contains(E data) {
if (this.head == null)
return false;
else {
for (Node<E> cursor = this.head; cursor != null; cursor = cursor
.getNext()) {
if (cursor.getData().equals(data))
return true;
}
}
return false;
}
}
Now I am getting how the garbage collector works confused. I have heard, it will clean up any references that don't get pointed too. So I keep getting nullpointerexception on my dequeue class on the part that does the
this.tail.setNext(null);
now, hearing that for garbage collector to work, nothing can reference it, so I thought to myself my nodes are set up like this
head tail
null<-[1]-><-[2]-><-[3]->null
where each node can point to next and to previous, so for my dequeue I think I have to do few things
1) get the data (that is easy)
2) get a temp Node that points to previous
Node<E> temp = this.tail.getPrev()
3) now here is where I start to get lost, in order for each node to no longer be referenced, I have to get rid of all things pointer to it, so this means that I must set to null the
this.tail.setPrev(null);
since when I delete the node after that, I can't go backwards to erase that reference
head tail
null<-[1]-><-[2]-> null<-[3]->null
<-[temp]-> ( equals node [2])
4) Is set tail to point at temp node, which is what the prev node was
this.tail = temp;
no it should look like this
head tail
null<-[1]-><-[2]->(this still points to [3]) null<-[3]->null
5) but the second node still points to the memory address of [3], so i continue to
this.tail.setNext(null);
in order to make it so nothing at all references any spot of memory no longer in us,
head tail will be deleted by GC
null<-[1]-><-[2]->null null<-[3]->null
However, THIS PART gives me NullPointerException when there is only one node left in queue.
Now, I know I may be wrong on a lot of this, I am still learning, but I am jsut not sure how much stuff I have to do to each node to make sure garbage collector gets it so any help will do, do i need to set both prev and next to null? or only one? etc, so any help will be appreciated, thank you ;)
You don't really need to care about how the garbage collector works. If your list implementation is correct then the garbage collector will function correctly.
Your NullPointerException will be caused by a logic error. Nothing to do with garbage collection.
Your head and tail references in the queue should reference the first and last elements.
Each node should correctly point to previous and next elements. Your logic should recognise the beginning and end of the list and should correctly handle insert and deleting of nodes.
If you get that right from a functional point of view, then deleted nodes won't be referenced by anything and the garbage collector will clean it up.
Concentrate on writing unit tests for edge cases (empty lists, one node lists) and test the operations insert and delete.
Once it is functionally correct, the garbage collection will work ok.
EDIT:
In a long list, inner nodes will have a previous and last element, but the head and tail don't, so you need special logic to deal with deleting them.
If the list has one element, the head and tail are the same, so both the head and tail special logic will apply to that one node.
There is bug in your code. It has nothing to do with Garbage Collector.
You get NullPointerException because this.tail is null in your example when you have only one node in the queue. You assign temp = this.tail.getPrev(); which is null for one node only. Then you assign this.tail = temp;. Below you will find right implementation of dequeue().
You don't have to, but maybe some people would consider this a good practice to set everything to null in deleted node.
public E dequeue() {
if (this.tail == null)
throw new IndexOutOfBoundsException();
else {
E data = this.tail.getData();
Node<E> temp = this.tail;
this.tail = temp.getPrev();
if ( this.tail == null ) { // if that was last node
this.head = null;
return data;
}
this.tail.setNext(null);
temp.setPrev(null);
temp.setNext(null);
this.size--;
return data;
}
}
In the method enqueue() you check head for an emtpy queue. But in the method dequeue() you check tail for the same. It might be little confusing. You should probably check both for null. It's additional test of your program.
There is also a bug in constructor. this.size should be set to 1 not 0.
public Queue(E data) {
Node<E> temp = new Node<E>(data);
this.tail = this.head = temp;
this.size = 1;
}

Categories

Resources