Java LinkedList : remove from to to - java

I have a java.util.LinkedList containing data logically like
1 > 2 > 3 > 4 > 5 > null
and I want to remove elements from 2 to 4 and make the LinkedList like this
1 > 5 > null
In reality we should be able to achieve this in O(n) complexity considering you have to break chain at 2 and connect it to 5 in just a single operation.
In Java LinkedList I am not able to find any function which lets remove chains from linkedlist using from and to in a single O(n) operation.
It only provides me an option to remove the elements individually (Making each operation O(n)).
Is there anyway I can achieve this in just a single operation (Without writing my own List)?
One solution provided here solves the problem using single line of code, but not in single operation.
list.subList(1, 4).clear();
The question was more on algorithmic and performance. When I checked the performance, this is actually slower than removing the element one by one. I am guessing this solution do not actually remove an entire sublist in o(n) but doing that one by one for each element (each removal of O(n)). Also adding extra computation to take the sublist.
Average of 1000000 computations in ms:
Without sublist = 1414
With the provided sublist solution : = 1846**

The way to do it in one step is
list.subList(1, 4).clear();
as documented in the Javadoc for java.util.LinkedList#subList(int, int).
Having checked the source code, I see that this ends up removing the elements one at a time. subList is inherited from AbstractList. This implementation returns a List that simply calls removeRange on the backing list when you invoke clear on it. removeRange is also inherited from AbstractList and the implementation is
protected void removeRange(int fromIndex, int toIndex) {
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
As you can see, this removes the elements one at a time. listIterator is overridden in LinkedList, and it starts by finding the first node by following chains either by following links from the start of the list or the end (depending on whether fromIndex is in the first or second half of the list). This means that list.subList(i, j).clear() has time complexity
O(j - i + min(i, list.size() - i)).
Apart from the case when the you are better off starting from the end and removing the elements in reverse order, I am not convinced there is a solution that is noticeably faster. Testing the performance of code is not easy, and it is easy to be drawn to false conclusions.
There is no way of using the public API of the LinkedList class to remove all the elements in the middle in one go. This surprised me, as about the only reason for using a LinkedList rather than an ArrayList is that you are supposed to be able to insert and remove elements from the middle efficiently, so I thought this case worth optimising (especially as it's so easy to write).
If you absolutely need the O(1) performance that you should be able to get from a call such as
list.subList(1, list.size() - 1)).clear();
you will either have to write your own implementation or do something fragile and unwise with reflection like this:
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
for (int a = 0; a < 5; a++)
list.add(a);
removeRange_NEVER_DO_THIS(list, 2, 4);
System.out.println(list); // [0, 1, 4]
}
public static void removeRange_NEVER_DO_THIS(LinkedList<?> list, int from, int to) {
try {
Method node = LinkedList.class.getDeclaredMethod("node", int.class);
node.setAccessible(true);
Object low = node.invoke(list, from - 1);
Object hi = node.invoke(list, to);
Class<?> clazz = low.getClass();
Field nextNode = clazz.getDeclaredField("next");
Field prevNode = clazz.getDeclaredField("prev");
nextNode.setAccessible(true);
prevNode.setAccessible(true);
nextNode.set(low, hi);
prevNode.set(hi, low);
Field size = LinkedList.class.getDeclaredField("size");
size.setAccessible(true);
size.set(list, list.size() - to + from);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

To remove the middle elements in a single operation (method call) you could subclass java.util.LinkedList and then expose a call to List.removeRange(int, int):
list.removeRange(1, 4);
(Credit to the person who posted this answer then removed it. :)) However, even this method calls ListIterator.remove() n times.
I do not believe there is a way to remove n consecutive entries from a java.util.LinkedList without performing n operations under the hood.
In general removing n consecutive items from any linked list seems to require O(n) operations as one must traverse from the start index to the end index one item at a time - inherently - in order to find the next list entry in the modified list.

Related

add element to limited size list

I have the following method which adds an element to a size limited ArrayList. If the size of the ArrayList exceeds, previous elements are removed (like FIFO = "first in first out") (version 1):
// adds the "item" into "list" and satisfies the "limit" of the list
public static <T> void add(List<T> list, final T item, int limit) {
var size = list.size() + 1;
if (size > limit) {
var exeeded = size - limit;
for (var i = 0; i < exeeded; i++) {
list.remove(0);
}
}
list.add(item);
}
The "version 1"-method works. However, I wanted to improve this method by using subList (version 2):
public static <T> void add(List<T> list, final T item, int limit) {
var size = list.size() + 1;
if (size > limit) {
var exeeded = size - limit;
list.subList(0, exeeded).clear();
}
list.add(item);
}
Both methods works. However, I want to know if "version 2" is also more performant than "version 1".
EDIT:
improved "Version 3":
public static <T> void add(List<T> list, final T item, int limit) {
var size = list.size() + 1;
if (size > limit) {
var exeeded = size - limit;
if (exeeded > 1) {
list.subList(0, exeeded).clear();
} else {
list.remove(0);
}
}
list.add(item);
}
It seems you have the ArrayList implementation in mind where remove(0) imposes the cost of copying all remaining elements in the backing array, repeatedly if you invoke remove(0) repeatedly.
In this case, using subList(0, number).clear() is a significant improvement, as you’re paying the cost of copying elements only once instead of number times.
Since the copying costs of remove(0) and subList(0, number).clear() are identical when number is one, the 3rd variant would save the cost of creating a temporary object for the sub list in that case. This, however is a tiny impact that doesn’t depend on the size of the list (or any other aspect of the input) and usually isn’t worth the more complex code. See also this answer for a discussion of the costs of a single temporary object. It’s even possible that the costs of the sub list construction get removed by the JVM’s runtime optimizer. Hence, such a conditional should only be used when you experience an actual performance problem, the profiler traces the problem back to this point, and benchmarks prove that the more complicated code has a positive effect.
But this is all moot when you use an ArrayDeque instead. This class has no copying costs when removing its head element, hence you can simply remove excess elements in a loop.
Question 1: The problem is this line:
list = list.subList(exeeded, list.size());
You're reassigning the variable list which will not change to object passed as an argument but only its local counterpart.
Question 2: The sublist will (on an array list) still need to recreate the array at some point. If you don't want that you could use a LinkedList. But as a general rule the ArrayList will still perform better on the whole. Since the underlying array only has to be recreated when exceeding the maximum capacity it usually doesn't matter a lot.
You could also try to actually shift the array, move every element to the next slot in the array. That way you would have to move all elements when a new one is added but don't need to recreate the array. So you avoid the trip to the heap which is usually the biggest impact on performance.

Remove first N occurrence of an element from a List

Java 8 introduced Lambdas, which allow us to efficiently remove all elements from a List. The following removes all instances of 2 from myList.
List<Integer> myList;
...
myList.removeIf(x -> x==2);
If I wanted to remove N number of elements (in this case, three), I would use a for loop.
for (int i = 0; i < 3; i++) {
myList.remove(Integer.valueOf(2));
}
Is there a way to remove a specified number of elements from a List using Lambdas? If so, is it more efficient than the for loop code?
When you repeatedly call remove(Object) you get O(n²) time complexity from both, starting the search repeatedly from the beginning (applies to all List types) and from repeatedly copying the elements after the removed one, when the list is an ArrayList or similar.
The time complexity of the search can be avoided by using a dedicated search and remove loop, e.g. using an Iterator and its remove method. But the copying time complexity remains, unless you use removeIf and the list class overrides it with an appropriate implementation (as ArrayList does).
One way of utilizing this advantage for removing n matches would be
int n = 3;
int last = IntStream.range(0, myList.size())
.filter(ix -> myList.get(ix) == 2)
.limit(n)
.reduce((a,b) -> b)
.orElse(-1);
myList.subList(0, last + 1).removeIf(x -> x == 2);
It’s more complicated and for small lists, it will be more expensive. However, for really large lists where the time complexity matters, it will benefit from the O(n) time complexity.
Note that when the predicate is a simple match operation, you can also use, e.g. removeAll(Collections.singleton(2)) instead of removeIf(x -> x == 2).
Using lambdas:
Runnable runnable = () -> {
for (int i = 0; i < 3; i++) {
myList.remove(Integer.valueOf(2));
}
};
runnable.run();
Just kidding.
You can do something close to a for-loop:
IntStream.range(0,3).forEach(a-> integers.remove(Integer.valueOf(2)));
It should be equivalent to a for-loop in terms of performance.
A bit dirty, but requires just one pass:
public static <T extends Comparable<? super T>> List<T> removeFirstN(List<T> list, T t, int N) {
int[] a = {0};
return list.stream().filter(p -> !p.equals(t) || ++a[0] > N).collect(toList());
}

Handling duplicates when shuffling an array

I wish to shuffle an array with duplicate elements. Using the shuffle method from Collections, how does it handle duplicates? I don't want two duplicates swapping each other. Thanks
All that is guaranteed about the behavior of the method is in the Javadoc.
The current implementation chooses swaps randomly, without regard to the content of the list. The general contract states that the ideal is for all permutations to be equally likely, so I would not anticipate it ever going to an implementation that requires an element to move, much less requires that it be swapped with an element of differing value. In general that's what shuffling is - random order, which can just as well (some small percentage of the time) mean "the same order that came in (or an equivalent order)". And the shuffle() method addresses the general case.
If you need every element to be swapped with an element of differing value, you can of course write your own method to do that. Beware that a naive implementation could fall into an infinite loop if there are too many duplicates relative to the size of the collection.
This is the method for Collections#shuffle(List, Random) (A pastebin including the documentation can be found here:
#SuppressWarnings({"rawtypes", "unchecked"})
public static void shuffle(List<?> list, Random rnd) {
int size = list.size();
if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
for (int i=size; i>1; i--)
swap(list, i-1, rnd.nextInt(i));
} else {
Object arr[] = list.toArray();
// Shuffle array
for (int i=size; i>1; i--)
swap(arr, i-1, rnd.nextInt(i));
// Dump array back into list
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
ListIterator it = list.listIterator();
for (int i=0; i<arr.length; i++) {
it.next();
it.set(arr[i]);
}
}
}
This is the overloaded variant of Collections#shuffle(List).
The difference is that you can pass your own Random object if you want to seed it yourself.
As you can see it does not look at the values in each array slot. You could try to override this method and include a check for duplicates.
On a side note: Try checking the JavaDocs for these kind of questions. If you are unsure how a method works, just google the class + method name, or use the local Java source code on ur computer.

