第K个最小元素和第K个元素?

时间:2019-03-22 17:05:36

标签: java algorithm sorting

我对第K个最小元素和第K个元素之间的确切区别感到困惑。

第K个元素=第k个元素是一个数组= array [k-1]

但是,第k个最小元素是什么?我有一个作业问题,我需要编写一种算法来找到2个排序数组中的第k个最小元素。我不是在这里要您做作业,您不需要给我任何算法或代码。我只想了解第k个最小元素的含义。第K个最小元素和第k个元素有什么区别。

我问这个的原因是: 我用Google搜索什么是第k个最小元素,该网站之一:

forward_iterator_tag

它与数组的第k个元素完全相同。答案是U B [k-1]。

另一个例子是:

template<class I>
  concept ForwardIterator =
    InputIterator<I> &&
    DerivedFrom<ITER_CONCEPT(I), forward_iterator_tag> &&
    Incrementable<I> &&
    Sentinel<I, I>;

我正确吗?是否有第k个最小元素不在AUB [k-1]处的例外情况?如果是,请给我一个例子,并解释一下吗?

编辑:我刚刚看到有人说第k个最小元素是数组[k-1]升序。

我问老师一个问题:

当我们谈论第k个元素时,它是在a [k]还是a [k-1]

他的答案是:

仔细阅读问题说明。输出应该是S U T中2n个元素中第k个最小的元素。该输出不一定在任何一个列表的索引k处。为什么会这样呢?

我不明白。 输出不一定在任何一个列表的索引k上是什么意思?

4 个答案:

答案 0 :(得分:1)

正如您已经指出的那样,这两个数组的并集将成为您要查找的内容。因此,这里是一个示例:

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

现在,当您查看此数组时,您会发现第k个元素位于索引k-1处。这是因为我们倾向于从 one 开始计数。所以我们说第一个元素,就是指索引为0的元素。

接着,这也是您其他问题的答案。由于您有两个数组,因此第k个最小的数字将是A[k-1],但您的老师的意思是在两个数组中的任何一个中,因此ST可能不会位于索引k-1。在上面的示例中,第五个最小的数字是5的索引4上的A,但它是SS[2]中的第三个元素。

答案 1 :(得分:1)

  

输出不一定在任一列表的索引k上是什么意思?

这意味着您应该在不创建C(也称为AUB)的情况下解决问题。

您应该并行地迭代两个数组,直到找到第k个最小元素为止。

伪逻辑:

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

示例

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

位于35处的第4个最小值是 B[1]

如您所见,输出不在两个列表的索引3(= 4-1)上。


  

第K个最小元素和第K个元素?

并且因为您从不创建组合列表,而是直接在两个不同的列表上工作,所以没有Kth元素,因此标题中提出的问题毫无意义。

答案 2 :(得分:0)

两个数组的联合只是一个包含两个数组的所有元素的数组。

例如PROCEDURE p_buscar_salario_emp3 FUNCTION f_foo 以上2个数组的并集可能是A[1,20,40,70] and B[10,50,60,80]

现在,假设k的范围从1(含)开始,假设k = 3,则第k个元素为40,但第k个最小元素为20。

有效执行此操作的方法取决于您的处理方式。一种(不太高效)的方法可能只是使用k个嵌套迭代,然后从未排序的并集数组中找到第k个最小的元素。

另一种方法可能是在合并后对数组进行排序,另一种方法是简单地合并两个数组,以便对生成的联合进行排序(合并排序:合并过程)。在这种情况下,所得数组将具有与第k个元素相同的第k个最小元素。

答案 3 :(得分:0)

正如其他人所说,第K个最小的元素是arr[k],在 递增排序之后。

这也称为Selection algorithm,随机输入数组的最著名算法是Quick select,运行时间为O(n),与Quick Sort密切相关。

我碰巧最近写了一个实现,您可以看一下。


代码-在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
(测试用例,通过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);
    }
}

提示:

  • 除非复制,否则它将修改输入数组。
  • 它支持重复的元素。
  • 代码用于理解算法,而不是用于生产。
    对于生产,我不确定,可能需要更加随机地选择支点。

为您的原始输入内容

由于您有2个已排序数组,因此不需要上述算法。

您可以简单地在一个循环中遍历2个数组,每个数组只有一个指针,然后在每个步骤中将值较小的指针相加1,直到总步数为k。