1

Algorithm:

Procedure SELECT( k,S)
{ if ISI =1 then return the single element in S
   else { choose an element a randomly from S;
              let S1,S2,and S3 be he sequences of elements in S  
              less than, equal to, and greater than m, respectively;
             if IS1I >=k then return SELECT(k,S1)
              else
                   if (IS1I + IS2I >=k then return m
                   else return SELECT(k-IS1I-IS2I , S3);
             }
}

The question is to implement the first algorithm for finding the kth smallest integer in a set of integers and test your program for different sets of integers generated by a random number generator. Below is my solution.

import java.util.Random;
import java.util.Scanner;

public class main {
    private static Random rand = new Random();
    private static Scanner keyboard = new Scanner(System.in);

    public static int firstAlgorithm(int k, int[] S) {
        int m = S[rand.nextInt(S.length)];
        int[] S1 = new int[S.length];
        int[] S2 = new int[S.length];
        int[] S3 = new int[S.length];
        int p = 0;
        int q = 0;
        int r = 0;

        if (S.length == 1)
            return S[0];

        for (int i = 0; i < S.length; i++) {
            if (S[i] < m) {
                S1[p] = S[i];
                p++;
            } else if (S[i] == m) {
                S2[q] = S[i];
                q++;
            } else {
                S3[r] = S[i];
                r++;
            }
        }

        S1 = trimToSize(S1, p);
        S2 = trimToSize(S2, q);
        S3 = trimToSize(S3, r);

        if (S1.length >= k)
            return firstAlgorithm(k, S1);
        else if (S1.length + S2.length >= k)
            return m;
        else
            return firstAlgorithm(k - S1.length - S2.length, S3);
    }

    private static int[] trimToSize(int[] arr, int size) {
        int[] temp = new int[size];
        for (int i = 0; i < size; i++) {
            temp[i] = arr[i];
        }
        return temp;
    }

    public static void printArray(int[] S) {
        for (int i = 0; i < S.length; i++) {
            System.out.print(S[i] + "\t");
            if (i % 10 == 9)
                System.out.println();
        }
    }

    // start main method
    public static void main(String[] args) {
        System.out.print("Enter the size of an array: ");
        int size = keyboard.nextInt();
        while (size < 1) {
            System.out.println("Size of the array should be greater than 0.");
            System.out.print("Enter the size of an array: ");
            size = keyboard.nextInt();
        }

        System.out.print("Enter the value of k: ");
        int k = keyboard.nextInt();
        while (k < 1 || k > size) {
            System.out.println("Value of k should be in the range 1-" + size + ".");
            System.out.print("Enter the value of k: ");
            k = keyboard.nextInt();
        }

        int[] S = new int[size];
        for (int i = 0; i < size; i++) {
            S[i] = 100 + rand.nextInt(900);
        }

        System.out.println("\nRandom values generated in the array:");
        printArray(S);
        System.out.println();
        System.out.println(k + "th smallest value in the array using Algorithm #1: " + firstAlgorithm(k, S));

    }
}

But I need to implement the above algorithm without using a temporary array for partitioning. How can I do it?

Kim Gordon
  • 31
  • 6
  • if your input is: `1, 4, 2, 6, 2` and k = 2 what should be its output? – Papai from BEKOAIL Jul 09 '21 at 06:41
  • The answer should be 2 because the 2th smallest of that array is 2 – Kim Gordon Jul 09 '21 at 06:47
  • The (inplace) partition algorithm is the same as in quicksort. You can find pseudo-code here: https://en.wikipedia.org/wiki/Quicksort#Lomuto_partition_scheme – Paul Hankin Jul 09 '21 at 06:55
  • Note that your algorithm is recursive, and I don't think java does tall-call optimization. That means as-written, your code may take O(n) space (on the stack). You should also get rid of "trimToSize" and use a view into your array (eg like this: https://stackoverflow.com/questions/39218468/get-a-view-of-the-portion-of-java-array). Maintaining high and low indices yourself is an easy and good alternative to the list sub-view. – Paul Hankin Jul 09 '21 at 06:59
  • @Paul Hankin. I just updated to given algorithm above. – Kim Gordon Jul 09 '21 at 07:01
  • https://en.wikipedia.org/wiki/Quickselect (especially the final implementation under "algorithm" is what you can aim for. – Paul Hankin Jul 09 '21 at 07:02
  • @PaulHankin It's pretty confusing to me. Can you give me an example base on my code, please? – Kim Gordon Jul 09 '21 at 07:10
  • The pseudo-code in https://en.wikipedia.org/wiki/Quickselect is better than any code I can write and implements the algorithm you provided. It seems straightforward to turn it into java -- it doesn't do anything fancy. – Paul Hankin Jul 09 '21 at 07:13
  • @Paul Hankin. The problem is that I must use the Algorithm above. I can't use the algorithm that you gave m – Kim Gordon Jul 09 '21 at 07:22
  • The two algorithms are the same. The algorithm you added to your question is higher-level, and it abstracts away from the details of partitioning and how the array ranges are recursed into. The wikipedia version provides those details. – Paul Hankin Jul 09 '21 at 07:40

1 Answers1

0

The algorithm is Dijkstra's 3-way partition.

You will have to modify the original S.

Untested (pseudo) code ahead

public static int partition(int left, int right, int[] S) {
  int m = rand.nextInt(right-left);  // protect against malicious data
  swap(S[left+m], S[right]);
  int equal = left;

  while (left < right) {
    if (a[left] < a[n])
      swap(S, left++, equal++)
    else if (a[left] == a[n]) 
      swap(S, left, --right);
    else 
      left++;
  }

  return left, equal;
}


public static int firstAlgorithm(int k, int left, int right, int[] S) {
  if (left == right)
     return S[left];

  int p, e = partition(left, right, S); // returns 2 values. S1=[0,p), S2=[p,e), S3=[e, n)

  if (p >= k)
    return firstAlgorithm(k, left, p, S);
  else if (e >= k)  // p < k
     return S[p]; // p is the first equal, e is first larger than equal
  else // e < k
    return firstAlgorithm(k, e, right, S);
}


// test
S = {1, 4, 2, 6, 2};
k = 2;
int result = firstAlgorithm(2, 0, S.length-1, S);
assert(result == 2);

Warning syntax and off-by-one errors guarantied.

See here multiple ways to return 2 values in java.

Surt
  • 15,501
  • 3
  • 23
  • 39