Quicksort (from the book Programming perls) - java

I am trying to learn all I can about sorting algorithms. On today's agenda is the Quicksort. Heres what I got.
QuickSort Class:
import java.util.*;
public class QuickSort{
public static void swap(int A[], int x ,int y){
int temp = A[x];
A[x] = A[y];
A[y] = temp;
}
public static int[] QSort(int A[], int L, int U){
Random randomGenerator = new Random();
if (L >= U){
System.out.printf("The value of L: %d, U: %d\n",L,U);
return A; // Sorted.
}
if (L < U ){
int randomInt = randomGenerator.nextInt(U);
swap( A, L, randomInt);
int T = A[L];
int M = L;
for (int i = L+1;i < U; i++){
if ( A[i] < T){
M = M+1;
swap(A, M, i);
}
}
swap(A, L, M);
QSort(A, L, M-1 );
QSort(A, M+1, U );
}
//System.out.println(Arrays.toString(A));
return A;
}
}
The Main:
import java.util.*;
public class Main{
public static void main(String [] args){
int[] intArray = {1,3,2,4,56,0,4,2,4,7,80,120,99,9,10,67};
System.out.printf("Original Array was: %s\n\n",Arrays.toString(intArray));
System.out.printf("Size of Array is: %d\n",intArray.length);
QuickSort qs = new QuickSort();
int[] A = qs.QSort(intArray, 5, intArray.length);
System.out.println(Arrays.toString(intArray));
}
}
Well that's all have till now. The code compiles and everything but the sorting algorithm is wrong. I am trying to understand logically whats happening in the QuickSort algorithm and used the book Programming Perls to aid my understanding.
Here's the list of questions I have:
In the QuickSort class in the for loop according to the book, "i's" conditional clause needs to be "i <=U" but if I do that the code gives me an 'Array index out of bounds' error. Why is that happening ? I know what an 'array index out of bounds' error is, I just can't grasp why the array location wont exist?
The first if clause checks to see if the array is sorted. When I compile the code this clause is meet on the first try(which should not happen in the first place). And if it is why isn't the function ended via the return A line?
The book I am using is by John Bentley page 112.

Apart from other anomalies already pointed out,
Your question 1:
As already stated the initial value for U should be intArray.length - 1.
int[] A = qs.QSort(intArray, 5, intArray.length - 1 );
Your question 2:
The first if clause does not check if the array is sorted, it just checks whether the U and L pointers have crossed each other or not.
For quicksort, this means that for the current pivot, all items less than pivot are to its left and all elements greater than pivot are to its right.
This crossing may happen multiple times during the sort depending on the number of pivot changes required to completely sort the array.
For more information on quicksort, following link may be helpful. http://www.cplusplus.com/faq/sequences/sequencing/sort-algorithms/quicksort/

In your invocation of QSort the upper bound is exclusive
QSort(A, L, M-1 );
but here you miss to sort the number at index M-1.
It should be
QSort (A, L, M);
PS. and what #LeeNeverGup said about the random.
Basically, this:
import java.util.*;
public class QSort {
public static void swap(int A[], int x, int y) {
int temp = A[x];
A[x] = A[y];
A[y] = temp;
}
public static void sort (int A[], int L, int U) {
Random randomGenerator = new Random();
if (L + 1 >= U)
return;
int randomInt = L + randomGenerator.nextInt(U - L);
swap(A, L, randomInt);
int T = A[L];
int M = L;
for (int i = L + 1; i < U; i++) {
if (A[i] < T) {
M = M + 1;
swap(A, M, i);
}
}
swap(A, L, M);
sort(A, L, M);
sort(A, M + 1, U);
}
public static void main(String[] args) {
int[] intArray =
{ 1, 3, 2, 4, 56, 0, 4, 2, 4, 7, 80, 120, 99, 9, 10, 67 };
System.out.printf( "Original Array was: %s\n\n",
Arrays.toString(intArray));
System.out.printf("Size of Array is: %d\n", intArray.length);
QSort.sort (intArray, 0, intArray.length);
System.out.println(Arrays.toString(intArray));
}
}

