MergeSort IllegalArgumentException - java

So I'm learning about MergeSort right now to solve a problem on Project Euler.
I am trying to sort a list 5163 names alphabetically. I did some research and found that MergeSort would be a fairly efficient way of doing so.
So I coded up an implementation based off of this link.
Below are my mergeSort and merge methods.
public static List<String> mergeSort(List<String> list){
if (list.size() == 1){ //if fully divided
return list;
}
List<String> leftSide = list.subList(0, list.size()/2);
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
leftSide = mergeSort(leftSide);
rightSide = mergeSort(rightSide);
return merge(leftSide, rightSide);
}
public static List<String> merge(List<String> listA, List<String> listB){
List<String> listC = new LinkedList<String>();
while (!listA.isEmpty() & !listB.isEmpty()){ //while both have elements
if (listA.get(0).compareTo(listB.get(0)) > 1){ //if stringA is greater than stringB
listC.add(listA.get(0));
listA.remove(0);
} else { //if stringB is greater than stringA
listC.add(listB.get(0));
listB.remove(0);
}
}
while (!listA.isEmpty()){ //while listA has elements
listC.add(listA.get(0));
listA.remove(0);
}
while (!listB.isEmpty()){ //while listB has elements
listC.add(listB.get(0));
listB.remove(0);
}
return listC;
}
The problem is that when I try and use the mergeSort method with my list of names, it gives me the following error:
Exception in thread "main" java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
Which is pointing at this line:
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
I don't really understand why it is happening. It seems to me that I have used the exact same method as shown in the tutorial.
Also, from what I understand, this error is telling me that the size of my list is zero. This would be the only way in which list.size()/2 + 1 could equal one and list.size() could equal zero. However, I don't see why my list could be zero. Because it is going to be divided until the size is one, and then it will be returned.
The only way I could see it equalling zero was if it was zero to begin with, but I have confirmed that my list.size() is 5163 to start with.
Could someone help me point out what I am doing wrong? I am sure it is something really simple, but since I have just started learning this, I am not sure what it is.
EDIT:
I check the list's size here:
System.out.println(namesArray.size()); //prints "5163"
namesArray = mergeSort(namesArray);
So how could my list ever have a size of zero?

The only way I could see it equalling zero was if it was zero to begin with, but I have confirmed that my list.size() is 5163 to start with.
There is a second way. Consider what happens deep into your recursion when on entry to mergeSort the input sublist contains two elements.
List<String> leftSide = list.subList(0, list.size()/2);
The above becomes list.sublist(0,1). Since the ending index is exclusive, the sublist contains one element. Then:
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
This becomes list.sublist(2,2). Again, since the ending index is exclusive, this creates a list of length zero, or an emtpy list. Now when you reach the recursive calls
leftSide = mergeSort(leftSide);
rightSide = mergeSort(rightSide);
the left side recursion works but the right side recursion passes a zero-length list. At the top of mergeSort you have
if (list.size() == 1){ //if fully divided
return list;
}
which checks for the terminating condition of list size 1, but does not stop the code from attempting to process an empty zero-length list. This is what causes your exception immediately after this point.
If you changed the test to if (list.size() <= 1) you'd have prevented the exception, but would still have a problem since some of the elements would not have been sorted due to the ending index problem.
The correct solution is to change one line:
List<String> rightSide = list.subList(list.size()/2 + 1, list.size());
^^^
Remove this----------------------------------^

If you have an empty list, you call
List<String> rightSide = list.subList(0/2 + 1, 0);
So your from index is bigger than the toIndex. And that raises the exception.
Form the javadoc:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. The returned list supports all of the optional list operations supported by this list.
This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole list. For example, the following idiom removes a range of elements from a list:
list.subList(from, to).clear();
Similar idioms may be constructed for indexOf and lastIndexOf, and all of the algorithms in the Collections class can be applied to a subList.
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)
Parameters:fromIndex - low endpoint (inclusive) of the subList
toIndex - high endpoint (exclusive) of the subListReturns:a view of the specified range within this list
Throws:IndexOutOfBoundsException - for an illegal endpoint index value (fromIndex < 0 || toIndex > size || fromIndex > toIndex)
You have to check if the list is empty.

