I am trying to implement the top-down merge sort algorithm in Java, using the pseudocode from Wikipedia.
My problem is that my code sometimes throws a StackOverflowError, but not always. I have checked that my code matches the pseudocode several times and cannot find what is wrong with it.
Here is my Java code:
import java.util.ArrayList;
import java.util.Random;
public class Main {
public static void main(String[] args) {
Random r = new Random();
ArrayList<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i <= 15; i++) {
numbers.add(r.nextInt(100));
}
numbers = mergeSort(numbers);
System.out.println(numbers);
}
public static ArrayList<Integer> mergeSort(ArrayList<Integer> m) {
if (m.size() <= 1) {
return m;
}
ArrayList<Integer> left = new ArrayList<Integer>();
ArrayList<Integer> right = new ArrayList<Integer>();
for (Integer x : m) {
if (m.indexOf(x) < (m.size()) / 2)
left.add(x);
else {
right.add(x);
}
}
left = mergeSort(left);
right = mergeSort(right);
return merge(left, right);
}
private static ArrayList<Integer> merge(ArrayList<Integer> l, ArrayList<Integer> r) {
ArrayList<Integer> result = new ArrayList<Integer>();
while (l.size() > 0 && r.size() > 0) {
if (l.get(0) <= r.get(0)) {
result.add(l.get(0));
l.remove(0);
}
else {
result.add(r.get(0));
r.remove(0);
}
}
while (l.size() > 0) {
result.add(l.get(0));
l.remove(0);
}
while (r.size() > 0) {
result.add(r.get(0));
r.remove(0);
}
return result;
}
}
Your algorithm encounters issues when there are duplicate elements, as indexOf will only return the index of the first one. Use a index-based for loop instead. Demo
for (int i = 0; i < m.size(); i++) {
if (i < (m.size()) / 2)
left.add(m.get(i));
else {
right.add(m.get(i));
}
}
In the mergeSort method need to change the for loop little bit and try again.
for (int i=0;i< m.size()/2;i++)
left.add(m.get(i));
for (int i=m.size()/2;i< m.size();i++)
right.add(m.get(i));
Related
I am trying to delete the odd positions starting from 1 continuously and to get the last remaining element;
e.g.:
n=6;
1 2 3 4 5 6
first: removing odd indices will get (2 4 6);
second: removing odd indices will get (4) which is the answer...
here is my code:
import java.util.HashMap;
public class Odd_Deletions {
public static void oddDeletions(HashMap<Integer, Integer> hm) {
int j = 1;
for (int i = 1; i < hm.size(); i++) {
if (hm.get(i) % 2 != 0) {
continue;
} else {
hm.put(j, i);
j++;
}
}
//System.out.println(hm);
while (true) {
if (hm.size() == 1) {
System.out.println(hm);
break;
} else
oddDeletions(hm);
}
}
public static void main(String args[]) {
int n = 6;
HashMap<Integer, Integer> hm = new HashMap<>();
for (int i = 1; i <= n; i++) {
hm.put(i, i);
}
//System.out.println(hm);
oddDeletions(hm);
}
}
why I am getting StackOverflow Error and what is wrong with this logic?
can anyone fix it?
Thanks and regards;
Perhaps the HashMap is not the right class to use here. Anyway, as #Welbog pointed out, you never remove anything from your table. Also, why use recursion?
Try something like this:
while (hm.size() > 1) {
int j = 1;
int last = hm.size();
for (int i = 1; i <= last; i++) {
int value = hm.remove(i);
if (i % 2 == 0) {
hm.put(j, value);
j++;
}
}
}
There are 3 possible solutions.
(1) I don't know why you need to apply that "removal process" on the HashMap with that logic, anyway a possible solution could be the following one. But use it only if you need to apply to a Map where you need to remove its entries by, for some reasons, applying that logic on the Map keys.
public class RemovingOddIndexes {
public static void main(String[] args) {
// initialize
int n = 6;
HashMap<Integer, Integer> hm = new HashMap<>();
for (int i = 1; i <= n; i++) {
hm.put(i, i);
}
//
oddDeletions(hm);
// print result
hm.forEach((k, v) -> System.out.println(String.format("%s:%s, ", k, v)));
}
public static void oddDeletions(HashMap<Integer, Integer> hm) {
while (hm.size() > 1) {
hm.keySet()
.stream()
.sorted()
.forEach(new Consumer<Integer>() {
int i = 1;
#Override
public void accept(Integer n) {
if (i % 2 == 1)
hm.remove(n);
++i;
}
});
}
}
}
(2) Otherwise use a simple LinkedList that you can skim recursively. I prefer to use a LinkedList over an ArrayList because the alg need to remove elements at every iteration, till the last one. And the remove operation on a LinkedList performs better.
public class RemovingOddIndexes {
public static void main(String[] args) {
// initialize
int n = 6;
List<Integer> list = new LinkedList<>();
for (int i = 1; i <= n; i++) {
list.add(i);
}
//
oddDeletions(list);
// print result
list.forEach(i -> System.out.println(String.format("%s, ", i)));
}
public static void oddDeletions(List<Integer> list) {
while (list.size() > 1) {
int i = 1;
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
it.next();
if (i++ % 2 == 1) {
it.remove();
}
}
}
}
}
(3) Last option, the fastest way possible
int lastOdd = 1 << (int)(Math.log(n) / Math.log(2))
I have been trying to figure out how to use the quick select algorithm correctly to find the median of an array at an efficiency of o(n). I have tried to code it as such and it doesn't find the correct median. Im just a little stuck with what I am doing that needs to change in order to find the right median.
Here is my code as of now.
public static void main(String args[])
{
int count = 0;
Scanner in = new Scanner (System.in);
int houses = in.nextInt();
ArrayList <Integer> array = new ArrayList <Integer>();
while (count < houses)
{
array.add(in.nextInt());
count++;
}
System.out.println(median(array, houses));
in.close();
}
public static int median (ArrayList <Integer> array, int k)
{
int median = 0;
int lpos = 0;
int rpos = 0;
int pivot = k/2;
int length = k/2;
ArrayList <Integer> leftArray = new ArrayList <Integer>();
ArrayList <Integer> rightArray = new ArrayList <Integer>();
if (array.size() == 1)
{
return array.get(0);
}
else if (array.size() < pivot)
{
return array.get(pivot);
}
else
{
for (int i=0; i < array.size()-1; i++)
{
if (array.get(i) <= array.get(pivot))
{
leftArray.add(array.get(i));
lpos++;
}
else
{
rightArray.add(array.get(i));
rpos++;
}
}
if (leftArray.size() == length)
{
return array.get(pivot);
}
else if (leftArray.size() > length)
{
return median(leftArray, length-1);
}
else
{
return median(rightArray, length-leftArray.size()-1);
}
}
}
So I got this assignment where I had to split an array into two, split those split arrays in 2, etc... up to a certain threshold.
So if my threshold was lets say 1000 it would be like:
10k>2x 5k>4x 2500>8x 1250>16x 625 (numbers in array).
Then I'd have to sort those arrays and merge them:
16x 625 (merged)>8x 1250 (merged)>4x 2500 (merged)>2x 5000 (merged)>1x 10k.
All of them must remain sorted.
So I think I got pretty far but it seems like it does not work the way I want it to. I tried multiple things to figure what is going wrong but I haven#t figured it out which is why I am here.
Can someone help me understand what I am doing wrong and give me a solution?
Bubblesort.java:
package PartThree;
import java.util.ArrayList;
import java.util.List;
public class Bubblesort implements Runnable {
public List<Integer> arrayList;
private int threshold;
private int middle;
public Bubblesort(List<Integer> arrayList, int threshold) {
this.arrayList = arrayList;
this.threshold = threshold;
this.middle = arrayList.size() / 2;
System.out.println(arrayList.size() + " ha");
}
#Override
public void run() {
if (arrayList.size() <= treshold) {
sort();
}else if(threshold<arrayList.size()){
Bubblesort rLeft = new Bubblesort(arrayList.subList(0, middle), threshold);
Bubblesort rRight = new Bubblesort(arrayList.subList(middle, arrayList.size()), threshold);
Thread tLeft = new Thread(rLeft);
Thread tRight = new Thread((rRight));
tRight.start();
tLeft.start();
try {
tLeft.join();
tRight.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
merge(rLeft.arrayList, rRight.arrayList);
System.out.println(arrayList.size() + ":size");
}
}
private void merge(List<Integer> left, List<Integer> right) {
int i = 0, j = 0, k = 0;
List<Integer> temp = new ArrayList<>();
while (i < left.size() && j < right.size()) {
if (left.get(i) < right.get(j)) {
temp.add(k, left.get(i));
i++;
} else {
temp.add(k, right.get(j));
j++;
}
k++;
}
while (i < left.size()) {
temp.add(k, left.get(i));
i++;
k++;
}
while (j < right.size()) {
temp.add(k, right.get(j));
j++;
k++;
}
arrayList = temp;
temp.clear();
}
private void sort() {
int i, j, tijdelijk;
for (j = 0; j < arrayList.size(); j++) {
for (i = 1; i < arrayList.size() - j; i++) {
if (arrayList.get(i - 1) > arrayList.get(i)) {
tijdelijk = arrayList.get(i);
arrayList.set(i, arrayList.get(i - 1));
arrayList.set(i - 1, tijdelijk);
}
}
}
}
}
Implementation:
package PartThree;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class API {
private Random random = new Random();
private List<Integer> array_unsorted_main = new ArrayList<>();
private int maxSize;
private Thread t1;
private void run(){
this.maxSize = 10000;
for (int i = 0; i < maxSize; i++) {
array_unsorted_main.add(random.nextInt(10000));
}
t1 = new Thread(new Bubblesort(array_unsorted_main,1000));
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String args[]){
new PartThree.API().run();
}
}
Please help me out, thank you for your time.
One of your problems is in your merge method you do arrayList = temp; and then do temp.clear() thereby clearing arrayList since it now points to temp.
Remove the temp.clear() statement so arrayList will retain the sorted values.
I have implemented the select/median of medians algorithm using the following as a reference http://www.ics.uci.edu/~eppstein/161/960130.html (this has previously been linked here Median of Medians in Java).
My code seems to work for small arrays (~100) and even works for arrays of size 100001 http://pastebin.com/mwRc4Hig (answer 5008), but then fails on an input array of size 10001 http://pastebin.com/YwVBmgDk (answer 4960, my code outputs 4958).
Note that the correct answers for the texts above are equivalent to sorting the array and returning the element at array[array.length / 2], regardless of whether the array size is even or odd.
I'm not sure how to debug this issue. The functionality seems arbitrary and I'm just lost. Here below is my code:
public class MedianOfMedians {
public static void main(String[] args) {
MedianOfMedians mds = new MedianOfMedians();
mds.run();
}
private void run() {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] numArray = new int[n];
for (int i = 0; i < n; i++) {
numArray[i] = in.nextInt();
}
int median = select(numArray, numArray.length / 2);
System.out.print(median);
}
private int select(int[] numArray, int k) {
if (numArray.length <= 10) {
int[] sorted = insertionSort(numArray);
return sorted[k];
}
int divCount = (numArray.length % 5 == 0) ? numArray.length / 5 - 1 : numArray.length / 5;
int[] medOfMed = new int[divCount + 1];
int counter = 0;
int[] subArray;
while (counter <= divCount) {
subArray = splitByFive(counter, divCount, numArray);
medOfMed[counter] = select(subArray, subArray.length / 2);
counter++;
}
int M = select(medOfMed, numArray.length / 10);
List<Integer> lt = new ArrayList<>();
List<Integer> eq = new ArrayList<>();
List<Integer> gt = new ArrayList<>();
for (int i : numArray) {
if (i < M) {
lt.add(i);
} else if (i == M) {
eq.add(i);
} else {
gt.add(i);
}
}
if (k < lt.size()) {
return select(createArray(lt), k);
} else if (k > lt.size() + eq.size()) {
return select(createArray(gt), k - lt.size() - eq.size());
} else {
return M;
}
}
private int[] splitByFive(int splitIter, int divisions, int[] toSplit) {
int numToCopy;
if (splitIter == divisions) {
numToCopy = toSplit.length - (5 * splitIter);
} else {
numToCopy = 5;
}
int[] subArray = new int[numToCopy];
System.arraycopy(toSplit, splitIter * 5, subArray, 0, numToCopy);
return subArray;
}
private int[] createArray(List<Integer> list) {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i);
}
return result;
}
private int[] insertionSort(int[] numArray) {
for (int i = 1; i < numArray.length; i++) {
int j = i;
while (j - 1 >= 0 && numArray[j] < numArray[j - 1]) {
int temp = numArray[j];
numArray[j] = numArray[j - 1];
numArray[j - 1] = temp;
j--;
}
}
return numArray;
}
}
I don't have time to debug your code, but maybe I can offer a debugging technique for you to try yourself that's useful for recursive algorithms like this.
If there is an input that the algorithm fails on (and there is, as you found) then there is a smallest such input -- and the smaller this input, the easier it is to figure out what's going wrong. Because the algorithm is recursive, you have a nice way to isolate the first place that things go wrong: you can test that the result you are about to return from select() is correct (using a slow, trusted method like copying the data to a temporary buffer, sorting it and then grabbing the half-way element) just before returning the value. Doing this will be much easier if you rearrange the function to use just a single return statement, e.g.:
private int select(int[] numArray, int k) {
int knownCorrectAnswer = selectSlowlyButDefinitelyCorrectly(numArray, k);
int willReturn;
if (numArray.length <= 10) {
int[] sorted = insertionSort(numArray);
willReturn = sorted[k]; // Just remember what we will return
} else { // Need to add else branch here now
...
if (k < lt.size()) {
willReturn = select(createArray(lt), k);
} else if (k > lt.size() + eq.size()) {
willReturn = select(createArray(gt), k - lt.size() - eq.size());
} else {
willReturn = M;
}
} // End of inserted else branch
if (willReturn == knownCorrectAnswer) {
return willReturn;
} else {
yell("First problem occurs with numArray=<...> and k=<...>!");
}
}
yell() should print out the entire problem instance and halt the program (e.g. by throwing an exception). The nice thing about this setup is that you know that when yell() gets called, every call to select() that has already completed was correct -- since if it wasn't, yell() would have already been called and the program would have halted before now. So the output produced by yell() is guaranteed to be the first (not necessarily the smallest, but often that also) subproblem in which things went wrong.
I have a function 'generateRan' that generates random numbers. This function can not be changed.
int generateRan() {
Random num = new Random();
return (1 + num.nextInt(100));
}
I have to write code that will:
Print numbers 1-20 randomly.
Print numbers 1-200 randomly.
Each number should be printed only once.
The function can be used any number of times. But it is a bit heavy so I want to make the code more optimized.
Here is what I've coded:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList();
Test t = new Test();
iniList(list, 20);
for (Integer i = ((t.generateRan()) % 20); list.size() > 0; i = 1+((t
.generateRan()) % 20)) {
if (list.contains(i.toString())) {
list.remove(i.toString());
System.out.println(i);
}
}
System.out.println("********");
iniList(list, 200);
for (Integer i = ((t.generateRan()%2)*100 + t.generateRan()) ; list.size() > 0; i = ((t.generateRan()%2)*100 + t.generateRan())) {
if (list.contains(i.toString())) {
list.remove(i.toString());
System.out.println(i);
}
}
}
private static void iniList(List list, int i) {
for (Integer k = 1; k <= i; k++) {
list.add(k.toString());
}
}
int generateRan() {
Random num = new Random();
return (1 + num.nextInt(100));
}
}
Currently the code for 1-200 is incorrect.
Each numbers should print only once
Then all you need to to is create a List<Integer> of the entire range, then call Collections.shuffle.
private static void displayNumbers(int minInclusive, int maxInclusive) {
List<Integer> list = new ArrayList<Integer>();
for (int i = minInclusive; i <= maxInclusive; i++) {
list.add(i);
}
Collections.shuffle(list);
for (int value : list) {
System.out.println(value);
}
}
Personally I'd normally use parameters of minInclusive, maxExclusive or minInclusive, count, but it looks like it may be more readable this way for your situation.
Assuming you have to use your generateRan() function, otherwise use Collections.shuffle as indicated.
public static void main(String[] args) {
List<Integer> list = new ArrayList();
initList(list, 200);
while (list.size() > 0) {
int index = generateRan() % list.size();
System.out.println(list.remove(index));
}
}
public static void initList(List<Integer> s, int size) {
for (int i = 1; i <= size; i ++)
s.add(i);
}
public static int generateRan() {
Random num = new Random();
return (1 + num.nextInt(100));
}
You add all the ints you want to print to your list, then only use random to choose which one of these to print. Your function is called n times.
I would recomment using a Set rather than a List, for it's faster in searching for duplicates
Set<Integer> set = new HashSet<Integer>();
for(int i = 0; i < 20;) {
Integer r = generateRan();
if(set.add(r)) {
System.out.println(r);
++i;
}
}