My understanding of why arraylist is faster than a linkedlist is that with an arraylist you basically only need one action - update the reference at the end array element, whereas with a linked list you have to do much more e.g. create a new node, update 2 references, go through linked list and update the last node to point to the new one etc.
However I am not sure how java implement these. How does the arraylist know where the "last" element is, does it store a value of the last element or does it traverse the array and add a new element after the last?
And linked lists, do they store a reference to the last node in the list, or do they traverse the entire list to get to the end?
Look at the source:
ArrayList:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
LinkedList:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
An array-list is only faster at certain operations. If you add an element in the middle of an array the arraylist needs to copy basically all data into a new array. It is only if the arraylist already have allocated room for new data it is fast when inserting data where it is empty (usually in the end). Read/update by index is very-fast.
A LinkedList is fast when inserting since it never require the whole array to be copied. But accessing data in a linked list is slow since you need to "walk" all the elements until you are at the element you want to find.
You can always look at the sources of java.* classes.
However, answering particular your question: there is int field in ArrayList class, which contains current size of filled internal array area. When you add new value in ArrayList object, this field increments and then directly addresses to that element in internal array.
Related
So I have tried to make an insertion sort for doubly linked list with iterators. It returns a sorted list with the 'keys' sorted from smaller to bigger. Althought my code doesn't sort the actual list and i am not sure if it's an insertion sort. Can anyone help?
public static List sort(List list) {
List sortedList = new List(); // sortedList
List.Iter curIndex = List.Iter.last(sortedList); // terminated forward iterator
for(List.Iter iter = List.Iter.first(list); !iter.end(); iter.next()) {
curIndex = List.Iter.last(sortedList);
List.Node node = iter.key_data();
System.out.println("node: "+node.data);
System.out.println("curIndex: "+curIndex.key_data().data);
if (sortedList.empty()) {
sortedList.insAfter(curIndex, node.key, node.data);
}
else if (curIndex.key_data().key >= node.key) {
boolean hasPrev = true;
while (hasPrev && curIndex.key_data().key >= node.key) {
hasPrev = curIndex.prev();
}
sortedList.insAfter(curIndex, node.key, node.data);
}
else {
boolean hasNext = true;
while (hasNext && curIndex.key_data().key < node.key) {
hasNext = curIndex.next();
}
sortedList.insAfter(curIndex, node.key, node.data);
}
}
return sortedList;
}
Whether or not the definition of insertion sort would still consider your code to be an implementation of that, is probably a matter of interpretation. But in the more strict definition of insertion sort, the algorithm should move nodes, not create new nodes. This is what Wikipedia has as definition:
Insertion sort iterates, consuming one input element each repetition, and grows a sorted output list. At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there.
I highlight the word removes, which is an action your algorithm does not perform. The same article has a section specifically on linked lists, where it states (my highlights):
If the items are stored in a linked list, then the list can be sorted with O(1) additional space. The algorithm starts with an initially empty (and therefore trivially sorted) list. The input items are taken off the list one at a time, and then inserted in the proper place in the sorted list. When the input list is empty, the sorted list has the desired result.
Your implementation needs O(n) space. It would be more in line with the spirit of insertion sort, if it would do as described in that quote: instead of creating new nodes, it could take the original node object out of the original list, and rewire it so it becomes part of the newly sorted list, such that the whole process does not ever create new nodes.
I created Linked List from scratch, and added methods such as add, remove, set, size, etc. I've also added a simple static and recursive sort method, which accepts a Linked List reference as parameter, so that it can be used in the Main class being called as sort(linkedList); and it returns a sorted linked list.
The program throws Exception in thread "main" java.lang.StackOverflowError, at lines, if (biggest.compareTo(nextNode.value) < 0) biggest = nextNode.value; and return sort(list);. I want the sort method to sort the list in alphabetical order (my Linked List consists of String elements).
This is the method in code:
/**
* The sort method sorts the list in alphabetical order
* #param list list to be sorted
* #return sorted linked list
*/
static DD_RecursiveLinkedList sort(DD_RecursiveLinkedList list) {
DD_Node nextNode = head.next;
String biggest = head.value, smallest = tail.value; //by default biggest is the head, and smallest is the tail
if (isEmpty()) return null; //if list is empty, return null
do { //find the biggest and smallest value in the list
if (biggest.compareTo(nextNode.value) < 0) biggest = nextNode.value; //if nextNode is bigger than the biggest, biggest is nextNode
if (smallest.compareTo(nextNode.value) > 0) smallest = nextNode.value; //if nextNode is smaller than the smallest, smallest is nextNode
nextNode = nextNode.next; //update nextNode
} while (nextNode!=null); //loop until nextNode is null
set(0, biggest); set(size()-1, smallest); //set biggest as the head of the list, and smallest as the tail
// remove(biggest);//remove the biggest (head) from the list
// remove(smallest); //remove the smallest (tail) from the list
// list.add(0, biggest); //add the biggest to the sorted list as head element
// list.add(size()-1, smallest); //add the smallest to the sorted list as tail element
return sort(list); //sort the order of sorted list recursively
}
I've commented out the add and remove lines, because they were included in the error so instead of add and remove methods, I've used the set method, to replace the element at the specified index with the specified element.
The problem here is , the sort(list) method is going to be called infinite number of time, causing the java stack to overflow and impossible to store any variables in stack further.
return sort(list);
1.As there is no restriction saying at what conditions recursion should. Stop. It will keep on calling sort (). - this leading to stackOverflow error
2.Since the remove() statements are commented. List size is not altered at all .So it will not stop at isEmpty check also. Also: bcoz whatever you list is it’s finally going to return null always.
I'm having trouble understanding how this method below removes the duplicates in the linked list. After calling this method, all duplicates are successfully removed. Why is the head not null? Wouldnt the head node be null because the current variable in the method iterated to the end. How does this method successfully update the list to get rid of the duplicate items?
static void removeDuplicate(node head)
{
// Hash to store seen values
HashSet<Integer> hs = new HashSet<>();
node current = head;
node prev = null;
while (current != null)
{
int curval = current.val;
// If current value is seen before
if (hs.contains(curval)) {
prev.next = current.next;
} else {
hs.add(curval);
prev = current;
}
current = current.next;
}
}
Elements get removed by changing the pointer from the previous element to point to the next element. That is how you remove elements in LinkedLists, by skipping them. The garbage collector will later remove the object as no object refers to it anymore.
Here is an illustration of the removal:
Duplicates are identified by memorizing each encountered value in a HashSet. If you find an element which was already encountered before (i.e. contained in the set), it is a duplicate.
The head can not get null because it was not null previously and duplicates can only occur after the first element, since you need to encounter an element at least once until you can find a duplicate. For example a list like [1, 1, 1] gets modified to [1] and not to [].
Also the variable head gets not changed in the method, it points to the head-node before and after the method. It seems that you got confused by current = head but you need to know that this, in Java, does not synchronize both variables. If the method changes current this changes are not reflected by head. The statement just means 'let current point to where head points' and afterwards you let current point elsewhere.
I am trying to reverse a linked list which works, but when I try to print the original, it fails (only prints the head). My question is, why would reversing affect the original. Below is my code. LinkedList is my own class, so is Node. Before reversing, if I try to print my list, that works.
public static void main(String[] args) {
LinkedList list;
...
Node head = list.getHead();
Node rev = reverse(head);
Node temp = rev;
while (temp != null) {
System.out.println(temp);
temp = temp.next;
}
temp = head;
while (temp != null) {
System.out.println(temp);
temp = temp.next;
}
}
private static reverse(Node head) {
// Reversing the linked list
}
EDIT::
This seems to be a Java thing. Java passes an object by reference. When I pass head as a parameter, it's passed by reference and any change made to it is reflected in the calling function.
Doing Node h = head and then passing h as a parameter won't work either since h will be the same object as head.
The only option I can think of is to create a new object, copy the linked list over and pass that as a parameter.
My question becomes, is there a better solution?
To understand it, picture your list looks like this
list (list.head =a) --> a (a.next=b) --> b (b.next= c) -> c (c.next = null)
If you get the head, then you are getting object 'a'.
Then you are modifying object 'a'.
So you can see you are editing the list by doing this.
What you need to do is:
Get the head
Create a copy
Reverse the copy
Get the next item
Copy it
Reverse it
Join it to the last
And so on
Since you are using your own classes not java collections classes, the easiest way is for you to make sure that reverseNode() only edits a copy of the one you pass it, and returns the copy.
First make sure your Node class has a constructor that copies another Node, then do something like this:
private static Node reverse(Node original)
{
Node retval = new Node(original);
// or you could use clone () as Bhavik suggested, if your Node class implements it
// modify retval
// I haven't shown code to reverse it as I assume you already have that and you didnt specify if it was a bidirectional list or just one direction.
return retval;
}
Or you might add a static method in your Node class that constructs a new node that is reversed:
static Node createReverse(Node n)
{
return new Node(n.data,n.next,n.prior);
}
Or a non static method of the node class which returns a reversed copy of itself;
Node createReverse()
{
return new Node(this.data,this.next,this.prior);
}
But you should consider this can get very ugly because your copies will still have pointers pointing into the existing list!
A better technique might be to create a new empty list, and then start from the end of your original, make a copy, and add it to the start of your new list.
You can use recursion to do this but might easily run out of memory.
But rather than do this manually, you might look at the java.util packages and switch to using one of their LinkedList and list item types. These classes have already solved all the problems with doing this stuff.
Then you could (if you need to keep the original list unmodified):
- Make a copy of your entire list.
- reverse the copy as below
If you don't care about keeping the original, then just use this method(below) on your list, no need then to make a copy.
From java.util.Collections:
Collections.reverse(List a_list);
The Collections class will choose an efficient way to reverse the list, depending on whether it is bidirectional, single directional, etc.
Chunko's answer is right in that you should be creating copies of your Nodes instead of just passing references to them.
However, I think you might be overthinking this:
LinkedList reversedList = new LinkedList();
for (int i = originalList.size()-1; i >= 0; i--) {
reversedList.add(list.get(i));
}
Java's LinkedList doesn't have a getHead() method, so I guess you're using some homework-related custom implementation of a linked list. Still, your implementation should have an add() method that appends a new Node to the list, as well as a get(int) method that returns the item in a given position.
You can use those methods to create a new list and populate it with the original list's items in reversed order.
Essentially what this problem does is take in a Linked List as a parameter and subtracts its contents from the calling Linked List if the same contents exist in both. I have to do it this way (so no changing the parameters).
For instance: l1.subtractList(l2) would subtract the contents of l2 from l1.
The trouble here is that the calling Linked List has 2 of the same number and that number is also in the Linked List passed as a parameter. I only have to remove one instance of it as well.
I've managed to subtract everything but that duplicate number, but I'm not sure what I'm doing wrong. Keep in mind this is a very new subject to me, so I may be way off base. But I appreciate any and all help you may offer. Thanks.
public void subtractList(LinkedList list)
{
Node current = head;
Node<Integer> temp = list.getFirst();
Integer count = -1;
while (current != null)
if (current == temp){
count++;
list.listRemove(count);
temp = list.getFirst();
}
else
{
current = current.getNext();
}
}
What is listRemove method? Why do you need count? Just traverse the argument list and check if its element temp exists in the calling Linked List. You will need an outer loop traversing the list passed as argument and an inner loop iterating over the calling list to check the value of the element needing to be removed and to remove it if required
while(temp!=null)
{
while(current!=null){
//Check if element exists in list
//If yes, remove it from the calling list
}
//Repeat
temp = temp.getNext();
}