Related

Remove elements from a List at a specific index

I am trying to program a method that deletes the first, second and third element of every group of 4 elements.
It seems not working at all.
Could anyone please help?
public static void reduziereKommentare(List<String> zeilen) {
if (!zeilen.isEmpty()) {
if (zeilen.size() % 4 != 0) {
throw new RuntimeException("Illegal size " + zeilen.size() + " of list, must be divisible by 4.");
}
for (int i = 1; i <= zeilen.size() % 4; i++) {
zeilen.remove(i);
zeilen.remove(i + 1);
zeilen.remove(i + 2);
}
}
System.out.println(zeilen);
}
As said in the comments, removing an element impacts the indexing. Whenever I need to do something like this, I either use an Iterator, or loop backwards.:
for (int i = zeilen.size() - 4; i >= 0; i -= 4) {
zeilen.remove(i + 2);
zeilen.remove(i + 1);
zeilen.remove(i);
}
Note that I subtract 4 from i each iteration, so I go back a full block of four each time.
Also note that I remove the largest indexed elements first. If I use i, i + 1 and i + 2 inside the loop, I again run into the same issue. I could also have used i 3 times, but this makes it more clear.
My take...does not require the size precondition check but you may want to still catch that if it represents an error of broader scope than this method.
Given this test code...
// Test code
List<String> myList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
myList.add(String.valueOf(i));
}
the 'zeilen' loop can be implemented as ...
// "before" diagnostics
System.out.println(zeilen);
// The 'zeilen' loop
for (int i = 0, limit = zeilen.size(); i < limit; i++) {
if ((i+1) % 4 > 0) zeilen.remove(i/4);
}
// "after" diagnostics
System.out.println(zeilen);
and produces
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[3, 7, 11, 15, 19]
Works with any length list leaving every '4th' element in list.
A few more test cases :
Given Results in
[] []
[0,1] []
[0,1,2,3] [3]
[0,1,2,3,4] [3]
[0,1,2,3,4,5,6,7] [3,7]
[0,1,2,3,4,5,6,7,8] [3,7]
Would it not be easier to simply add every fourth item to a new list and return that? This would also eliminate any repetitive copying that could be involved when removing elements from a list. And the target list can be appropriately sized to start.
public static List<String> reduziereKommentare(List<String> zeilen) {
Objects.requireNonNull(zeilen);
List<String> zeilen1= new ArrayList<>(zeilen.size()/4);
for(int i = 3; i < zeilen.size(); i+=4) {
zeilen1.add(zeilen.get(i));
}
return zeilen1;
}
You could also use a stream.
zeilen = IntStream.iterate(3, i ->i < zeilen.size(), i->i+=4)
.mapToObj(zeilen::get).toList();
Notes:
whether the list is empty or the size is not divisible by 4, this will work. It will just ignore the extra elements.
assigning the result to the original variable will result in the old list being garbage collected.
I only check for a null argument since that would cause an exception. Of course, if alerting the user of the size is important just add the other check(s) back in.
Your code sample uses a data type of List - List<String> zeilen - but you separately wrote a comment which states that you're starting from an array:
"I used the Arrays.asList() function to add elements to the list"
The signature for asList() shows the input argument is an array, defined using varargs:
public static <T> List<T> asList(T... a)
Thus, you would start from something like this:
// rely on automatic array creation via varargs
List<String> list = Arrays.asList("one", "two", "three");
or from an explicit array, like this:
String[] strings = {"one", "two", "three"};
List<String> list = Arrays.asList(strings);
Here's a more complete picture of your current solution:
start with an array – String[] – creating it explicitly or relying on automatic array creation via varargs
create a List<String> from that array using Arrays.asList()
traverse the List skipping three items at a time, keeping only each fourth item (so: 4th, 8th, 12th, 16th, etc.)
Since the starting point is a String array, and knowing that you're
interested in keeping only every 4th element,
you could:
create a new, empty java.util.List<String>
iterate over each element of the array
for every 4th, 8th, etc element, add that to the final result list; ignore everything else
Here's the code to do that:
private static List<String> buildListOfEveryFourthElement(String[] array) {
List<String> everyFourthElement = new ArrayList<>();
if (array != null) {
// start from "1", a bit easier to reason about "every 4th element"?
int current = 1;
for (String s : array) {
if (current > 1 && current % 4 == 0) {
everyFourthElement.add(s);
}
current++;
}
}
return everyFourthElement;
}
I omitted the check for whether the input is exactly divisible by 4, but you could easily edit the first if statement
to include that: if (array != null && array.length % 4 == 0) { .. }
A benefit to this "build the List as you go" approach (vs. calling Arrays.asList() with a starting array)
is that the original input array would not be associated in any way with the result list.
So what? As you mentioned in one of your comments that you discovered it's not permissible
to modify the list – calling .remove() will throw java.lang.UnsupportedOperationException.
Note this will also happen if you try to add() something to the list.
Why does it throw an exception?
Because asList() returns a java.util.List which is backed by the input array, meaning the list and array are
sort of tied together. If it allowed you to remove (or add) items from (or to) the
list then it would also have to automatically update the backing array, and they didn't implement it that way.
Here's a brief snip from asList() Javadoc:
Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.)
By creating a new List and populating it along the way, you are free to modify that list later in your code
by removing or adding elements, sorting the whole thing, etc. You would also be guarded against any changes to the array
showing up as (possibly surprising) changes in the list – because
the list is backed by the array, a change in an array element would be visible in the associated list.

