2
votes

I am really confused about what exactly the differences between Kth smallest Element and Kth element.

Kth element = kth element is an array = array[k-1]

but, what is a kth smallest element? I have a homework question, where I need to write an algorithm to find the kth smallest element in 2 sorted arrays. I am not here to ask you to do my homework, you don't need to give me any algorithm or code. All I want is to understand what does it meant by kth smallest element. What is the different between Kth smallest element and kth element.

The reason why I asked this is: I google what is kth smallest element, one of the website:

For example if A = [10, 20, 40, 60] and B =[15, 35, 50, 70, 100] and K = 4 
then solution should be 35 because union of above arrays will be C = 
[10,15,20,35,40,50,60,70,100] and fourth smallest element is 35.

It is exactly the same as the kth element of the array. A U B[k-1] is the answer.

Another example is:

A = [3, 5, 9, 15, 27, 33, 35, 41, 57, 65]
B = [1, 16, 18, 42, 44, 46, 48, 50, 52, 54]

AUB = [1, 3, 5, 9, 15, 16, 18, 27, 33, 35, 41, 42, 44, 46, 48, 50, 52, 54, 57, 65]
and if k = 6
then AUB[6-1] = 16;
if k = 8
then AUB[8-1] = 27;

Am I correct? Is there any exception that kth smallest element is not at AUB[k-1]? If yes, can you give me an example, and explain?

Edit: I just saw someone say that the kth smallest element is the array[k-1] in ascending order.

I asked my teacher a question:

When we talk about kth element, is it at a[k] or a[k-1]

His answer is:

Read the problem statement carefully. The output should be the k-th smallest element among the 2n elements in S U T. That output is not necessarily at index k of either list. Why should it be?

I don't understand. The output is not necessarily at index k of either list? What does it means?

4
So yes you need AUB[k-1], whar your teacher is saying that AUB[k-1] != A[k-1] && AUB[k-1] != B[k-1] is possiblePhoton
What is the index of the smallest element in the list [42, 0, 5]?Code-Apprentice
@Prune I politely disagree, this is about terminology and communication, which there are both well used tags for (OP didn't use them but that's fine for a new member). Not everything in software development is programming, thus closing this question would be odd imho.MrDeal
@MrDeal: thanks for the perspective; I have no problem with being the only closure vote -- that's why it takes more than one of me. This is on the borderline in my experience, so I've raised the issue.Prune
@Prune You're right it is on the border and I appreciate your view on it, too. My main concern was just where OP should have asked instead. CS SE would probably have sent them here I'd say, maybe CS Educators SE considering the question is about phrasing of an assignment?MrDeal

4 Answers

1
votes

As you already pointed out the union of the two arrays will be the what you are looking in. So here is an example:

S = [0,4,5,7]
T = [1,2,8,9]
then A = S v T = [0,1,2,4,5,7,8,9]

Now when you are looking in this array you will find that the k'th element is at index k-1. This is because we tend to start counting from one up. So we say the first element and we mean the element at index 0.

Following with that, this is also the answer to your other question. Since you have two arrays, the k'th smallest number will be at A[k-1], but what your teacher meant is that in either one of the arrays, so S and T they might not be at index k-1. In the above example the 5th smallest number is 5 at index 4 of A but it is the third element in S or S[2].

1
votes

The output is not necessarily at index k of either list? What does it means?

It means that you should solve the problem without creating C, aka AUB.

You should instead iterate both arrays in parallel until you find the kth smallest element.

Pseudo logic:

Ai = 0, Bi = 0
Loop K-1 times:
    if A[Ai] < B[Bi] then Ai++ else Bi++
kth smallest = min(A[Ai], B[Bi])

Example

A = [10, 20, 40, 60], B =[15, 35, 50, 70, 100], K = 4

Ai = 0, Bi = 0: A[0] < B[0] so Ai++
Ai = 1, Bi = 0: A[1] > B[0] so Bi++
Ai = 1, Bi = 1: A[1] < B[1] so Ai++
Ai = 2, Bi = 1: min(A[2], B[1]) = 35

4th smallest value is 35, found at B[1].

As you can see, output is not at index 3 (=4-1) of either list.


Kth smallest Element and Kth Element?

And because you never create a combined list, but instead work directly on two different lists, there is no Kth element, so the question posed in the title is meaningless.

0
votes

Union of two arrays is simply an array containing all the elements of both the arrays.

For example A[1,20,40,70] and B[10,50,60,80] Union of above 2 arrays may be C[1,20,40,70,10,50,60,80]

Now assuming range of k starts from 1(inclusive), suppose k = 3 , now kth element is 40 but kth smallest element is 20.

The method to do this efficiently lies as how you approach this. One (not too efficient) approach may be simply use k nested iterations and find the kth smallest element from the unsorted union array.

Another approach may be to sort the array after taking union, another way is simply merge the two arrays such that the resultant union is made sorted (merge sort : merge procedure). In such a case the resultant array will have kth smallest element same as kth element.

0
votes

As others said, K-th smallest element is arr[k], after sorting ascendingly.

This is also known as Selection algorithm, and the best known algorithm for random input array is Quick select, runs in O(n) time, which is closely related to Quick Sort.

I happen to wrote an implementation recently, you may take a look.


Code - in Java

QuickSelect.java

/**
 * Find k-th smallest element from an array, via quick select.
 *
 * @author eric
 * @date 3/24/19 3:49 PM
 */
