Iterative QuickSort with O(log n) extra space (stack) - java

Hello I have written QuickSort with stack but I'm not sure this alghoritm is using O(n) extra space or O(log n) extra space which I want to make.
I will be very thankful if somebody could look at this code and tell me what extra space uses here stack and if it uses O(n) extra space how to do it to use only O(log n) extra space?
Here is my code
public class QuickSortStack{
public static void quickSortStack(int[] tab, int L, int R) {
Stack<Integer> stack = new Stack();
while (L < R || !stack.isEmpty()) {
if (L < R) {
int q = lomutoPartition(tab, L, R);
stack.push(R);
R = q - 1;
} else {
L = R + 2;
R = stack.pop();
}
}//end of while
}//end of QS method
public static void main(String[] args) {
int[] test= {-4,2,-4,2,-12,5,-1,6,-9,0,9};
Random random = new Random();
int[] tab= new int[20];
for (int i = 0; i < 20; i++) {
tab[i] = random.nextInt(50);
System.out.print(tab[i] + " ");
}
System.out.println();
quickSortStos(tab, 0, tab.length - 1);
for (int x : tab
) {
System.out.print(x + " ");
}
}
public static void swap(int[] tab, int i, int j) {
int tmp = tab[i];
tab[i] = tab[j];
tab[j] = tmp;
}
public static int lomutoPartition(int[] tab, int L, int R){
int i = L-1;
int x = tab[R];
for(int j = L; j < R; j++){
if(tab[j] <= x){
i = i+1;
swap(tab, i, j);
}
}
swap(tab, i+1, R);
return i+1;
}

In order to guarantee O(log N) space usage, you need to push the larger of the two partitions and loop with the smaller one. That's equivalent to a recursive solution where the non-tail recursion is always the smaller partition, guaranteeing a stack depth of no more than log2N.
When you do that, you will need to push both boundaries of the partition, or at least a boolean which tells you whether it was the first or second partition.
Fwiw, the common experience is that the iterative solution is not faster than a recursive solution. The recursive implementation is safe as long as you tail-call optimize the second recursion (for the larger partition); if your language doesn't guarantee TCO, it's easy to do it by hand.

Related

Implementing Merge Sort with Java

I was trying to implement merge sort using java, but it's saying:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at HelloWorld.merge(HelloWorld.java:42)
at HelloWorld.sort(HelloWorld.java:30)
at HelloWorld.sort(HelloWorld.java:28)
at HelloWorld.sort(HelloWorld.java:29)
at HelloWorld.sort(HelloWorld.java:28)
at HelloWorld.main(HelloWorld.java:10)
So I tried to copy the original array into a helper array in the merge subroutine, I think it's the length I use for helper array messed everything up, isn't "r-l+1" the length of the helper array?
If I put 100 as the length of the helper array, the code would work, but obviously it's not going to work when the size of the original array gets larger.
Please help and thanks in advance.
public class HelloWorld{
public static void main(String []args){
HelloWorld ms = new HelloWorld();
int arr[] = new int[]{4,3,0,1,3,2,4,20,13,22,10};
ms.sort(arr, 0, arr.length - 1);
ms.printArr(arr);
}
void printArr(int arr[])
{
int n = arr.length;
for(int i = 0; i<n; i++){
System.out.print(" "+arr[i]);
}
}
void sort(int arr[], int l, int r)
{
if(l<r){
int m = (l+r)/2;
sort(arr, l, m);
sort(arr, m+1, r);
merge(arr, l, m, r);
}
}
void merge(int arr[], int l, int m, int r)
{
//find out helper array length
int helper[] = new int[r-l+1];
// Your code here
for(int i=l; i<=r; i++){
helper[i] = arr[i];
}
int i = l;
int k = l;
int j = m+1;
while(i<=m && j<=r){
if(helper[i]<=helper[j]){
arr[k]=helper[i];
i++;
}else{
arr[k]=helper[j];
j++;
}
k++;
}
while(i<=m){
arr[k]=helper[i];
i++;
k++;
}
}
}
If I understand the way you're attempting to implement the MERGE subroutine for your merge sort algorithm correctly, you're copying the relevant portion of the array (from index l, inclusive, to index l, inclusive) into the helper array, then modifying the original array using the data copied into the helper array.
In this case, the helper array should indeed have length r-l+1. However, taking a look at your code for copying the original array portion into the helper array, you're not offsetting your indexes in the for loop. The correct way to do it would be:
int helper[] = new int[r-l+1];
for (int i = l; i <= r; i++){
helper[i-l] = arr[i];
}
Don't forget arrays are zero indexed!
I do however recommend moving on to more modern ways of copying arrays if you absolutely have to copy them. In this case, the most adapted in my opinion would be the system call System.arraycopy(arr, l, helper, 0, r-l+1). If you would like to read more about the different methods of copying an array (completely or partially) in Java, check this answer out.
You create an array with the size r - l + 1
int helper[] = new int[r-l+1];
The merge method will be executed with l = 3, m = 3, r = 4
So the array helper has a size of r - l + 1 = 2 = [0, 0]
Your for loop runs from l..r
for(int i=l; i<=r; i++){
helper[i] = arr[i];
}
And you try to set the value from arr[3] to helper[3] which is not possible because helper.length = 2
That's why the ArrayIndexOutOfBoundsException occurs! You can modfiy the access to the helper index to i - l
void merge(int arr[], int l, int m, int r) {
// find out helper array length
int helper[] = new int[r - l + 1];
// Your code here
System.arraycopy(arr, l, helper, 0, helper.length);
int i = l;
int k = l;
int j = m + 1;
while (i <= m && j <= r) {
if (helper[i - l] <= helper[j - l]) { // <---
arr[k] = helper[i - l]; // <---
i++;
} else {
arr[k] = helper[j - l]; // <---
j++;
}
k++;
}
while (i <= m) {
arr[k] = helper[i - l]; // <---
i++;
k++;
}
}

One method policy in Java

I need some help. My professor give us an assignment that we need to "extract" these codes into ONE METHOD ONLY. Is there a way to do it? It's a heap code sorting algorithm. I'm currently do have a little knowledge about programming so bear with me guys. Can you help me?
import java.util.Arrays;
import java.util.Scanner;
class HeapSort {
private static Scanner sc;
public static void main(String args[]) {
sc = new Scanner(System.in);
System.out.println("Enter no of terms");
int n = sc.nextInt();
System.out.println("Enter the terms");
int arr[] = new int[n];
for (int i = 0; i < n; i++)
arr[i] = sc.nextInt();
System.out.println("The unsorted array is:");
System.out.println(Arrays.toString(arr));
heap(arr);
System.out.println("The sorted array is:");
System.out.println(Arrays.toString(arr));
}
static void heapify(int a[], int n, int i) {
int max, child;
child = 2 * i + 1;
max = i;
if (child < n)
if (a[child] > a[max])
max = child;
if (child + 1 < n)
if (a[child + 1] > a[max])
max = child + 1;
if (max != i) {
int temp = a[i];
a[i] = a[max];
a[max] = temp;
heapify(a, n, max);
}
}
static void buildheap(int a[]) {
for (int i = a.length / 2 - 1; i >= 0; i--)
heapify(a, a.length, i);
}
static void heap(int a[]) {
buildheap(a);
for (int i = a.length - 1; i >= 1; i--) {
int temp = a[0];
a[0] = a[i];
a[i] = temp;
heapify(a, i, 0);
}
}
}
You get there by simply replacing each method invocation with the actual body of the method. Of course, that will quickly lead to all kinds of confusion, given the poor naming of method parameters.
But the real challenge here (and probably your actual homework) is that you have to rework that heapify() method to not use recursion. In other words: you have to do the heap sort without using recursion. You can find some guidance here for example.
And of course: this code is already hard to read. Forcing all code into a single method will make it unreadable and not human comprehensive. It is like the exact opposite of good practices!
One thing you can do is replace all the code in each method to where the method is being called.
For example, instead of calling heap(arr) you can move all the code up to the place where heap(arr) is being called and continue to do so with the rest of the code.

Non-recursive O(N) space merge sort

I am coding the non-recursive merge sort algorithm in Java
I have to make sure if this method works as a non recursive as well as the space complexity should be O(N)
Instruction I got: You can use O(N) space (in addition to the input array) and your algorithm should have the same running time as recursive merge sort.
here's my code.
I want to make sure the recursiveness as well as the O(N) space
If there's a better way, please let me know.
private static void merge(Integer[] a, Integer[] tmpArray, int leftPos, int rightPos, int rightEnd) {
int leftEnd = rightPos - 1;
int tmpPos = leftPos;
int numElements = rightEnd - leftPos + 1;
// Main loop
while(leftPos <= leftEnd && rightPos <= rightEnd) {
if( a[leftPos] <= a[rightPos ]) {
tmpArray[tmpPos++] = a[leftPos++];
} else {
tmpArray[tmpPos++] = a[rightPos++];
}
}
while( leftPos <= leftEnd ) { // Copy rest of first half
tmpArray[tmpPos++] = a[leftPos++];
}
while( rightPos <= rightEnd ) { // Copy rest of right half
tmpArray[tmpPos++] = a[rightPos++];
}
// Copy tmpArray back
for( int i = 0; i < numElements; i++, rightEnd-- ) {
a[rightEnd] = tmpArray[rightEnd];
}
}
public static void mergeSortB(Integer[] inputArray) {
Integer[] tempArray = new Integer[inputArray.length];
for(int i = 1; i<inputArray.length; i=i*2) {
for(int j=i; j<inputArray.length; j=j+i*2) {
int k = j+i-1;
if(inputArray.length<j + i) {
k = inputArray.length -1;
}
//call the merge method(non recursive)
merge(inputArray, tempArray, j-i,j, k);
}
}
}
Your code looks okay. Although, you can create a test (and debug if necessary) to make sure your code is working.
But Merge Sort has Big(O)== NlogN, not N.

HW Recursive Divide and Conquer Algorithm

I'm having a really big issue with finding the solution to my problem. I have to create a recursive, divide-and conquer algorithm that computes the length of the longest non-decreasing subsequence of elements in an array of integers. I have the following code, but it's not really working, any help would be much appreciated!!!
public class LongestSubSequence {
public static int getPartition(int[] a, int p, int r)
{
int mid = ((p+r)/2)-1;
int q=0;
int i = 1;
int j= mid+i;
int k = mid -i;
while (a[mid]<=a[j] && j < r)
{
q = j;
i++;
}
while (a[mid] >=a [k] && k > p)
{
q = k;
i++;
}
return q;
}
public static int getCount (int[]a, int p, int r)
{
int i = p;
int j = p+1;
int count = 0;
while (i<r && j<r)
{
if(a[i]<=a[j])
count++;
i++;
j++;
}
return count;
}
public static int getLongestSubsequence (int[] a, int p, int r) {
int count = 0;
if (p<r)
{
int q = getPartition (a, p, r);
count = getCount(a,p,r);
if (count < getLongestSubsequence(a,p,q))
count = getLongestSubsequence(a, p, q);
else if (count < getLongestSubsequence(a, q+1, p))
{
count = getLongestSubsequence(a, q+1, p);
}
}
return count;
}
public static int LongestSubsequence (int[] a) {
return getLongestSubsequence(a, 0, a.length);
}
public static void main(String[] args) {
int[] a = {1,3,5,9,2, 1, 3};
System.out.println(LongestSubsequence(a));
}
}
This is a pretty big body of code, and it's a little hard to follow with all the a's, r's, q's, etc.
In general, I would create an array (call it longestSeq) where longestSeq[i] is the length of the longest non-decreasing sequence found so far that starts at index, i, of your original sequence. For instance, if I had
int[] sequence = new int[] { 3, 5, 1, 2 }
then the algorithm would yield
longestSeq[0] = 2;
longestSeq[1] = 1;
longestSeq[2] = 2;
longestSeq[3] = 1;
So you would initialize longestSeq to all 0's, and then iterate through your list and fill in these values. At the end, just take the max of longestSeq.
Maybe start with just trying to make it work iteratively (without recursion) and then add recursion if that's a requirement.

3-way quicksort, question

I am trying to understand the 3-way radix Quicksort, and i dont understand why the the CUTOFF variable there? and the insertion method?
public class Quick3string {
private static final int CUTOFF = 15; // cutoff to insertion sort
// sort the array a[] of strings
public static void sort(String[] a) {
// StdRandom.shuffle(a);
sort(a, 0, a.length-1, 0);
assert isSorted(a);
}
// return the dth character of s, -1 if d = length of s
private static int charAt(String s, int d) {
assert d >= 0 && d <= s.length();
if (d == s.length()) return -1;
return s.charAt(d);
}
// 3-way string quicksort a[lo..hi] starting at dth character
private static void sort(String[] a, int lo, int hi, int d) {
// cutoff to insertion sort for small subarrays
if (hi <= lo + CUTOFF) {
insertion(a, lo, hi, d);
return;
}
int lt = lo, gt = hi;
int v = charAt(a[lo], d);
int i = lo + 1;
while (i <= gt) {
int t = charAt(a[i], d);
if (t < v) exch(a, lt++, i++);
else if (t > v) exch(a, i, gt--);
else i++;
}
// a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi].
sort(a, lo, lt-1, d);
if (v >= 0) sort(a, lt, gt, d+1);
sort(a, gt+1, hi, d);
}
// sort from a[lo] to a[hi], starting at the dth character
private static void insertion(String[] a, int lo, int hi, int d) {
for (int i = lo; i <= hi; i++)
for (int j = i; j > lo && less(a[j], a[j-1], d); j--)
exch(a, j, j-1);
}
// exchange a[i] and a[j]
private static void exch(String[] a, int i, int j) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
// is v less than w, starting at character d
private static boolean less(String v, String w, int d) {
assert v.substring(0, d).equals(w.substring(0, d));
return v.substring(d).compareTo(w.substring(d)) < 0;
}
// is the array sorted
private static boolean isSorted(String[] a) {
for (int i = 1; i < a.length; i++)
if (a[i].compareTo(a[i-1]) < 0) return false;
return true;
}
public static void main(String[] args) {
// read in the strings from standard input
String[] a = StdIn.readAll().split("\\s+");
int N = a.length;
// sort the strings
sort(a);
// print the results
for (int i = 0; i < N; i++)
StdOut.println(a[i]);
}
}
from http://www.cs.princeton.edu/algs4/51radix/Quick3string.java.html
It would appear to be used in order to invoke insertion sort for sufficiently small (size <= 15) arrays. This is most likely to speed up sorting.
It's a simple optimization of quicksort algorithm. The cost of recursive calls in quicksort are quite high, so for small arrays insertion sort works better. So, the idea is, that if length of subarray to be sorted os below certain threshold, it's better to sort it using insertion sort than quicksort. In your example, CUTOFF variable defines that threshold, i.e. if less than 15 elements are left, they are sorted using insertion sort instead of quicksort.
The sort method above is a recursive method. And every recursive method should have some kind of base case (otherwise it will keep calling itself, eventually leading to a stack overflow).
The insertion part is the base case in that method, because at every recursive step, the hi-lo difference keeps decreasing, & when its less than CUTOFF, the insertion sort will eventually be triggered, forcing the recursion to stop.
if (hi <= lo + CUTOFF) { // base case
insertion(a, lo, hi, d);
return;
}
Now, why insertion ? Because it works well for small arrays.
More on sorting here: http://www.sorting-algorithms.com/
This idea comes from Robert Sedgewick, who knows more about Quicksort than probably any man alive. It is cited in Donald E. Knuth, The Art of Computer Programming, Volume III, where he shows that for small M, insertion sort is faster than Quicksort, so he recommends not sorting small partitions < M at all and leaving it to one final insertion sort over the whole data set at the end. Knuth gives a function for calculating M (which is your CUTOFF), and which is 9 for his MIX pseudo-computer.

Categories

Resources