I want to modify QuickSort (in Java) so that every time Partition is called, the median of the proportioned array is used as the pivot.
I have a median selection algorithm in Java that returns the kth smallest element, in this case the median. I have tons of quicksort algorithms in java that all work by themselves and sort an array. Unfortunately I can't combine those two in order to achieve the above... Everytime I try it i usually get stackoverflow erros.
Can anybody show me code to see how it can be done?
Thanks
EDIT: For example this is a median selection algorithm that I have tried to use.
public int quickSelect(int[] A, int p, int r, int k) {
if (p==r) return A[p];
int q = Partition(A,p,r);
int len = q-p+1;
if (k == len) return A[q];
else if (k<len) return Select(A,p,q-1,k);
else return Select(A,q+1,r,k-len);
}
public int partition(int[]A, int p, int r) {
int x = A[r];
int i = p-1;
for (int j = p; j<=r-1; j++) {
if (A[j] <= x) {
i++;
swap(A,i,j);
}
}
swap(A,i+1,r);
return i+1;
}
It works by itself but when I try to call quickSelect through quicksort's partition function to return the pivot to be used, it doesn't work. Obviously I'm doing something wrong but I don't know what. Unfortunately on the Internet I haven't found any algorithm, even in pseudocode, that would combine a median selection with quicksort.
The standard way to get the median is to sort the data. And you want to sort the data by partitioning on the median. This seems very chicken and egg to me.
Could you elaborate on why you want to partition/pivot on the median?
What you are looking for is the Selection Algorithm. Here's a link with pseudocode.
From the link:
In computer science, a selection algorithm is an algorithm for finding the kth smallest number in a list
To find the median you want to find the k=floor((n+1)/2) smallest number in the list where n is the size of the list.
Note that in PARTITION the pivot is A[r].
public int QUICKSORT2(int[] A, int p, int r) {
if (p<r) {
int median=Math.floor((p + r) /2) - p + 1
int q=SELECT(A, p, r, median)
q=PARTITION2(A, p, r, q)
QUICKSORT2(A, p, q-1)
QUICKSORT2(A, q+1, r)
}
}
public int PARTITION2(int[]A, int p, int r, int q) {
int temp = A[r];
A[r]=A[q];
A[q]=temp;
return PARTITION(A, p, r)
}
You can use this ...
int Select(int array[],int start, int end,int k){
if(start==end){
return start;
}
int x=array[end];
int i=start-1;
for(int j=start;j<=end-1;j++){
if(array[j]<x){
i++;
Swap(array+i,array+j);
}
}
i++;
Swap(array+i,array+end);
if(i==k){
return i;
}
else if(i>k){
return Select(array,start,i-1,k);
}
else{
return Select(array,i+1,end,k);
}
}
Select will partition array on kth smallest element in array;
Related
So the question is
You are given a 0-indexed integer array nums and an integer k.
You are initially standing at index 0. In one move, you can jump at most k steps forward without going outside the boundaries of the array. That is, you can jump from index i to any index in the range [i + 1, min(n - 1, i + k)] inclusive.
You want to reach the last index of the array (index n - 1). Your score is the sum of all nums[j] for each index j you visited in the array.
Return the maximum score you can get.
Example 1:
Input: nums = [1,-1,-2,4,-7,3], k = 2
Output: 7
Explanation: You can choose your jumps forming the subsequence [1,-1,4,3] (underlined above). The sum is 7.
I wrote a recursive solution in which we explore all the possibilies . If the function is called at index ind , then the value at index ind is added to a variable curSum for the next function call.
The base condition is when we reach the last index , we will return curSum+value of last index.
Here is the code:
class Solution {
static int min= Integer.MIN_VALUE;
public int maxResult(int[] nums, int k) {
return max(nums,0,k,0);
}
public int max(int [] nums, int ind, int k, int curSum)
{
if(ind==nums.length-1)
return curSum+nums[ind];
int max=Integer.MIN_VALUE;
for(int i=ind+1;i<=Math.min(nums.length-1,ind+k);i++)
max=Math.max(max, max(nums,i,k,curSum+nums[ind]));
return max;
}
}
Code works fine except the exponential complexity ofcourse.
I tried memoizing it as
class Solution {
static int[] dp;
static int min= Integer.MIN_VALUE;
public int maxResult(int[] nums, int k) {
dp=new int[nums.length];
Arrays.fill(dp,min);
return max(nums,0,k,0);
}
public int max(int [] nums, int ind, int k, int curSum)
{
if(ind==nums.length-1)
return curSum+nums[ind];
if(dp[ind]!=min)
return dp[ind];
int max=Integer.MIN_VALUE;
for(int i=ind+1;i<=Math.min(nums.length-1,ind+k);i++)
max=Math.max(max, max(nums,i,k,curSum+nums[ind]));
return dp[ind]=max;
}
}
But this solution gives the wrong max everytime and I am not quite able to figure out why.
Any hints will be appreciated.
Thanks
You only need to memoize dp[index] instead of calling f(index, currSum).
Take for example present arr.length = 5, k = 2
For index-2 you need wether index 3 or 4 gives better points to you irrespective of your present point.
Better way would be :
class Solution {
static int[] dp;
static int min= Integer.MIN_VALUE;
public int maxResult(int[] nums, int k) {
dp=new int[nums.length];
Arrays.fill(dp,min);
return max(nums,0,k,0);
}
public int max(int [] nums, int ind, int k, int curSum)
{
// base-case
if(ind==nums.length-1)
return curSum+nums[ind];
// if already memoized
if(dp[ind]!=min)
return dp[ind] + curSum;
// if not memoized, calculate value now
int max=Integer.MIN_VALUE;
for(int i=ind+1;i<=Math.min(nums.length-1,ind+k);i++)
max=Math.max(max, max(nums,i,k,nums[ind]);
// memoize here
dp[ind] = max
return dp[ind] + curSum;
}
}
I just learned about the Binary Search algorithm and I tried to implement it. I have used a few test cases and it seems to work fine. However, when I checked on GeeksforGeeks, there are quite a few differences in the way they handled the indexing. Can anyone shed some lights on whether my implementation is good or not. And if not, how would it fail to work?
Here's my code:
static int binarySearch(int arr[], int i, int r, int x) {
if(r > 1) {
int middle = r/2;
if(arr[middle] == x) {
return middle;
}
if(arr[middle] > x) {
return binarySearch(arr, i, middle, x);
}else {
return binarySearch(arr, middle, r+1, x);
}
}
return -1;
}
Suppose that our function is going to do a binary search on the subarray arr[l..r] (inclusive). Here, r - l + 1 is the length of this subarray and when it's not a positive number (or r < l), we don't have a valid subarray and we can't search for anything, so the function stops the recursion and returns -1.
You know, we always make two subarrays with (almost) equal length. So, middle = r/2 is not correct and middle = (l + r) / 2 is correct. The new subarrays are arr[l..middle-1] and arr[middle+1..r]. We divide the subarray after checking its middle element, so these two subarrays should not contain the middle index as you see.
Now, we can rewrite your code.
static int binarySearch(int arr[], int l, int r, int x) {
if (r >= l) {
int middle = (l + r) / 2; //int middle = l + (r - l) / 2; for avoiding integer overflow
if(arr[middle] == x)
return middle;
if (arr[middle] > x)
return binarySearch(arr, l, middle - 1, x);
else
return binarySearch(arr, middle + 1, r, x);
}
return -1;
}
And this is a non-recursive version of binary search algorithm. Usually, it can work slightly faster.
static int binarySearch2(int arr[], int l, int r, int x) {
while (r >= l) {
int middle = (l + r) / 2; //int middle = l + (r - l) / 2; for avoiding integer overflow
if (arr[middle] == x)
return middle;
if (arr[middle] > x)
r = middle - 1;
else
l = middle + 1;
}
return -1;
}
Don't forget to sort your array before calling these functions.
Suppose that n is the length of the given array.
For 0-based arrays, the correct form of calling the functions is this:
binarySearch(arr, 0, n - 1, x);
and
binarySearch2(arr, 0, n - 1, x);
For 1-based arrays, the correct form of calling the functions is this:
binarySearch(arr, 1, n, x);
and
binarySearch2(arr, 1, n, x);
I have this code for finding median of an unsorted array in O(n) expected time, O(n^2) worst case. I use longs because I want to be able to hold long values.
public class Randomized {
long kthSmallestHelper(long arr[], long l, long r, long k)
{
if (k > 0 && k <= r - l + 1)
{
long pos = randomPartition(arr, l, r);
if (pos-l == k-1)
return arr[(int)pos];
if (pos - l > k - 1)
return kthSmallestHelper(arr, l, pos - 1, k);
return kthSmallestHelper(arr, pos + 1, r, k - pos + l - 1);
}
return Integer.MAX_VALUE;
}
void swap(long arr[], long i, long j)
{
long temp = arr[(int)i];
arr[(int)i] = arr[(int)j];
arr[(int)j] = temp;
}
long partition(long arr[], long l, long r)
{
long x = arr[(int)r], i = l;
for (long j = l; j <= r - 1; j++)
{
if (arr[(int)j] <= x)
{
swap(arr, i, j);
i++;
}
}
swap(arr, i, r);
return i;
}
long randomPartition(long arr[], long l, long r)
{
long n = r - l + 1;
long pivot = (long)(Math.random()) * (n - 1);
swap(arr, pivot + 1, r);
return partition(arr, l, r);
}
long kthSmallestRandom(long arr[], long k){
return kthSmallestHelper(arr, 0, arr.length - 1, k);
}
}
But when I run it
long[] array = {12, 3, 5, 7, 4, 19, 26}; //median is 7
Randomized rand = new Randomized();
System.out.println(rand.kthSmallestRandom(array, (array.length + 1)/2));
It's incorrect (it returns 4).
My idea was to use this version of the kth smallest number to say that I want the (length/2)th smallest, which is the median.
What is wrong with my idea or implementation?
There is a small error in your kthSmallestHelper function in this line:
if (pos-l == k-1)
return arr[(int)pos];
The return uses the wrong index. Try pos-l+1 instead of pos (and cast it to int). This returns the kth item, which was just sorted to its correct place in the array.
I'm not in a location to run it, but it seems like a lot could be wrong at this point.
In Java, when you're passing an array into a function only a copy of the array is passed, so any swaps you would do would not change the array outside of that method. Also looks like your partitions are off. Instead of passing in the partition you're just passing in the left and right bounds, and it looks like your partition method only ever sends things to the left of the partition.
This code should be commented a lot more so we can tell what you're trying to do at each point. ALso it looks like since you're swapping things into a sorted order it would be O(nlogn) since you can't sort in linear time. And why can't you use k == 0 for the kth smallest helper?
I don't know why, but just changing all longs to ints fixed it. It now works as expected.
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;
}
As I was testing my quicksort I noticed another problem. Sometimes it arranges the array in alphabetical order and sometimes it does not. For example if I have p, o, j, l as my array it sorts it to j, o, l, p which is wrong because l should be before o However if I add a to the array it sorts to a, j, l, o, p which is correct. Why is this happening?
Code:
private ArrayList<String> sort(ArrayList<String> ar, int lo, int hi){
if (lo < hi){
int splitPoint = partition(ar, lo, hi);
sort(ar, lo, splitPoint);
sort(ar, splitPoint +1, hi);
}
return ar;
}
private int partition(ArrayList<String> ar, int lo, int hi){
String pivot = ar.get(lo);
lo--;
hi++;
while (true){
lo++;
hi--;
while (lo<hi && ar.get(lo).compareTo(pivot) < 0){
lo++;
}
while (hi>lo && ar.get(hi).compareTo(pivot) >= 0){
hi--;
}
if (lo<hi){
swap(ar, lo, hi);
}else {
return hi;
}
}
}
private ArrayList<String> swap(ArrayList<String> ar, int a, int b){
String temp = ar.get(a);
ar.set(a, ar.get(b));
ar.set(b, temp);
return ar;
}
As far as I can see, you are not changing the position of pivot element. You are just swapping hi with lo.
At the end, you should place the pivot at its correct position and then return it.
I think there is a danger in the last iteration of your loop.
Suppose you had an array 2,1.
Your pivot is 2.
The lo index increases until it finds an element above the pivot of 1 or meets hi.
In this case it will meet hi and both lo and hi will be pointing to 1.
At this point your partition function returns and reports that the array has been partitioned into:
{2},{1}
But this is incorrect because 1 is < than the pivot so should have been in the first partition.
Perhaps when lo meets hi you should perform an extra test to see whether the element at hi should be included in the left or right partition?