第K个最小元素-创建的数组不能超过k个大小

时间:2019-12-05 06:22:40

标签: c++ algorithm

我实现了一种算法,该算法解决了在未排序的数组中找到 k th 个最小元素的问题。我使用了堆结构,并依靠此公式优化了代码

k 1 = n-k + 1

k 1 k 1 th 最大元素,所以我选择 k k 1 中较小的一个。

不过,我无法通过在线裁判传递时限错误。我不知道是否还要创建一个不超过 k 大小的数组,这会带来更好的复杂性。可能少于 k 个?或者,除了使用堆结构之外,还有另一种解决此问题的方法。

1 <= k <= n <= 10 5

代码:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

void minHeapify(int arr[], int n, int i)
{
    int largest = i; // Initialize largest as root 
    int l = 2 * i + 1; // left = 2*i + 1 
    int r = 2 * i + 2; // right = 2*i + 2 

    if (l < n && arr[l] < arr[largest])
        largest = l;

    if (r < n && arr[r] < arr[largest])
        largest = r;

    if (largest != i) {
        swap(arr[i], arr[largest]);

        minHeapify(arr, n, largest);
    }
}

void maxHeapify(int arr[], int n, int i)
{
    int smallest = i; // Initialize largest as root 
    int l = 2 * i + 1; // left = 2*i + 1 
    int r = 2 * i + 2; // right = 2*i + 2 

    if (l < n && arr[l] > arr[smallest])
        smallest = l;

    if (r < n && arr[r] > arr[smallest])
        smallest = r;

    if (smallest != i) {
        swap(arr[i], arr[smallest]);

        maxHeapify(arr, n, smallest);
    }
}

void buildMinHeap(int a[], int n) {
    for (int i = n / 2; i >= 0; i--)
        minHeapify(a, n, i);
}

void buildMaxHeap(int a[], int n) {
    for (int i = n / 2; i >= 0; i--)
        maxHeapify(a, n, i);
}

int kthsmallest(int minHeap[], int k, int n) {
    int i, temp;
    for (i = 0; i < k; i++)
        cin >> minHeap[i];

    buildMaxHeap(minHeap, k);

    for (i = k; i < n; i++)
    {
        cin >> temp;
        if (temp < minHeap[0])
        {
            minHeap[0] = temp;
            maxHeapify(minHeap, k, 0);
        }
    }
    return minHeap[0];
}

int kthlargest(int minHeap[], int k, int n) {    
    int i, temp;
    for (i = 0; i < k; i++)
        cin >> minHeap[i];

    buildMinHeap(minHeap, k);

    for (i = k; i < n; i++)
    {
        cin >> temp;
        if (temp > minHeap[0])
        {
            minHeap[0] = temp;
            minHeapify(minHeap, k, 0);
        }
    }
    return minHeap[0];    
}

int main() {//kth smallest element    
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n, k, k1;
    cin >> n >> k;
    k1 = n - k + 1;//kth smallest element is the same as k1th largest element

    if (k < k1) {
        int *minHeap = new int[k];
        cout << kthsmallest(minHeap, k, n);
    }    
    else {
        int *minHeap = new int[k1];
        cout << kthlargest(minHeap, k1, n);
    }
    return 0;
}

请帮助您找到更好的时间复杂度吗?

Problem

  

查找数组的第k个个最大元素

     

内存限制: 256 MB
  时间限制: 1 s
  输入:input.txt
  输出: output.txt

     
     

任务:

     

您会得到一个由 n 个整数组成的数组和一个自然的 k
  您必须找到数组的 k th 个最大元素。
  您创建的数组不能超过 k 个元素。

     

输入:

     

第一行包含自然的 n (1≤ n ≤10 5 )–   数组元素的数量以及自然的 k
  第二行包含 n 个数字-数组的元素。

     

输出:

     

数组中第 k th 个最大元素。

     

示例:

     
Input        | Output
-------------+-----------
6 2          | 7
7 4 6 3 9 1  | 

2 个答案:

答案 0 :(得分:1)

时间复杂度是最佳的,但是您可以使代码效率更高一点:

  • 不使用递归,而是迭代解决方案
  • 不要使用swap,而是将原始值保留在内存中,同时将子值复制到其父项,并且仅在到达适当的插槽后才存储初始值。
  • 不要执行两次2 * i:另一个子节点只是下一个。
  • 让heapify函数采用一个额外的参数,该参数可以是索引i上的当前值,也可以是它的替换值。这样可以节省一项任务。

这是查找两个heapify函数的方式:

