Quicksort - recursive - java

I am trying to code quicksort in two ways, one in-place, and the other by using separate arrays. I'm kind of stuck on some of the logic, take a look at what I have, Thanks for the help in advance!
public List<Integer> sort(List<Integer> arr){
if(arr.length > 0)
List<Integer> ret = new ArrayList<Integer>();
ret = quickSort(arr);
return ret;
}
public List<Integer> quickSort(List<Integer> arr){
if(arr.length < 2)
return;
int pivot = arr[0];
List<Integer> left = new ArrayList<Integer>();
List<Integer> right = new ArrayList<Integer>();
for(int i = 0; i < arr.length; i++){
if(arr[i] <= pivot)
left.add(arr[i]);
else
right.add(arr[i]);
}
quickSort(left);
quickSort(right);
}
Now i'm stuck, I don't know what I would do after recursively going through both sets, mostly stuck on how would I connect them together and return a sorted list.

You need to combine left and right sequences together. You need to do it at the end of your algorithm (before the closing }). In pseudo code:
int leftpos = 0, rightpos = 0;
List newlist = new ArrayList();
for(int pos = 0; pos < arr.length; pos++)
if left[pos] < right[pos] newlist.add(left[leftpos++]);
else newlist.add(right[rightpos++]);
return newlist;
This is just a pseudo-code. You need to add code to check lengths of each array (left and right) in the for cycle.
Also I must note that this is far from quicksort. So many new array allocations make the algorithm extremely slow and that's unwelcome when sorting.
Also, right side of line 3 is redundant. You don't need to allocate anything here, as it is overwritten in the next line. I would just simply replace your lines 3-5 with this:
return quickSort(arr);

Let me have a crack at this for you.
First off, you always want to do in-place sorting unless you're working with linked lists (and even then it usually pays to convert to an array, sort in place, then convert back to a linked list -- it puts way less pressure on the garbage collector). .NET List<>s are actually expanding arrays.
Next, quicksort is really all about the pivot operation. Here's one way to do it:
// Quicksort the sub-array xs[lo..hi].
void QSort(int[] xs, int lo, int hi) {
if (hi <= lo) return; // Don't sort empty or singleton sub-arrays.
var p = [choose some pivot value from xs[lo..hi]];
var a = lo; // Invariant: x[lo..a - 1] <= p.
var z = hi; // Invariant: p < x[z + 1..hi].
while (a <= z) {
if (xs[a] <= p) a++; else Swap(xs, a, z--);
}
QSort(xs, lo, a - 1); // Sort the items <= p.
QSort(xs, z + 1, hi); // Sort the items > p.
}
void Swap(int[] xs, int i, int j) {
var tmp = xs[i];
xs[i] = xs[j];
xs[j] = tmp;
}

Simple implementation on Groovy
def qs(list) {
if (list.size() < 2) return list
def pivot = list[0]
def items = list.groupBy { it <=> pivot }.withDefault { [] }
qs(items[-1]) + items[0] + qs(items[1])
}

Related

Problem with Implementing Merge Sort using an ArrayList

When I execute the following code, for some reason I get
java.util.ConcurrentModificationException
I have tried researching this exception and I believe it happens because the list is being continually edited while I am trying to access it yet again.
This is really frustrating because when I instead of using ArrayLists, used regular arrays, everything seemed to work fine, so I'm not exactly sure how I can go around using the same procedure just with array lists and get it to work.
Here's the code:
public static void mergeSort(List<Integer> indexList, int listLen) {
if (listLen < 2) {
// calls merge method when 1 term is in either left or right arrays
return;
}
int middlepoint = listLen / 2;
List<Integer> leftArr = indexList.subList(0, middlepoint);
List<Integer> rightArr = indexList.subList(middlepoint, listLen);
// passing the numList to the merge (once all numbers are in groups of 1)
merge(indexList, leftArr, rightArr, middlepoint, listLen - middlepoint);
}
public static void merge(
List<Integer> numList, List<Integer> leftArr, List<Integer> rightArr, int left, int right) {
// while there are terms in both lists
int i = 0, j = 0, k = 0;
// while numbers in both lists
while (i < left && j < right) {
int leftVal = leftArr.get(i);
int rightVal = rightArr.get(j);
// if the term in the right array is bigger/equal (filling the final list smallest to greatest)
if (leftVal <= rightVal) {
numList.add(k++, leftVal);
i++;
}
else {
numList.add(k++, rightVal);
j++;
}
while (i < left) {
numList.add(k++, leftVal);
i++;
}
while (j < right) {
numList.add(k++, rightVal);
j++;
}
}
}
You have used subList to divide and traverse the List. Arraylist doesn't allow you to modify the values when you are in middle of traversal and throws an Concurrent Modification Exception.
One way of solving this issue is to remove the dependency on subList method and update your recursive method to take List, startIndex and endIndex.
The other way to work around is to use a Thread safe implementation of List. You can take this route if you can change your list data structure.
Hope this helps.