In this line : int randomInt = randomGenerator.nextInt(U);
You should get a random int between L and U, by using:
int randomInt = L + randomGenerator.nextInt(U - L);
Also, if you want U to be the largest index in the array, you shoule pass to it intArray.length - 1

Related

What mistake am I making while memoizing this solution for Leetcode problem jump game 4

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

Pick from both sides?

The problem statement is :
Given an integer array A of size N.
You can pick B elements from either left or right end of the array A to get maximum sum.
Find and return this maximum possible sum.
NOTE: Suppose B = 4 and array A contains 10 elements then:
You can pick first four elements or can pick last four elements or can pick 1 from front and 3 from back etc . you need to return the maximum possible sum of elements you can pick.
public class Solution {
ArrayList<Integer> c = new ArrayList<>();
ArrayList<Integer> A= new ArrayList<>();
public int solve(ArrayList<Integer> A, int B) {
if (B>A.size()){
int sum=0;
for(int i=0;i<A.size();i++)
sum= sum+A.get(i);
return sum;
}
int max_sum=0;
for(int i=0;i<A.size();i++){
if((max_sum<suffix(A.size()-(B-i))+prefix(i-1)) ){
max_sum=suffix(A.size()-(B-i))+prefix(i-1);
}
}
return max_sum;
}
int prefix_sum=0;
int prefix(int a) {
for(int p=0;p<a+1;p++){
c=A;
prefix_sum=prefix_sum + c.get(p);
}
return prefix_sum;
}
int suffix_sum=0;
int suffix(int b){
c=A;
for(int q=b;q<c.size();q++){
suffix_sum=suffix_sum+c.get(q);
}
return suffix_sum;
}
}
I am getting runtime error, I have tried to implement the suffix and prefix methods which return the sum from the index[ 0, i] and sum from [i, N-i] respectively, then in the solve function I am trying to find the sum of prefix [a-1] +suffix[N-(b-a)] and find out the maximum sum, the syntax is completely correct, there is something wrong with the logic I assume, please help me find the correct solution by correcting this code instead of providing an alternative method
package com.array;
import java.util.Arrays;
import java.util.List;
public class PickFromBothSides {
public static void main(String[] args) {
Integer[] arr = { 5, -2, 3, 1, 2 };
System.out.println(solve(Arrays.asList(arr), 3));
}
public static int solve(List<Integer> A, int B) {
int n = A.size();
int result = 0;
for (int i = 0; i < B; i++) {
result += A.get(i);
}
int sum = result;
for (int i = 0; i < B; i++) {
sum -= A.get(B - 1 - i);
sum += A.get(n - 1 - i);
result = Math.max(result, sum);
}
return result;
}
}
Runtime O(n)
Space complexity O(1)
You are declaring int prefix_sum=0; and int suffix_sum=0; as fields, not as local variables of the respective methods.
You are calling suffix(A.size()-(B-i)) so with your example that is 10 - (4 -i) which is 6 + i. You iterate through i being in the range {0, ..., 10} so the value 6 + i will be all the numbers 6 through 16. You cannot index in the array above 9, so you get an exception.
You need to change
for(int i=0;i<A.size();i++){
to
for(int i=0; i <= B; i++){
because you are trying to ask each iteration "how many numbers are taken from the beginning"? 0, 1, 2, 3 or 4 if B is 4
Other upgrades:
You are calling suffix(A.size()-(B-i))+prefix(i-1)) twice in a row. Call it only once, store it in a variable and reuse.
You are calling prefix(i-1) but inside prefix() you are using the parameter a as a + 1. You don't need to subtract one and add one to the same thing

How to implement randomized O(n) algorithm for finding median of unsorted array in Java?

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.

Equal Partitioning with Minimum Sum Difference

I'm trying to write this algorithm in Java following the steps below:(I know other solutions, just trying to figure out this one)
int min_diff = LARGE_NUMBER;
int diff;
for (each subset S of size n/2 of A) {
diff = abs(sum(S) – sum(A-S));
if (diff < min_diff) {
min_diff = diff;
TempSet = S;
}
}
print min_diff, TempSet;
I tried to find all subset permutations of size n/2 using the code from this link: https://www.geeksforgeeks.org/print-subsets-given-size-set/
The code in this link print all permutations. I thought first I need to store the arrays in an ArrayList so I can use them in the for loop, but I couldn't get it to work. The code below gives wrong output (every array is 60 60 60 instead of permutations:
static List<int[]> intArrays = new ArrayList<>();
static void combinationUtil(int[]arr, int n, int r, int index, int[] data, int i)
{
if (index == r) {
intArrays.add(data);
return;
}
if (i >= n)
return;
data[index] = arr[i];
combinationUtil(arr, n, r, index + 1, data, i + 1);
combinationUtil(arr, n, r, index, data, i + 1);
}
static void printCombination(int arr[], int n, int r)
{
int data[] = new int[r];
combinationUtil(arr, n, r, 0, data, 0);
for(int[] arr1:intArrays){
System.out.println(Arrays.toString(arr1));
}
}
public static void main(String[] args)
{
int arr[] = { 10, 20, 30, 40,50,60};
int n=arr.length;
int r=n/2;
printCombination(arr, n, r);
}
Can anyone tell me what's wrong with my code? Or how can I solve this problem following the above steps?
Your problem is when you do intArrays.add(data);
intArrays always contains the reference to data array. The array is passed by reference. You gets {60, 60, 60} because is the last state of data array (the last subset).
To fix the problem you must to do intArrays.add(data.Clone()); if exists Clone function or similar in you language or just implement it yourself.
Code in C#. Sorry I don't have any java compiler installed.
static int[] CloneArray(int[] arr)
{
int[] ret = new int[arr.Length];
for (int i = 0; i < arr.Length; ++i) ret[i] = arr[i];
return ret;
}

Algorithm to find the narrowest intervals, m of which will cover a set of numbers

Let's say you have a list of n numbers. You are allowed to choose m integers (lets call the integer a). For each integer a, delete every number that is within the inclusive range [a - x, a + x], where x is a number. What is the minimum value of x that can get the list cleared?
For example, if your list of numbers was
1 3 8 10 18 20 25
and m = 2, the answer would be x = 5.
You could pick the two integers 5 and 20. This would clear the list because it deletes every number in between [5-5, 5+5] and [20-5, 20+5].
How would I solve this? I think the solution may be related to dynamic programming. I do not want a brute force method solution.
Code would be really helpful, preferably in Java or C++ or C.
Hints
Suppose you had the list
1 3 8 10 18 20 25
and wanted to find how many groups would be needed to cover the set if x was equal to 2.
You could solve this in a greedy way by choosing the first integer to be 1+x (1 is the smallest number in the list). This would cover all elements up to 1+x+x=5. Then simply repeat this process until all numbers are covered.
So in this case, the next uncovered number is 8, so we would choose 8+x=10 and cover all numbers up to 10+x=12 in the second group.
Similarly, the third group would cover [18,24] and the fourth group would cover [25,29].
This value of x needed 4 groups. This is too many, so we need to increase x and try again.
You can use bisection to identify the smallest value of x that does cover all the numbers in m groups.
A recursive solution:
First, you need an estimation, you can split in m groups, then estimated(x) must be ~ (greather - lower element) / 2*m. the estimated(x) could be a solution. If there is a better solution, It has lower x than extimated(x) in all groups! and You can check it with the first group and then repeat recursively. The problem is decreasing until you have only a group: the last one, You know if your new solution is better or not, If there'is better, you can use it to discard another worse solution.
private static int estimate(int[] n, int m, int begin, int end) {
return (((n[end - 1] - n[begin]) / m) + 1 )/2;
}
private static int calculate(int[] n, int m, int begin, int end, int estimatedX){
if (m == 1){
return estimate(n, 1, begin, end);
} else {
int bestX = estimatedX;
for (int i = begin + 1; i <= end + 1 - m; i++) {
// It split the problem:
int firstGroupX = estimate(n, 1, begin, i);
if (firstGroupX < bestX){
bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m-1, i, end, bestX)));
} else {
i = end;
}
}
return bestX;
}
}
public static void main(String[] args) {
int[] n = {1, 3, 8, 10, 18, 20, 25};
int m = 2;
Arrays.sort(n);
System.out.println(calculate(n, m, 0, n.length, estimate(n, m, 0, n.length)));
}
EDIT:
Long numbers version: Main idea, It search for "islands" of distances and split the problem into different islands. like divide and conquer, It distribute 'm' into islands.
private static long estimate(long[] n, long m, int begin, int end) {
return (((n[end - 1] - n[begin]) / m) + 1) / 2;
}
private static long calculate(long[] n, long m, int begin, int end, long estimatedX) {
if (m == 1) {
return estimate(n, 1, begin, end);
} else {
long bestX = estimatedX;
for (int i = begin + 1; i <= end + 1 - m; i++) {
long firstGroupX = estimate(n, 1, begin, i);
if (firstGroupX < bestX) {
bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m - 1, i, end, bestX)));
} else {
i = end;
}
}
return bestX;
}
}
private static long solver(long[] n, long m, int begin, int end) {
long estimate = estimate(n, m, begin, end);
PriorityQueue<long[]> islands = new PriorityQueue<>((p0, p1) -> Long.compare(p1[0], p0[0]));
int islandBegin = begin;
for (int i = islandBegin; i < end -1; i++) {
if (n[i + 1] - n[i] > estimate) {
long estimatedIsland = estimate(n, 1, islandBegin, i+1);
islands.add(new long[]{estimatedIsland, islandBegin, i, 1});
islandBegin = i+1;
}
}
long estimatedIsland = estimate(n, 1, islandBegin, end);
islands.add(new long[]{estimatedIsland, islandBegin, end, 1});
long result;
if (islands.isEmpty() || m < islands.size()) {
result = calculate(n, m, begin, end, estimate);
} else {
long mFree = m - islands.size();
while (mFree > 0) {
long[] island = islands.poll();
island[3]++;
island[0] = solver(n, island[3], (int) island[1], (int) island[2]);
islands.add(island);
mFree--;
}
result = islands.poll()[0];
}
return result;
}
public static void main(String[] args) {
long[] n = new long[63];
for (int i = 1; i < n.length; i++) {
n[i] = 2*n[i-1]+1;
}
long m = 32;
Arrays.sort(n);
System.out.println(solver(n, m, 0, n.length));
}
An effective algorithm can be(assuming list is sorted) ->
We can think of list as groups of 'm' integers.
Now for each group calculate 'last_element - first_element+1', and store maximum of this value in a variable say, 'ans'.
Now the value of 'x' is 'ans/2'.
I hope its pretty clear how this algorithm works.
I think it's similarly problem of clusterization. For example You may use k-means clustering algorithm: do partitions of initial list on m classes and for x get maximum size divided by two of obtained classes.
1) You should look into BEST CASE, AVERAGE CASE and WORST CASE complexities with regards to TIME and SPACE complexities of algorithms.
2) I think David Pérez Cabrera has the right idea. Let's assume average case (as in the following pseudo code)
3) Let the list of integers be denoted by l
keepGoing = true
min_x = ceiling(l[size-1]-l[0])/(2m)
while(keepGoing)
{
l2 = l.copy
min_x = min_x-1
mcounter = 1
while(mcounter <= m)
{
firstElement = l2[0]
// This while condition will likely result in an ArrayOutOfBoundsException
// It's easy to fix this.
while(l2[0] <= firstElement+2*min_x)
{ remove(l2[0]) }
mcounter = mcounter+1
}
if(l2.size>0)
keepGoing = false
}
return min_x+1
4) Consider
l = {1, 2, 3, 4, 5, 6, 7}, m=2 (gives x=2)
l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=2
l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=3

Categories

Resources