Printing every possible sub-list of a list using recursion

I am having trouble solving this recursion problem. Recursion is quite difficult to understand and be able to code as I am new to coding. The problem is to write a recursive method to find every possible sub-list of a given list. Your method should accept a list of strings as a parameter and print every sub-list that could be created from elements of that list, one per line. Assume there is no duplicates and the list is not null. Do not use any loops.
The only possible way I can think of doing this is with a for loop or use more parameters but I can't per instructions. This is what I have so far. I checked the list api it says there is a subList method you can use. I was able to print the first 5 possible sublists just by substracting -1 from the list size every recursion and then I get an index error. This is very frustrating so if anyone has any tips or pointers that would greatly be appreciated.
If you can possibly solve it with loops, I'd love to see how you would solve it.
public static void main(String[]args){
ArrayList<String> list = new ArrayList<>(List.of("Janet", "Robert", "Morgan", "Char"));
subsets(list);
}
public static void subsets(List<String> list) {
int n = list.size();
if(list.isEmpty()){
System.out.println(list);
}
if(n > 0){
System.out.println(list.subList(0 , n));
}
subsets(list.subList(0,n -1));
}
Results of my code
The best solution I came up with is based on randomness, I'll post in even though it is not what is expected by the Java Programming textbook.
You can calculate how many distinct k-combinations of K elements exists in a list of N elements.
For example:
One combination of 4 elements exists in a list of 4
4 combinations of 3 elements exist in a list of 4.
The idea is to have as args of the recursive method:
The initial list you want to extract sublists
A list of every sublist already printed
The number K of elements of the wanted sublist size
You should then have the following method signature:
public static void subsets(List<String> list, ArrayList<List<String>> alreadyPrinted, int nbOfElementsInTheSubList);
and the call in your main method will be
subsets(list, new ArrayList<>(), list.size());
Now in the body of the recursive method, process as follow (pseudo-code)
Pick a sublist of nbOfElementsInTheSubList random elements from list that is not in alreadyPrinted, print it, and add it to alreadyPrinted
compute combinationNumber = list.size() choose nbOfElementsInTheSubList (ie: the number of nbOfElementsInTheSubList-combination in list)
compare it to alreadyThere, the number of combination of nbOfElementsInTheSubList elements presents in alreadyPrinted
if alreadyThere = combinationNumber : You have all the nbOfElementsInTheSubList-Combination available in list, you can call recursively your method using (nbOfElementsInTheSubList - 1) as the last arg
else : You are missing at least one of the nbOfElementsInTheSubList-Combination available in list. Call subset again using the same nbOfElementsInTheSubList but with the updated alreadyPrinted
I doubt this is an optimal solution, so I bookmarked your topic since I am sincerely curious about the expected code.
If we want to permutate all the value in the list then we can use this code->
public static void main(String[] args) {
List<String> list = Arrays.asList("Janet", "Robert", "Morgan", "Char");
recursiveprint(list, new boolean[list.size()], "");
}
private static void recursiveprint(List<String> list, boolean b[], String s) {
System.out.println(s);
for (int j = 0; j < list.size(); j++) {
if (b[j] == false) {
b[j] = true;
recursiveprint(list, b, s + list.get(j)+" ");
b[j] = false;
}
}
}