MergeSort on ArrayList only work with ArrayList(Collection<? extends E> c) not ArrayList(int initialCapacity)?

I am working on sort interval which exist on an ArrayList with its start property, the full definition of interval will show in sample code as private class.
The implementation I am using is MergeSort, and very similar to Princeton's stuff which I refer to, but problem is I find this implementation only work with creating auxiliary ArrayList aux with ArrayList(Collection<? extends E> c) and initialize it with aux.set(i, intervals.get(i)); which try to fill aux with same order of interval items from original list
I try to work it out with creating aux with ArrayList(int initialCapacity) and initialize it with aux.add(intervals.get(i)), but failed. And I still could not figure out why this way doesn't work ?
The full code which can directly used for debug:
public class Test {
private class Interval {
// Will use start property to sort
int start;
int end;
Interval() {
start = 0;
end = 0;
}
Interval(int s, int e) {
start = s;
end = e;
}
#Override
public String toString() {
return "[" + start + "," + end + "]";
}
}
public void sortHelper(List<Interval> intervals) {
int len = intervals.size();
// Only this way works
List<Interval> aux = new ArrayList<Interval>(intervals);
// This way NOT work ?
//List<Interval> aux = new ArrayList<Interval>(len);
sort(intervals, aux, 0, len - 1);
}
public void sort(List<Interval> intervals, List<Interval> aux, int lo, int hi) {
if(hi <= lo) {
return;
}
int mid = lo + (hi - lo) / 2;
sort(intervals, aux, lo, mid);
sort(intervals, aux, mid + 1, hi);
mergeHelper(intervals, aux, lo, mid, hi);
}
public void mergeHelper(List<Interval> intervals, List<Interval> aux, int lo, int mid, int hi) {
// Only this way works
for(int i = lo; i <= hi; i++) {
aux.set(i, intervals.get(i));
}
// Combine with List<Interval> aux = new ArrayList<Interval>(len);
// this way NOT work ?
//for(int i = lo; i <= hi; i++) {
// aux.add(intervals.get(i));
//}
int left = lo;
int right = mid + 1;
for(int k = lo; k <= hi; k++) {
if(left > mid) {
intervals.set(k, aux.get(right++));
} else if(right > hi) {
intervals.set(k, aux.get(left++));
} else if(less(aux.get(right), aux.get(left))) {
intervals.set(k, aux.get(right++));
} else {
intervals.set(k, aux.get(left++));
}
}
}
public boolean less(Interval a, Interval b) {
return a.start - b.start < 0;
}
public static void main(String[] args) {
Test m = new Test();
Interval one = m.new Interval(1, 3);
Interval two = m.new Interval(2, 6);
Interval three = m.new Interval(8, 10);
Interval four = m.new Interval(15, 18);
List<Interval> intervals = new ArrayList<Interval>();
// Adding order as [2,6],[1,3],[8,10],[15,18]
intervals.add(two);
intervals.add(one);
intervals.add(three);
intervals.add(four);
m.sortHelper(intervals);
// Expected sort result should be
// [1,3],[2,6],[8,10],[15,18]
for(Interval i : intervals) {
System.out.println(i);
}
}
}
Could anyone help me on understanding why must create auxiliary ArrayList aux with ArrayList<Collection<? extends E> c> and initialize it with aux.set(i, intervals.get(i)); ? Or my understanding goes the wrong way ?
Thanks
Thanks for important hint from #maraca and #Dukeling, after some work, below is my opinion for the issue here:
Q1: Why using List<Interval> aux = new ArrayList<Interval>(len); with aux.add(intervals.get(i)); not work ?
A1: There are two main issues here, I should admit I fail into some habitual thoughts as previous work with array implementation of MergeSort, the impact here are (1) new ArrayList<Interval>(len) equal to new Interval[len] or not ? (2) aux[i] = intervals[i](just for explain, not use = in real code) equal to aux.add(intervals.get(i)) ?
For problem (1), the real case here is using new ArrayList<Interval>(len) will only define the initial limitation of ArrayList size, but no real items used like space holder to fill into the initialized list, e.g If we set len = 4, the initialized list is actually [], not [interval obj, interval obj, interval obj, interval obj]. Assume using new ArrayList<Interval>(len) with aux.set(i, intervals.get(i)), IDE will throw out java.lang.IndexOutOfBoundsException: Index: 0, Size: 0, that's because no real object fill into this list as initialized and set method cannot work on an empty list. The solution for this issue is create len numbers of dummy interval objects to fill in list when initialized, such as
List<Interval> aux = new ArrayList<Interval>((Arrays.asList(new Interval(), new Interval(), new Interval(), new Interval())));, but since we don't care what initialized first on auxiliary list, just used as place holder and in case of the objects are too many, above code can be replaced with List<Interval> aux = new ArrayList<Interval>(intervals); which return to our right solution.
For problem (2), the real case here is using aux.add(intervals.get(i)) not equal to aux[i] = intervals[i], at least we should use set() instead of add(), because add() will continually append on aux, which not MergeSort suppose to, because MergeSort require exactly same size auxiliary collection copy to help sorting. Still use same example to explain what happened when using add()
Note after the 3rd merge, the aux size increase to double size of original list, this will cause indices issue when copy back to original list. But if we are using set() will not cause this trouble.
Q2: If still want to use add() what is the proper way ?
A2: As mentioned by #Dukeling, we need to clear() the aux before final round copy from original list to auxiliary list, e.g like before the 3rd merge happen on above example.
The working code with add() and clear() below:
public void mergeHelper(List<Interval> intervals, List<Interval> aux, int lo, int mid, int hi) {
aux.clear();
for(int i = 0; i < lo; i++) {
aux.add(null);
}
for(int i = lo; i <= hi; i++) {
aux.add(intervals.get(i));
}
int left = lo;
int right = mid + 1;
for(int k = lo; k <= hi; k++) {
if(left > mid) {
intervals.set(k, aux.get(right++));
} else if(right > hi) {
intervals.set(k, aux.get(left++));
} else if(less(aux.get(right), aux.get(left))) {
intervals.set(k, aux.get(right++));
} else {
intervals.set(k, aux.get(left++));
}
}
}

