//I know that compareTo() returns an int value.
returns 0 if a equal b
returns -1 if a < b
returns 1 if a > b
//But how does sort method makes use of these 0, 1, -1 values, and how does it arranges the list exactly.
To sort an array with compareTo(),
public void sort(Comparable[] lst)
{
for(int i=0; i<lst.length; i++)
{
for(int j=i; j<lst.length; j++)
{
if(lst[i].compareTo(lst[j]) < 0)
{
//swap elements, sorts in ascending order
Comparable tmp = lst[i];
lst[i] = lst[j];
lst[j] = tmp;
}
}
}
}
Well, here's the code, not sure what more you want. Collections.sort seems to ultimately call this.
private static <T> void binarySort(T[] a, int lo, int hi, int start, Comparator<? super T> c) {
assert lo <= start && start <= hi;
if (start == lo)
start++;
for ( ; start < hi; start++) {
T pivot = a[start];
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
if (c.compare(pivot, a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
/*
* The invariants still hold: pivot >= all in [lo, left) and
* pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the
* first slot after them -- that's why this sort is stable.
* Slide elements over to make room for pivot.
*/
int n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch (n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}
Related
I have a quick sort method which sorts elements in ascenidng order but seem to keep getting a stackoverflowerror.
For some reason it is showing the error on the while loop, when the logic makes sense to me.
Here is the code for the quick sort class:
public T[] sort(T[] arr, int left, int right)
{
int l = left;
int r = right;
if (right <= left)
return null;
//Find the pivot in the middle
T pivot = arr[(left + (right - left)) / 2];
T temp;
while (l <= r)
{
// check values on left are bigger than the pivot
while (arr[l].compareTo(pivot) < 0)
{
l++;
}
// check if values are smaller than the pivot
while (arr[r].compareTo(pivot) > 0)
{
r--;
}
// l and r have gone past each other swap them
if (l <= r)
{
//swap process
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// left pointer goes up 1
// right pointer goes down 1
l++;
r--;
}
}
if (left < r)
sort(arr, left, r);
if (l < right)
sort(arr, l, right);
return arr;
}
The error seems to be pointing to
//Find the pivot in the middle
T pivot = arr[(left + (right - left)) / 2];
I then seem to be getting many occuring errors.
I believe you are calculating Pivot incorrectly
You should use T pivot = arr[left + (right - left) / 2];
Below is working quick sort program using middle element as pivot:
public void quickSort(T arr[],int left, int right){
int low =left, high = right;
int pivot = arr[left + (right - left) / 2];
while(low<=high){
while (arr[low] < pivot) {
low++;
}
while (arr[high] > pivot) {
high--;
}
if (low <= high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
low++;
high--;
}
if (left < high) {
quickSort(arr,left, high);
}
if (low < high) {
quickSort(arr,low, right);
}
}
}
Hope it helps !!
Stream API way:
T[] result = Arrays.stream(a)
.skip(left)
.limit(right - left)
.sorted((o1, o2) -> {{you logic of comparing}})
.toArray(String[]::new);
the difference in what you get only sorted part of an array. So you should concatenate them after if it is necessary.
You have a typo in that line, yes. T pivot = arr[(left + (right - left)) / 2];
Due to the extra parentheses, everything is divided by 2. It should be
T pivot = arr[left + (right - left) / 2];
A couple stylistical remarks:
as this is an in-place sort, returning T[] is not really necessary
T temp could be moved into the swap-block
Putting it together:
import java.util.Arrays;
public class QuickSort<T extends Comparable<? super T>> {
public void sort (T[] arr)
{
if (arr == null || arr.length <= 1)
return;
sort(arr, 0, arr.length - 1);
}
public void sort(T[] arr, int left, int right)
{
int l = left;
int r = right;
if (right <= left)
return;
//Find the pivot in the middle
T pivot = arr[(left + (right - left)/2)];
while (l <= r)
{
// check values on left are bigger than the pivot
while (arr[l].compareTo(pivot) < 0)
{
l++;
}
// check if values are smaller than the pivot
while (arr[r].compareTo(pivot) > 0)
{
r--;
}
// l and r have gone past each other swap them
if (l <= r)
{
//swap process
T temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
// left pointer goes up 1
// right pointer goes down 1
l++;
r--;
}
}
if (left < r)
sort(arr, left, r);
if (l < right)
sort(arr, l, right);
}
public static void main(String args[])
{
Integer[] numbers=new Integer[] {3,2,5,4,1};
System.out.println(Arrays.asList(numbers));
new QuickSort<Integer>().sort(numbers);
System.out.println(Arrays.asList(numbers));
}
}
Output:
[3, 2, 5, 4, 1]
[1, 2, 3, 4, 5]
I have two implementation of quick sort the first one uses a median of (fist ,middle , last ) as pivot and the second uses the middle element as pivot
the first Implementation :
public class quickMedian {
public void sort(int array[])
// pre: array is full, all elements are non-null integers
// post: the array is sorted in ascending order
{
quickSort(array, 0, array.length - 1); // quicksort all the elements in the array
}
public void quickSort(int array[], int start, int end)
{
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) // check that there are at least two elements to sort
{
if (array[start+(end-start)/2]>array[end]){
swap(array,start+(end-start)/2, end);
}
if (array[start]>array[end]){
swap(array,start, end);
}
if (array[start+(end-start)/2]>array[start]){
swap(array, start+(end-start)/2, start);
}
int pivot = array[start]; // set the pivot as the first element in the partition
while (k > i) // while the scan indices from left and right have not met,
{
while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first
i++; // element greater than the pivot
while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first
k--; // element not greater than the pivot
if (k > i) // if the left seekindex is still smaller than
swap(array, i, k); // the right index, swap the corresponding elements
}
swap(array, start, k); // after the indices have crossed, swap the last element in // the left partition with the pivot
quickSort(array, start, k - 1); // quicksort the left partition
quickSort(array, k + 1, end); // quicksort the right partition
}
else // if there is only one element in the partition, do not do any sorting
{
return; // the array is sorted, so exit
}
}
public void swap(int array[], int index1, int index2)
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
int temp = array[index1]; // store the first value in a temp
array[index1] = array[index2]; // copy the value of the second into the first
array[index2] = temp; // copy the value of the temp into the second
}
}
The second Implementation :
public class quickSort {
private int array[];
private int length;
public void sort(int[] inputArr) {
if (inputArr == null || inputArr.length == 0) {
return;
}
this.array = inputArr;
length = inputArr.length;
quickSorter(0, length - 1);
}
private void quickSorter(int lowerIndex, int higherIndex) {
int i = lowerIndex;
int j = higherIndex;
// calculate pivot number, I am taking pivot as middle index number
int pivot = array[lowerIndex+(higherIndex-lowerIndex)/2];
// Divide into two arrays
while (i <= j) {
/**
* In each iteration, we will identify a number from left side which
* is greater then the pivot value, and also we will identify a number
* from right side which is less then the pivot value. Once the search
* is done, then we exchange both numbers.
*/
while (array[i] < pivot) {
i++;
}
while (array[j] > pivot) {
j--;
}
if (i <= j) {
exchangeNumbers(i, j);
//move index to next position on both sides
i++;
j--;
}
}
// call quickSort() method recursively
if (lowerIndex < j)
quickSorter(lowerIndex, j);
if (i < higherIndex)
quickSorter(i, higherIndex);
}
private void exchangeNumbers(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
To obtain the median we need to do extra steps on each recursion which may increase the time a little bit (if the array is totally random) .
I am testing these two classes on an array of size N=10,000,000 randomly generated and I have done the test many times the first Implementation takes around 30 seconds and the second takes around 4 seconds
so this is obviously not caused by the extra overhead to get the median of three numbers .
There must be something wrong with the first implementation, what is it ?
here is the testing code :
public static void main(String[] args) {
File number = new File ("f.txt");
final int size = 10000000;
try{
// quickSort s = new quickSort();
quickMedian s = new quickMedian();
writeTofile(number, size);
int [] arr1 =readFromFile(number, size);
long a=System.currentTimeMillis();
s.sort(arr1);
long b=System.currentTimeMillis();
System.out.println("quickSort: "+(double)(b-a)/1000);
}catch (Exception ex){ex.printStackTrace();}
}
I have given this a lot of thought and was unable to find the most optimal solution. I am preparing for technical interviews, but I haven't found very much stuff related to this question. My first step was to implement a naive O(n) algorithm that searches through the entire array to find the maximum integer. Now I know I can do much better than this, so I thought maybe there was a way to use Binary Search or take advantage of the fact that at least one half of the array is fully sorted. Maybe you could find the middle value and compare it to the start and end of the array.
Example:
[5, 7, 11, 1, 3] would return 11.
[7, 9, 15, 1, 3] would return 15.
In a sorted array (even rotated), you can be sure to use binary search (O(log2(n)).
/**
* Time complexity: O(log2(n))
* Space complexity: O(1)
*
* #param nums
* #return
*/
public int findMax(int[] nums) {
// binary search
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[left] < nums[mid]) {
left = mid;
} else if (nums[left] > nums[mid]) {
right = mid - 1;
} else {
// subtility here if there are duplicate elements in the array.
// shift the left linearly
left = left + 1;
}
}
return nums[left];
}
You have to binary search in a clever way to achieve a O(lg n) bound. Observe that the element to the right of the max element is the min (or none if the array is not rotated at all). So do a regular binary search but check that the element at index mid is the max, if not compare the first and last elements in each of the left/right subarrays. If first<last in the left subarray, you know that the left subarray is sorted and go right, otherwise you go left.
Let's assume that array is called a and it has n elements.
/* check if not rotated at all */
int ans = INFINITY;
if(a[0] < a[n-1] || n == 1)
{ ans = a[n-1];
return;
}
/* array is certainly rotated */
int l = 0, r = n-1;
while(r - l > 5)
{ int m = (l + r) / 2;
if(a[m] > a[m+1]) { ans = a[m]; break; }
else
{ if(a[l] < a[m-1]) l = m+1;
else r = m-1;
}
}
/* check the remaining elements (at most 5) in a loop */
if(ans == INFINITY)
{ for(int i = l; i <= r; i++)
{ ans = max(ans, a[i]);
}
}
I've not tested this code. The reason i break when the number of elements is 5 or less is to be sure that number of elements in either subarray is at least 2 (so you can be sure that first and last are not the same element). You've got to try this yourself and fix it if there is anything to fix. Hope this helps.
Use modified binary search to eliminate half the sorted subarray (if there are two sorted subarrays remove the "lower" subarray) in each step while keeping track of a potentially updated maximum.
#include <iostream>
#include <cstdlib>
#include <vector>
int main(int argc, char** argv)
{
std::vector<int> nums;
for(int i = 1; i < argc; i++)
nums.push_back(atoi(argv[i]));
int start = 0;
int end = argc - 2;
int max = nums[start];
while(start <= end) {
int mid = (start + end) >> 1;
int cand;
if(nums[start] <= nums[mid]) {
start = mid + 1;
} else {
end = mid - 1;
}
cand = nums[mid];
if(cand > max)
max = cand;
}
std::cout << max << std::endl;
return 0;
}
Question : find largest in the rotated sorted array.The array don't have any duplicates:
Solution : Using Binary Search.
The Idea : Always remember in a Sorted Rotated Array, the largest element will always be on the left side of the array. Similarly, the smallest element will always be on the right side of the array.
The code is :
public class Test19 {
public static void main(String[] args) {
int[] a = { 5, 6, 1, 2, 3, 4 };
System.out.println(findLargestElement(a));
}
private static int findLargestElement(int[] a) {
int start = 0;
int last = a.length - 1;
while (start + 1 < last) {
int mid = (last - start) / 2 + start;
if (mid < start) {
mid = start;
}
if (mid > start) {
last = mid - 1;
} else {
mid--;
}
} // while
if (a[start] > a[last]) {
return a[start];
} else
return a[last];
}
}
The solution I've come up with is both compact and efficient.
It is basically a spin-off of the Binary Search Algorithm.
int maxFinder(int[] array, int start, int end)
{
//Compute the middle element
int mid = (start + end) / 2;
//return the first element if it's a single element array
//OR
//the boundary pair has been discovered.
if(array.length == 1 || array[mid] > array[mid + 1])
{return mid;}
//Basic Binary Search implementation
if(array[mid] < array[start])
{return maxFinder(array, start, mid - 1);}
else if(array[mid] > array[end])
{return maxFinder(array, mid + 1, end);}
//Return the last element if the array hasn't been rotated at all.
else
{return end;}
}
Speaking of using binary search to solve this problem at a time complexity of O(log2n). I would do as the following
#include<stdio.h>
#define ARRSIZE 200
int greatestElement(int* , int ) ;
int main() {
int arr[ARRSIZE] ;
int n ;
printf("Enter the number of elements you want to enter in the array!") ;
scanf("%d" , &n) ;
printf("Enter the array elements\n") ;
for(int i = 0 ; i < n ; i++) {
scanf("%d", &arr[i]) ;
}
printf("%d is the maximum element of the given array\n",greatestElement(arr,n)) ;
}
int greatestElement(int* arr, int n) {
int mid = 0 ;
int start = 0 , end = n-1 ;
while(start < end) {
mid = (start+end)/2 ;
if(mid < n-1 && arr[mid] >= arr[mid+1]) {
return arr[mid] ;
}
if(arr[start] > arr[mid]) {
end = mid - 1 ;
}
else {
start = mid + 1;
}
}
return arr[start] ;
}```
This question is so easy with another version of binary search:
int solve(vector<int>& a) {
int n = a.size();
int k=0;
for(int b=n/2; b>=1; b/=2)
{
while(k+b<n && a[k+b] >= a[0])
k += b;
}
return a[k];
}
We're learning about quickSort. The book provides the code at the end of my question.
I'm curious about this call at the end of the findPivot method:
swap(array, left++, right--);
Why have the "++" and "--" in there? It's not incrementing/decrementing either variable (before or after the swap) and it's not accessing (for example) array[left + 1].
So what gives?
EDIT
So before I posted the question I wrote the following test code:
public static void main(String[] args) {
int a = 0;
int b = 2;
int[] array = {1,10,20,30};
swap(array, a++,b--);
}
public static void swap(int[]array,int a, int b)
{
for(int i = 0; i < 10; i++)
{
Integer temp = array[a];
array[a] = array[b];
array[b] = temp;
System.out.println("a = " + a + "\nb = " + b + "\narray a: " + array[a] + "\narray b: " + array[b]);
}
The results are as follows:
a = 0
b = 2
array a: 20
array b: 1
a = 0
b = 2
array a: 1
array b: 20
a = 0
b = 2
array a: 20
array b: 1
The variable isn't post-incrementing at all when used in the method. That's why I asked the question.
Thanks. Here's the code:
private static void swap(Integer[] array, int i, int j)
{
Integer temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void quickSort(Integer[] array, int left, int right)
{
if(left < right)
{
int pivot = findPivot(array, left, right);
quickSort(array, left, pivot - 1);
quickSort(array, pivot + 1, right);
}
}//quickSort
public static int findPivot(Integer[] array, int left, int right)
{
int first = left++;
while (left <= right)
{
while (left <= right && array[first].compareTo(array[left]) > 0)
{
left++;
}
while (left <= right && array[first].compareTo(array[right]) < 0)
{
right--;
}
if (left < right)
swap(array, left++, right--);
}
swap(array, first, right);
return right;
}
It are post-increment (§15.14.2) (++) and post-decrement (§15.14.3) (--) operations. These will change the values for the next iteration in your while loop.
You basically have this:
while (left <= right)
{
// ...
if (left < right)
{
swap(array, left, right);
left++;
right--;
}
}
As you can see, the "post" means that the values are not affected for that particular statement. After evaluating the post increment operation, it will the variable will be increased, but the value passed to the method was still the old one. For the advanced readers, you could write the post-increment operator like this (pseudo code):
public int operator this++()
{
int temp = this;
++this; // regular pre-increment (JLS §15.15.1)
return temp;
}
For a read about the pre-increment operator, you can check JLS §15.15.1.
swap(array, left++, right--); is inside of a while loop, so the updated values will be used in the next loop iteration.
java increment(++) and decrement(--)
lets suppose i++; this means i+1 and i-- means i-1
in programming
int i = 1;
System.out.println("i : "+(i++)); // this means first print then add `1` into i;
//and
System.out.println("i : "+(++i)); // this means first add one and then print it;
// same for '--'
They are the post increment (++) and post decrement (--) operation, so they will change the values in the next iteration in your while loop
In your code at this line
swap(array, left++, right--);//left++ , right-- are post increment (++) and post decrement (--) operation
I try to find k-th minimum element using my code, but can't fix an error in my code.
When it try to make partitioning for [0, 0, 2] with pivot = 0 it's looping.
import java.util.Arrays;
public class OrderStat {
public static void main(String[] args) {
int[] uA = {13, 32, 28, 17, 2, 0, 14, 34, 35, 0};
System.out.println("Initial array: " + Arrays.toString(uA));
int kth = 3; // We will try to find 3rd smallest element(or 2nd if we will count from 0).
int result = getKthSmallestElement(uA, 0, uA.length - 1, kth - 1);
System.out.println(String.format("The %d smallest element is %d", kth, result));
System.out.println("-------------------------------------");
Arrays.sort(uA);
System.out.println("Sorted array for check: " + Arrays.toString(uA));
}
private static int getKthSmallestElement(int[] uA, int start, int end, int kth) {
int l = start;
int r = end;
int pivot = uA[start];
System.out.println("===================");
System.out.println(String.format("start=%d end=%d", start, end));
System.out.println("pivot = " + pivot);
//ERROR HERE: When we will work with [0, 0, 2] part of array with pivot = 0 it will give us infinite loop;
while (l < r) {
while (uA[l] < pivot) {
l++;
}
while (uA[r] > pivot) {
r--;
}
if (l < r) {
int tmp = uA[l];
uA[l] = uA[r];
uA[r] = tmp;
}
}
System.out.println("After partitioning: " + Arrays.toString(uA) + "\n");
if (l < kth)
return getKthSmallestElement(uA, l + 1, end, kth);
else if (l > kth)
return getKthSmallestElement(uA, start, l - 1, kth);
return uA[l];
}
}
Explain me, please, how to fix this problem.
After swapping
if (l < r) {
int tmp = uA[l];
uA[l] = uA[r];
uA[r] = tmp;
}
you need to move l and r (or at least one of them, to make any progress) to the next position (++l; --r;). Otherwise, if both values are equal to the pivot, you loop infinitely.
A correct partitioning that is also usable in a quicksort would be
// make sure to call it only with valid indices, 0 <= start <= end < array.length
private int partition(int[] array, int start, int end) {
// trivial case, single element array - garbage if end < start
if(end <= start) return start;
int pivot = array[start]; // not a good choice of pivot in general, but meh
int left = start+1, right = end;
while(left < right) {
// move left index to first entry larger than pivot or right
while(left < right && array[left] <= pivot) ++left;
// move right index to last entry not larger than pivot or left
while(right > left && array[right] > pivot) --right;
// Now, either
// left == right, or
// left < right and array[right] <= pivot < array[left]
if (left < right) {
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
// move on
++left;
--right;
}
}
// Now left >= right.
// If left == right, we don't know whether array[left] is larger than the pivot or not,
// but array[left-1] certainly is not larger than the pivot.
// If left > right, we just swapped and incremented before exiting the loop,
// so then left == right+1 and array[right] <= pivot < array[left].
if (left > right || array[left] > pivot) {
--left;
}
// Now array[i] <= pivot for start <= i <= left, and array[j] > pivot for left < j <= end
// swap pivot in its proper place in the sorted array
array[start] = array[left];
array[left] = pivot;
// return pivot position
return left;
}
Then you can find the k-th smallest element in an array
int findKthSmallest(int array, int k) {
if (k < 1) throw new IllegalArgumentException("k must be positive");
if (array.length < k) throw new IllegalArgumentException("Array too short");
int left = 0, right = array.length-1, p;
--k; // 0-based indices
while(true) {
p = partition(array, left, right);
if (p == k) return array[p];
if (p < k) {
left = p+1;
k -= left;
} else {
right = p-1;
}
}
// dummy return, never reached
return 0;
}