Find the Kth smallest element. looping - java

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;
}

Related

Quick sort method is giving me a stackoverflowerror?

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]

Finding Index bounds for a recursive subsequence

I have a quick question about a maximum subsequence algorithm that uses divide and conquer shown below:
private static int maxSum3Recursive(int[] a, int left, int right)
{
if (left == right)
{
if (a[left] > 0)
{
return a[left];
}
else {
return 0;
}
}
int center = (left + right) / 2;
int maxLeftSum = maxSum3Recursive(a, left, center);
int maxRightSum = maxSum3Recursive(a, center + 1, right);
int maxLeftBorderSum = 0;
int leftBorderSum = 0;
for (int i = center; i >= left; i--)
{
leftBorderSum += a[i];
if (leftBorderSum > maxLeftBorderSum)
{
maxLeftBorderSum = leftBorderSum;
}
}
int maxRightBorderSum = 0;
int rightBorderSum = 0;
for (int i = center + 1; i <= right; i++)
{
rightBorderSum += a[i];
if (rightBorderSum > maxRightBorderSum)
{
maxRightBorderSum = rightBorderSum;
}
}
return Math.max(maxLeftSum, Math.max(maxRightSum, maxLeftBorderSum + maxRightBorderSum));
}
The algorithm works as expected and gives me the maximum sum, however, I'm having trouble finding the start and end index for the bounds for the maximum. For example, let's say we have an array: {5, -5, 2, 9}. The sum would be 2 + 9 = 11 and index would be [2 - 3] since the sum has to be consecutive. In other words, we can't just use 5 and 9 to make 14 because they are not next to each other.
I would appreciate any help figuring out the indexes.

Given a rotated sorted array, how can I find the largest value in that array?

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];
}

Find Kth min elem by randomized pivot method. Some weird bug

I try to use "randomized pivot" method to find the Kth min elem among given array.
[The code]
public class FindKthMin {
// Find the Kth min elem by randomized pivot.
private static void exchange (int[] givenArray, int firstIndex, int secondIndex) {
int tempElem = givenArray[firstIndex];
givenArray[firstIndex] = givenArray[secondIndex];
givenArray[secondIndex] = tempElem;
}
private static int partition (int[] givenArray, int start, int end, int pivotIndex) {
// Debug:
//System.out.println("debug: start = " + start);
//System.out.println(">> end = " + end);
//System.out.println(">> pivotIndex = " + pivotIndex);
int pivot = givenArray[pivotIndex];
int left = start - 1;
int right = end;
boolean hasDone = false;
while (!hasDone) {
while (!hasDone) {
left ++;
if (left == right) {
hasDone = true;
break;
}
if (givenArray[left] >= pivot) {
// Exchange givenArray[left] and the givenArray[right].
exchange(givenArray, left, right);
break;
}
}
while (!hasDone) {
right --;
if (left == right) {
hasDone = true;
break;
}
if (givenArray[right] < pivot) {
// Exchange the givenArray[right] and the givenArray[left].
exchange(givenArray, right, left);
break;
}
}
}
givenArray[right] = pivot;
// Debug:
//System.out.println(">> split = " + right);
//System.out.println();
return right;
}
private static int findKthMin_RanP_Helper (int[] givenArray, int start, int end, int k) {
if (start > end) return -1;
// Generate a random num in the range[start, end].
int rand = (int)(start + Math.random() * (end - start + 1));
// Using this random num as the pivot index to partition the array in the current scope.
int split = partition(givenArray, start, end, rand);
if (k == split + 1) return givenArray[split];
else if (k < split + 1) return findKthMin_RanP_Helper(givenArray, start, split - 1, k);
else return findKthMin_RanP_Helper(givenArray, split + 1, end, k);
}
public static int findKthMin_RanP (int[] givenArray, int k) {
int size = givenArray.length;
if (k < 1 || k > size) return -1;
return findKthMin_RanP_Helper(givenArray, 0, size - 1, k);
}
// Main method to test.
public static void main (String[] args) {
// Test data: {8, 9, 5, 2, 8, 4}.
int[] givenArray = {8, 9, 5, 2, 8, 4};
// Test finding the Kth min elem by randomized pivot method.
System.out.println("Test finding the Kth min elem by randomized pivot method, rest = " + findKthMin_RanP(givenArray, 1));
}
}
But the result is unstable, sometimes right and sometimes wrong.
Please have a look at the 5th row of findKthMin_RanP_Helper method:
If I change this int split = partition(givenArray, start, end, rand); to int split = partition(givenArray, start, end, end);, the result is always correct. I really can not find what's wrong with this.
EDIT:
The problem comes from the "partition", the new partition should like this:
private static int partition_second_version (int[] givenArray, int start, int end, int pivotIndex) {
int pivot = givenArray[pivotIndex];
int left = start;
int right = end;
while (left <= right) {
while (givenArray[left] < pivot) left ++;
while (givenArray[right] > pivot) right --;
if (left <= right) {
// Exchange givenArray[left] and givenArray[right].
exchange(givenArray, left, right);
left ++;
right --;
}
}
return left;
}
And the findKthMin_RanP_Helper should be changed like this:
private static int findKthMin_RanP_Helper (int[] givenArray, int start, int end, int k) {
if (start > end) return -1;
// Generate a random num in the range[start, end].
int rand = start + (int)(Math.random() * ((end - start) + 1));
// Using this random num as the pivot index to partition the array in the current scope.
int split = partition_second_version (givenArray, start, end, rand);
if (k == split) return givenArray[split - 1];
else if (k < split) return findKthMin_RanP_Helper(givenArray, start, split - 1, k);
else return findKthMin_RanP_Helper(givenArray, split, end, k);
}
Your partition routine could be simplified...
private static int partition(int[] givenArray, int start, int end, int pivotIndex) {
final int pivot = givenArray[pivotIndex];
int left = start;
int right = end;
while (left < right) {
while (left < givenArray.length && givenArray[left] <= pivot) {
left++;
}
while (right > -1 && givenArray[right] > pivot) {
right--;
}
if (left >= right) {
break;
}
exchange(givenArray, right, left);
}
return right;
}
The one bug I see in your code is your partition routine. In the first exchange call, it is not guaranteed that the right index will always point to a value which is < pivot.

CompareTo() method in Java

//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;
}
}

Categories

Resources