Inplace Quicksort in Java

For refreshing some Java I tried to implement a quicksort (inplace) algorithm that can sort integer arrays. Following is the code I've got so far. You can call it by sort(a,0,a.length-1).
This code obviously fails (gets into an infinite loop) if both 'pointers' i,j point each to an array entry that have the same values as the pivot. The pivot element v is always the right most of the current partition (the one with the greatest index).
But I just cannot figure out how to avoid that, does anyone see a solution?
static void sort(int a[], int left, int right) {
if (right > left){
int i=left, j=right-1, tmp;
int v = a[right]; //pivot
int counter = 0;
do {
while(a[i]<v)i++;
while(j>0 && a[j]>v)j--;
if( i < j){
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
} while(i < j);
tmp = a[right];
a[right] = a[i];
a[i] = tmp;
sort(a,left,i-1);
sort(a,i+1,right);
}
}
When preforming a Quicksort I strongly suggest making a separate method for partitioning to make the code easier to follow (I'll show an example below). On top of this a good way of avoiding worst case run time is shuffling the array you're sorting prior to preforming the quick sort. Also I used the first index as the partitioning item instead of the last.
For example:
public static void sort (int[] a)
{
StdRandom.shuffle(a);
sort(a, 0, a.length - 1);
}
private static void sort(int[] a, int lo, int hi)
{
if (hi <= lo) return;
int j = partition(a, lo, hi) // the addition of a partitioning method
sort(a, lo, j-1);
sort(a, j+1, hi);
}
private static int partition(int[] a, int lo, int hi)
{
int i = lo, j = hi + 1, tmp = 0;
int v = a[lo];
while (true)
{
while (a[i++] < v) if (i == hi) break;
while (v < a[j--]) if (j == lo) break;
if (i >= j) break;
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
tmp = a[lo];
a[lo] = a[j];
a[j] = temp;
return j;
}
On top of this if you want a really good example on how Quicksort works (as a refresher) see here.
This should work (will check for correctness in a bit, it works!):
EDIT: I previously made a mistake in error checking. I forgot to add 2 more conditions, here is the amended code.
public static void main (String[] args) throws java.lang.Exception
{
int b[] = {10, 9, 8, 7, 7, 7, 7, 3, 2, 1};
sort(b,0,b.length-1);
System.out.println(Arrays.toString(b));
}
static void sort(int a[], int left, int right) {
if (right > left){
int i=left, j=right, tmp;
//we want j to be right, not right-1 since that leaves out a number during recursion
int v = a[right]; //pivot
do {
while(a[i]<v)
i++;
while(a[j]>v)
//no need to check for 0, the right condition for recursion is the 2 if statements below.
j--;
if( i <= j){ //your code was i<j
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
//we need to +/- both i,j, else it will stick at 0 or be same number
}
} while(i <= j); //your code was i<j, hence infinite loop on 0 case
//you had a swap here, I don't think it's needed.
//this is the 2 conditions we need to avoid infinite loops
// check if left < j, if it isn't, it's already sorted. Done
if(left < j) sort(a,left,j);
//check if i is less than right, if it isn't it's already sorted. Done
// here i is now the 'middle index', the slice for divide and conquer.
if(i < right) sort(a,i,right);
}
}
This Code in the IDEOne online compiler
Basically we make sure that we also swap the value if the value of i/j is the same as the pivot, and break out of the recursion.
Also there was a check in the pseudocode for the length, as if we have an array of just 1 item it's already sorted (we forgot the base case), I thought we needed that but since you pass in the indexes and the entire array, not the subarray, we just increment i and j so the algorithm won't stick at 0 (they're done sorting) but still keep sorting an array of 1. :)
Also, we had to add 2 conditions to check if the array is already sorted for the recursive calls. without it, we'll end up sorting an already sorted array forever, hence another infinite loop. see how I added checks for if left less than j and if i less than right. Also, at that point of passing in i and j, i is effectively the middle index we split for divide and conquer, and j would be the value right before the middle value.
The pseudocode for it is taken from RosettaCode:
function quicksort(array)
if length(array) > 1
pivot := select any element of array
left := first index of array
right := last index of array
while left ≤ right
while array[left] < pivot
left := left + 1
while array[right] > pivot
right := right - 1
if left ≤ right
swap array[left] with array[right]
left := left + 1
right := right - 1
quicksort(array from first index to right)
quicksort(array from left to last index)
Reference: This SO question
Also read this for a quick refresher, it's implemented differently with an oridnary while loop
This was fun :)
Heres some simple code I wrote that doesn't initialize to many pointers and gets the job done in a simple manner.
public int[] quickSort(int[] x ){
quickSortWorker(x,0,x.length-1);
return x;
}
private int[] quickSortWorker(int[] x, int lb, int ub){
if (lb>=ub) return x;
int pivotIndex = lb;
for (int i = lb+1 ; i<=ub; i++){
if (x[i]<=x[pivotIndex]){
swap(x,pivotIndex,i);
swap(x,i,pivotIndex+1);
pivotIndex++;
}
}
quickSortWorker(x,lb,pivotIndex-1);
quickSortWorker(x,pivotIndex+1,ub);
return x;
}
private void swap(int[] x,int a, int b){
int tmp = x[a];
x[a]=x[b];
x[b]=tmp;
}

Keeping two arrays, String[] and int[], parallel with merge sort

I'm having trouble with keeping two arrays in parallel within my Merge Sorting algorithm.
Suppose I have array defMergeSort and intMergeSort2.
I would like to lexicographically order String defMergeSort,
String[] defMergeSort = {"Echo", "Alpha", "Charlie", "Beta", "Alpha", "Echo"};
Array intMergeSort2 represents the element position in parallel to defMergeSort. (Ex: defMergeSort[0] = Echo contingent to intMergeSort2[0] = 0, defMergeSort[3] = Beta contingent to intMergeSort2[3] = 3)
intMergeSort2 is to be rearranged in parallel to defMergeSort, although not numerically sorted,
int[] intMergeSort2 = {0,1,2,3,4,5};
The end result should look similar to this (Not sure if parallel ordering for intMergeSort2[] in my example is correct for duplicate Strings in defMergeSort[]):
defMergeSort[0] Alpha = intMergeSort2[1] 1
defMergeSort[1] Alpha = intMergeSort2[4] 4
defMergeSort[2] Beta = intMergeSort2[3] 3
defMergeSort[3] Charlie = intMergeSort2[2] 2
defMergeSort[4] Echo = intMergeSort2[0] 0
defMergeSort[5] Echo = intMergeSort2[5] 5
The following merge sort algorithm can lexicographically order defMergeSort, although I cannot figure out how to keep defMergeSort in parallel as stipulated above:
//mergeSort code found at:
//http://www.buildingjavaprograms.com/code-files/2ed/ch13/MergeSort.java
public static void mergeSort(String[] defMergeSort, int[] intMergeSort2) {
if (defMergeSort.length > 1) {
// split array into two halves
String[] left = leftHalf(defMergeSort);
String[] right = rightHalf(defMergeSort);
// recursively sort the two halves
mergeSort(left, intMergeSort2);
mergeSort(right, intMergeSort2);
// merge the sorted halves into a sorted whole
merge(defMergeSort, intMergeSort2, left, right);
}
}
// Returns the first half of the given array.
public static String[] leftHalf(String[] defMergeSort) {
int size1 = defMergeSort.length / 2;
String[] left = new String[size1];
for (int i = 0; i < size1; i++) {
left[i] = defMergeSort[i];
}
return left;
}
// Returns the second half of the given array.
public static String[] rightHalf(String[] defMergeSort) {
int size1 = defMergeSort.length / 2;
int size2 = defMergeSort.length - size1;
String[] right = new String[size2];
for (int i = 0; i < size2; i++) {
right[i] = defMergeSort[i + size1];
}
return right;
}
// Merges the given left and right arrays into the given
// result array. Second, working version.
// pre : result is empty; left/right are sorted
// post: result contains result of merging sorted lists;
public static void merge(String[] defMergeSort, int[] intMergeSort2,
String[] left, String[] right){
int i1 = 0; // index into left array
int i2 = 0; // index into right array
for(int i = 0; i < defMergeSort.length; i++){
if (i2 >= right.length || (i1 < left.length &&
left[i1].compareTo(right[i2]) <= 0)) {
defMergeSort[i] = left[i1];
i1++;
} else {
defMergeSort[i] = right[i2];
i2++;
}
}
}
Define an object containing your integer and string values, have a single array of that object, sort that array.
i.e.
class DataElement {
final String str;
final int i;
//Add constructor here
}
DataElement[] array; // sort this array
Note that you will either need to implement Comparable in the DataElement or specify a Comparator to the sort method in order to control the sorting.
Tim B's answer is probably the best approach, I would just like to mention that since the class is just an int and a string, you can use the predefined datastructure std::pair. Just have an array of these pairs and sort by the string. To do this you can use std::sort() defined in , if I'm not wrong std::sort is a MergeSort. You can override the compare function defined with your own.
I find it extremely convenient to use pair in such situations as yours. Make sure you include .
Look into http://www.cplusplus.com/reference/utility/pair/ for reference.
Look into http://www.cplusplus.com/reference/algorithm/sort/ for reference.

How to find the median of a large number of integers (they dont fit in memory)

I know the answer is using median of medians but can someone explain how to do it?
There are linear time algorithms to do this, this page might be helpful, http://en.wikipedia.org/wiki/Selection_algorithm, if you are still confused just ask
Basically the way the selection algorithm works is like a quicksort but it only sorts on side of the pivot each time. The goal is to keep partitioning until you choose the pivot equal to the index of the element you were trying to find. Here is java code I found for quickselect:
public static int selectKth(int[] arr, int k) {
if (arr == null || arr.length <= k)
throw new Error();
int from = 0, to = arr.length - 1;
// if from == to we reached the kth element
while (from < to) {
int r = from, w = to;
int mid = arr[(r + w) / 2];
// stop if the reader and writer meets
while (r < w) {
if (arr[r] >= mid) { // put the large values at the end
int tmp = arr[w];
arr[w] = arr[r];
arr[r] = tmp;
w--;
} else { // the value is smaller than the pivot, skip
r++;
}
}
// if we stepped up (r++) we need to step one down
if (arr[r] > mid)
r--;
// the r pointer is on the end of the first k elements
if (k <= r) {
to = r;
} else {
from = r + 1;
}
}
return arr[k];
}
here is the Median of Medians algorithm. check this out
See the first two answers to this question. If the first one (frequency counts) can work for your data / available storage, you can get the exact answer that way. The second (remedian) is a robust, general method.

Categories

Resources