public class QuickSelect {
    /**
     * Find k-th smallest element, of given array.
     *
     * @param arr input array, will be modified (sorted partially),
     * @param k   k-th, start from 0,
     * @return index of k-th, in the array,
     */
    public static int findKth(int[] arr, int k) {
        if (k < 0 || k >= arr.length)
            throw new IllegalArgumentException("array length = " + arr.length + ", thus k should < " + arr.length + ", but get: " + k);
        return findKth(arr, k, 0, arr.length - 1);
    }

    /**
     * Find k-th smallest element, of given sub array.
     *
     * @param arr   input array, will be modified (sorted partially),
     * @param k     k-th, start from 0,
     * @param start inclusive
     * @param end   inclusive
     * @return index of k-th, in the array,
     */
    public static int findKth(int[] arr, int k, int start, int end) {
        if (start == end && start == k) return k; // base case,
        int pvt = end; // index of pivot, initially taken from last element of sub array,

        // check each element in sub array,
        for (int i = start; i <= end; i++) {
            if (i < pvt && arr[i] > arr[pvt]) { // on left of pivot, and it's larger,
                if (pvt - i == 1) { // neighbor, just switch,
                    int tmp = arr[i];
                    arr[i] = arr[pvt];
                    arr[pvt] = tmp;
                } else { // not neighbor,
                    // swap 3 positions,
                    int tmp = arr[i];
                    arr[i] = arr[pvt - 1];
                    arr[pvt - 1] = arr[pvt];
                    arr[pvt] = tmp;

                    pvt -= 1; // adjust pvt,

                    i--; // restart from i,
                }
            } else if (i > pvt && arr[i] < arr[pvt]) { // on right of pivot, and it's smaller,
                if (i - pvt == 1) { // neighbor, just switch,
                    int tmp = arr[i];
                    arr[i] = arr[pvt];
                    arr[pvt] = tmp;
                } else {
                    // swap 3 positions,
                    int tmp = arr[i];
                    arr[i] = arr[pvt + 1];
                    arr[pvt + 1] = arr[pvt];
                    arr[pvt] = tmp;

                    pvt += 1; // adjust pvt,
                    // continue from i+1;
                }
            }
        }

        int leftSize = pvt - start; // element count on left side of pivot, in sub array,
        if (leftSize == k) { // pivot itself is k-th,
            return pvt;
        } else if (leftSize > k) {
            return findKth(arr, k, start, pvt - 1); // find on left part,
        } else {
            return findKth(arr, k - leftSize - 1, pvt + 1, end); // find on right part,
        }
    }
}

QuickSelectTest.java
(Test case, via TestNG)

import eric.algorithm.dynamic.ShufflePerfectly;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.Arrays;

/**
 * QuickSelect test.
 *
 * @author eric
 * @date 3/24/19 3:50 PM
 */
public class QuickSelectTest {
    private int size = 20; // array size, should be even,
    private int[] arr; // array with unique elements,
    private int[] arrDup; // array with duplicated elements,

    @BeforeMethod
    private void setUp() {
        // init - arr,
        arr = new int[size];
        for (int i = 0; i < size; i++) arr[i] = i;
        ShufflePerfectly.shuffle(arr); // shuffle,
        // System.out.printf("[initial] arr = %s\n", Arrays.toString(arr));

        // init - arrDup,
        arrDup = new int[size];
        int halfIdx = size / 2;
        for (int i = 0; i < halfIdx; i++) {
            arrDup[i] = i;
            arrDup[i + halfIdx] = i;
        }
        ShufflePerfectly.shuffle(arrDup); // shuffle,
        // System.out.printf("[initial] arrDup = %s\n", Arrays.toString(arrDup));
    }

    @Test
    public void test() {
        System.out.printf("\n[initial]: arr = %s\n", Arrays.toString(arr));

        for (int i = 0; i < arr.length; i++) {
            // setUp(); // re-random array,
            int idx = QuickSelect.findKth(arr, i);

            Assert.assertEquals(idx, i); // check index,
            Assert.assertEquals(arr[idx], i); // check value,
            System.out.printf("[after %d-th]: arr = %s\n", i, Arrays.toString(arr));
        }
    }

    @Test
    public void test_dup() {
        System.out.printf("\n[initial]: arrDup = %s\n", Arrays.toString(arrDup));

        for (int i = 0; i < arr.length; i++) {
            // setUp(); // re-random array,
            int idx = QuickSelect.findKth(arrDup, i);

            Assert.assertEquals(idx, i); // check index,
            Assert.assertEquals(arrDup[idx], i / 2); // check value,
            System.out.printf("[after %d-th]: arrDup = %s\n", i, Arrays.toString(arrDup));
        }
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void test_invalid_outOfRange() {
        QuickSelect.findKth(arr, arr.length);
    }

    @Test(expectedExceptions = IllegalArgumentException.class)
    public void test_invalid_negative() {
        QuickSelect.findKth(arr, -1);
    }
}

Tips:

  • It will modify input array, unless a copy is made.
  • It support duplicated elements.
  • The code is for understanding the algorithm, not for production.
    For production, maybe need to choose the pivot more randomly, I am not sure.

For your original input

Since you got 2 sorted array, the algorithm above is not necessary.

You could simply loop through the 2 array in a single loop, with one pointer for each array, and add the pointer with smaller value by 1 on each step, till the total step is k.