how to merge two sorted arrays of same size?

1st array: {3,5,6,9,12,14,18,20,25,28}
2nd array: {30,32,34,36,38,40,42,44,46,48}
Sample Output:
{3,5,6,9,12,14,18,20,25,28,30,32,34,36,38,40,42,44,46,48}
I have to merge 1st array into second array. 2nd array has space to accomadate all values
Whenever i know i need a collection to be sorted, i use a method that will insert new elements in right place, so the collection will never have state when its not sorted... in your case you might be good with adding two into destination collection then use Collections.sort() but you can do sortedInsert() as well... you can create your collection and start addin all items into it using this method, and when you finished you dont need another call to Collections.sort() because collection is always in sorted state... This is handy if you often do single element update and dont want whole collection to be resorted... this will work with much better performance...
Here is what i do for List
/**
* Inserts the value keeping collection sorted, provided collections shall be sorted with provided
* comparator
*/
public static <E> void sortedInsert(List<E> list, E value, Comparator<? super E> comparator) {
assert Ordering.from(comparator).isOrdered(list);
if (list.size() == 0) {
list.add(value);
} else if (comparator.compare(list.get(0), value) > 0) {
list.add(0, value);
} else if (comparator.compare(list.get(list.size() - 1), value) < 0) {
list.add(list.size(), value);
} else {
int i = 0;
while (comparator.compare(list.get(i), value) < 0) {
i++;
}
list.add(i, value);
}
}
Use System.arraycopy to append a1 to a2
System.arraycopy(a1, 0, a2, a2_len, a1.length);
then Arrays.sort
Use two pointers/counters, i & j starting from 0 to size of the array. Compare a[i] & b[j] and based on the result shift i or j (similar to merge sort, merging step). If extra space isn't allowed then in worst case (which is true in your input, all the elements in first array is smaller than first element in second array) you might have to shift 2nd array every time you compare elements.
Make a ArrayList object as arrayListObject
ArrayList<Integer> arrayListObject= new ArrayList<>();
Add elements of both arrays in that arrayListObject
Do Collectios.sort(arrayListObject) to sort the elements.
Use
Integer list2[] = new Integer[arrayListObject.size()];
list2 = arrayListObject.toArray(list2);
to get the resulted array

IndexOutOfBoundsException when adding to ArrayList at index