void minHeapify(int arr[], int n, int i, int key) { // add key as parameter
    while (true) { // iterative
        int child = 2 * i + 1; // do this only for left child, and limit number of variables
        if (child+1 < n && arr[child] > arr[child+1]) // get child with least value
            child++; // the right child is just one index further
        if (child >= n || key <= arr[child]) break;
        arr[i] = arr[child]; // don't swap, just copy child value to parent
        i = child; // move down
    }
    arr[i] = key; // finally put the original value in the correct place
}

void maxHeapify(int arr[], int n, int i, int key) { // add key as parameter
    while (true) { // iterative
        int child = 2 * i + 1; // do this only for left child, and limit number of variables
        if (child+1 < n && arr[child] < arr[child+1]) // get child with greatest value
            child++; // the right child is just one index further
        if (child >= n || key >= arr[child]) break;
        arr[i] = arr[child]; // don't swap, just copy child value to parent
        i = child; // move down
    }
    arr[i] = key; // finally put the original value in the correct place
}

void buildMinHeap(int a[], int n) {
    for (int i = n / 2; i >= 0; i--)
        minHeapify(a, n, i, a[i]); // pass a[i] also
}

void buildMaxHeap(int a[], int n) {
    for (int i = n / 2; i >= 0; i--)
        maxHeapify(a, n, i, a[i]); // pass a[i] also
}

int kthsmallest(int heap[], int k, int n) {
    int i, temp;
    for (i = 0; i < k; i++)
        cin >> heap[i];

    buildMaxHeap(heap, k);

    for (i = k; i < n; i++) {
        cin >> temp;
        if (temp < heap[0])
            maxHeapify(heap, k, 0, temp); // pass temp
    }
    return heap[0];
}

int kthlargest(int heap[], int k, int n) {
    int i, temp;
    for (i = 0; i < k; i++)
        cin >> heap[i];

    buildMinHeap(heap, k);

    for (i = k; i < n; i++) {
        cin >> temp;
        if (temp > heap[0])
            minHeapify(heap, k, 0, temp); // pass temp
    }
    return heap[0];
}

在主函数中,您可以对 k == 1 k == n 进行特殊处理,因此不需要堆,只需min()max()

一件奇怪的事是,您链接到的挑战说的是“最大 k > 最小”。也许你混了。

这是工作返回最小的 k th 时的代码。但是请检查挑战,是否不应该对最大的 k th 做到这一点?

int main() {//kth smallest element    
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int n, k, k1;
    cin >> n >> k;
    k1 = n - k + 1;//kth smallest element is the same as k1th largest element

    if (k == 1) {
        int curr, next;
        cin >> curr;
        for (int i = 1; i < n; i++) {
            cin >> next;
            curr = min(curr, next);
        }
        cout << curr;
    } else if (k1 == 1) {
        int curr, next;
        cin >> curr;
        for (int i = 1; i < n; i++) {
            cin >> next;
            curr = max(curr, next);
        }
        cout << curr;
    } else if (k < k1) {
        int *heap = new int[k];
        cout << kthsmallest(heap, k, n);
    } else {
        int *heap = new int[k1];
        cout << kthlargest(heap, k1, n);
    }
    return 0;
}

答案 1 :(得分:0)

您假设使用较小的堆始终是最佳选择。您可能需要重新考虑。

例如,假设您想从100的列表中选择第96个最小的数字。如果您使用大小为96的堆,那么您将这样做:

  1. 用96个项目构建一个堆。 buildHeap为O(n),在这种情况下,n为96。
  2. 在96个项目的堆中最多进行4次插入。那将是4 * log(96)。

如果使用大小为4的堆,则将执行以下操作:

  1. 用4个项目构建一个堆。
  2. 在4个项目的堆中最多进行96次插入。那将是96 * log(4)。

第一个选项是96 + 4 * log(96)。 96的以2为底的对数约为6.58。因此插入的费用为26.32,总计122.32。

堆较小的第二个选项是4 + 96 * log(4)。 log(4)是2,所以最终得到4 + 196,或总共196。

较小的堆是这里的大输家。

通常,您想在(k + (n-k)*log(k)) < ((n-k) + k*log(n-k))时使用更大的堆。

也:

堆选择算法的实际运行时间对项目的显示顺序很敏感。例如,如果您正在寻找100,000个数组中的第1000个最小数字,那么如果数组按升序排列,则其运行速度将比按降序排列的速度快得多。原因?

因为在递增的情况下,您使用前1,000个项构建了初始堆,因此您无需再修改堆,因为以下项都不比堆中的最大项小。

但是,如果数组是降序排列的,那么您查看的每个项目都将小于堆中最大的项目,这意味着您将为所有99,000个剩余项目进行堆插入。

想象一下,如果其中一个测试用例是一个降序的大数组,那么代码的性能如何。

除非您已经证明选择使用哪种堆大小的方法明显更好,否则您可能要考虑使用大小为k的最大堆来选择“选择第k个最小”。