How to find a missing element between two linked lists in O(n)?

I have two Singly Linked Lists of Integer. One of them is a subset of another (the order of numbers is different). What is the best way (regarding performance) to find a number which the first list does contain and the second one does not?
My thought is first to sort them (using merge sort) and then just compare element by element.
So, it takes O(nlogn+mlogm+n), but a better O(n) soltuion should exist.
This is O(n) solution both in Time and Space.
Logic
Lets say the original Linked List has size N we'll call it LL1 and second Linked List as LL2.
=> Prepare a Hasmap of size N, key would be the numbers in the LL1 and value would be frequency in LL2
HashMap<Integer,Integer> map= new HashMap<Integer,Integer>();
=> Start traversing LL1 and set the frequency to 0 for all the NumbersBy the time all values in LL1 is iterated, you have all the Numbers present in HashMap with frequency = 0
map.put(key, 0);
=> Now start looping through the LL2, pick the numbers using them as key and increment the value by 1.By the time all values in LL2 is iterated, you have all the common numbers present in both LL1 and LL1 inside HashMap havingfrequency > 0
map.put(key, map.get(key) + 1);
=> Now start traversing the hasmap, searching for value = 0, when found, print the key as this number present only in LL1 and not in LL2
for (map.Entry<Integer,Integer> entry : map.entrySet())
{
if(entry.getValue() == 0)
System.out.println(entry.getKey());//This is a loner
}
2 Iterations and O(n) memory with O(n) time.
You can put both of them in different maps and then compare them. Putting in a map should be 2 single for loops of m & n and look up time for map is 1.
HashSet is the best data structure to use in this case.
With this code, you can achieve your results in O(n).
Let me know if you have more conditions, i can suggest something accordingly.
public class LinkedList {
private ListNode head;
public ListNode getHead() {
return head;
}
}
public class ListNode {
public int value;
public ListNode next;
ListNode(int value) {
this.value = value;
}
}
public class UtilClass{
public static int checkLists(LinkedList list1, LinkedList list){
ListNode head = myList2.getHead();
HashSet<Integer> hashSet = new HashSet<Integer>();
while(head!=null){
hashSet.add(head.value);
head = head.next;
}
head = myList.getHead();
while(head!=null){
boolean b = hashSet.add(head.value);
if(b == true) return head.value;
head = head.next;
}
return -1111;
}
}
You can use removeAll method. All you have to do is create a method that accepts two lists, one is the original and the other is the sublist, then, return a list of missing elements:
List getMissing(List original, List sub){
original.removeAll(sub);
return original;
}
This runs in quadratic time though.
If you really want to force it to run in linear time, O(n), then you have to write custom class that wrap your inputs such that for each input, there is a flag that monitors whether or not it has been added to the sublist. You can also design a class that facilitates addition and deletion of elements while monitoring the contents of both lists.
Let N = m+n.
Add the lists. As they are linked lists, this is cheap O(1).
Sort them O(N log N) - maybe better have used ArrayList.
Walk the list and on not finding a consecutive pair {x, x} you have found a missing one, O(N),
as the second list is a subset.
So O(N . log N).
As the lists are not ordered, any speedup consists of something like sorting, and that costs. So O(N.log N) is fine.
If you want O(N) you could do it as follows (simplified, using positive numbers):
BitSet present = new BitSet(Integer.MAX_VALUE);
for (int value : sublist)
present.set(value);
for (int value : list)
if (!present.isSet(value)) {
System.out.println("Missing: " + value);
break;
}
This trades memory against time. Mind this answer might not be accepted, as the memory is 2MAX_VALUE which to initialize/clear costs time too.
The possible < O(N log N) solutions
The most intelligent answer might be (quasi) sorting cooperatively both lists. And during the sort detect the missing element. Something like picking a haphazard "median" element and shifting shifting indices to split the lists, and divide and conquer.
If the list sizes differ by 1
Then you would only need to make the sums for every list, the difference being the missing value: O(N).
Works with overflow.