I get exception Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 for the below code. But couldn't understand why.
public class App {
public static void main(String[] args) {
ArrayList<String> s = new ArrayList<>();
//Set index deliberately as 1 (not zero)
s.add(1,"Elephant");
System.out.println(s.size());
}
}
Update
I can make it work, but I am trying to understand the concepts, so I changed declaration to below but didnt work either.
ArrayList<String> s = new ArrayList<>(10)
ArrayList index starts from 0(Zero)
Your array list size is 0, and you are adding String element at 1st index. Without adding element at 0th index you can't add next index positions. Which is wrong.
So, Simply make it as
s.add("Elephant");
Or you can
s.add(0,"Elephant");
You must add elements to ArrayList serially, starting from 0, 1 and so on.
If you need to add elements to specific position you can do the following -
String[] strings = new String[5];
strings[1] = "Elephant";
List<String> s = Arrays.asList(strings);
System.out.println(s);
This will produce the sollowing output
[null, Elephant, null, null, null]
Your ArrayList is empty. With this line:
s.add(1,"Elephant");
You are trying to add "Elephant" at index 1 of the ArrayList (second position), which doesn't exist, so it throws a IndexOutOfBoundsException.
Use
s.add("Elephant");
instead.
ArrayList is not self-expandable. To add an item at index 1, you should have element #0.
If you REALLY want "Elephant" at index 1, then you can add another (e.g. null) entry at index 0.
public class App {
public static void main(String[] args) {
ArrayList<String> s = new ArrayList<>();
s.add(null);
s.add("Elephant");
System.out.println(s.size());
}
}
Or change the calls to .add to specify null at index 0 and elephant at index 1.
add(int index, E element) API says, Your array list has zero size, and you are adding an element to 1st index
Throws:
IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())
Use boolean add(E e) instead.
UPDATE based on the question update
I can make it work, but I am trying to understand the concepts, so I
changed declaration to below but didnt work either.
ArrayList<String> s = new ArrayList<>(10)
When you call new ArrayList<Integer>(10), you are setting the list's initial capacity to 10, not its size. In other words, when constructed in this manner, the array list starts its life empty.
For Android:
If you need to use a list that is going to have a lot of gaps it is better to use SparseArray in terms of memory (an ArrayList would have lots of null entries).
Example of use:
SparseArray<String> list = new SparseArray<>();
list.put(99, "string1");
list.put(23, "string2");
list.put(45, "string3");
Use list.append() if you add sequential keys, such as 1, 2, 3, 5, 7, 11, 13...
Use list.put() if you add non-sequential keys, such as 100, 23, 45, 277, 42...
If your list is going to have more than hundreds of items is better to use HashMap, since lookups require a binary search and adds and removes require inserting and deleting entries in the array.
You can initialize the size (not the capacity) of an ArrayList in this way:
ArrayList<T> list = new ArrayList<T>(Arrays.asList(new T[size]));
in your case:
ArrayList<String> s = new ArrayList<String>(Arrays.asList(new String[10]));
this creates an ArrayList with 10 null elements, so you can add elements in random order within the size (index 0-9).
Don't add index as 1 directly in list
If you want to add value in list add it like this
s.add("Elephant");
By default list size is 0
If you will add any elements in list, size will increased automatically
you cant add directly in list 1st index.
//s.add(0, "Elephant");
By the way, ArrayList<String> s = new ArrayList<>(10);
set the initialCapacity to 10.
Since the capacity of Arraylist is adjustable, it only makes the java knows the approximate capacity and try to avoid the performance loss caused by the capacity expansion.

linked-list interview question

This was the question asked in interview.Can anybody help me out it in implementing in java
Given 2 linked lists(which need not have unique elements) find intersection point in the form of Y.(it can be anywhere--middle or tail)
If the length of the lists is O(N), this solution is O(N) with O(1) space.
FUNCTION length(LIST list) : INT
// returns number of nodes until the end of the list
FUNCTION skip(LIST list, INT k) : LIST
// return the sublist of list, skipping k nodes
FUNCTION intersection(LIST list1, list2) : LIST
// returns the sublist where the two lists intersects
INT n1 = length(list1);
INT n2 = length(list2);
IF (n1 > n2) THEN
list1 = skip(list1, n1-n2)
ELSE
list2 = skip(list2, n2-n1)
WHILE (list1 != list2)
list1 = skip(list1, 1)
list2 = skip(list2, 1)
RETURN list1
Essentially, traverse each lists to find how many nodes there are. Then, skip enough elements from the longer list so that now you have lists of the same length. Then, in-sync, move forward step-by-step until the two lists meet.
http://java.sun.com/docs/books/tutorial/collections/interfaces/set.html
I don't have a good example at the moment, but I believe he's referring to this:
"The intersection of two sets is the set containing only the elements common to both sets."
Where 'sets' can also be Lists, etc.
Java has built in methods for this.
Say you have a List list, you would do something like this:
list.removeAll(list2);
or
list.retainAll(list2);
retainAll will you give you the 'intersection', removeAll gives you the difference between the two lists.
According to #Lord Torgamus question, here is a suggested java algorithm.
Suppose you have two java Collection objects (and LinkedList, as an implementor of List, is also an implementor of Collection). To find their intersection, you only have to call Collection#retainAll method on the first object giving the second one as an argument.

Categories