Java - matching two unordered lists

I have the following problem: I need to find pairs of the same elements in two lists, which are unordered. The thing about these two lists is that they are "roughly equal" - only certain elements are shifted by a few indexes e.g. (Note, these objects are not ints, I am just using integers in this example):
[1,2,3,5,4,8,6,7,10,9]
[1,2,3,4,5,6,7,8,9,10]
My first attempt would be to iterate through both lists and generate two HashMaps based on some unique key for each object. Then, upon the second pass, I would simply pull the elements from both maps. This yields O(2N) in space and time.
I was thinking about a different approach: we would keep pointers to the current element in both lists, as well as currentlyUnmatched set for each of the list. the pseudocode would be sth of the following sort:
while(elements to process)
elem1 = list1.get(index1)
elem2 = list2.get(index2)
if(elem1 == elem2){ //do work
... index1++;
index2++;
}
else{
//Move index of the list that has no unamtched elems
if(firstListUnmatched.size() ==0){
//Didn't find it also in the other list so we save for later
if(secondListUnamtched.remove(elem1) != true)
firstListUnmatched.insert(elem1)
index1++
}
else { // same but with other index}
}
The above probably does not work... I just wanted to get a rough idea what you think about this approach. Basically, this maintains a hashset on the side of each list, which size << problem size. This should be ~O(N) for small number of misplaced elements and for small "gaps". Anyway, I look forward to your replies.
EDIT: I cannot simply return a set intersection of two object lists, as I need to perform operations (multiple operations even) on the objects I find as matching/non-matching
I cannot simply return a set intersection of two object lists, as I need to perform operations (multiple operations even) on the objects I find as matching/non-matching
You can maintain a set of the objects which don't match. This will be O(M) in space where M is the largest number of swapped elements at any point. It will be O(N) for time where N is the number of elements.
interface Listener<T> {
void matched(T t1);
void onlyIn1(T t1);
void onlyIn2(T t2);
}
public static <T> void compare(List<T> list1, List<T> list2, Listener<T> tListener) {
Set<T> onlyIn1 = new HashSet<T>();
Set<T> onlyIn2 = new HashSet<T>();
for (int i = 0; i < list1.size(); i++) {
T t1 = list1.get(i);
T t2 = list2.get(i);
if (t1.equals(t2)) {
tListener.matched(t1);
continue;
}
if (onlyIn2.remove(t1))
tListener.matched(t1);
else
onlyIn1.add(t1);
if (!onlyIn1.remove(t2))
onlyIn2.add(t2);
}
for (T t1 : onlyIn1)
tListener.onlyIn1(t1);
for (T t2 : onlyIn2)
tListener.onlyIn2(t2);
}
If I have understood your question correctly, You can use Collection.retainAll and then iterate over collection that is been retained and do what you have to do.
list2.retainAll(list1);
All approaches based on maps will be O(n log(n)) at best, because creating the map is an insertion sort. The effect is to do an insertion sort on both, and then compare them, which is as good as it's going to get.
If the lists are nearly sorted to begin with, a sort step shouldn't take as long as the average case, and will scale with O(n log(n)), so just do a sort on both and compare. This allows you to step through and perform your operations on the items that match or do not match as appropriate.

Categories

Resources