📜  流中第 K 个最大的元素

📅  最后修改于: 2021-10-28 01:54:41             🧑  作者: Mango

给定一个无限的整数流,找出任意时间点的第 k 个最大元素。
例子:

Input:
stream[] = {10, 20, 11, 70, 50, 40, 100, 5, ...}
k = 3

Output:    {_,   _, 10, 11, 20, 40, 50,  50, ...}

允许的额外空间为 O(k)。

我们在以下帖子中讨论了在数组中查找第 k 个最大元素的不同方法。
未排序数组中的第 K 个最小/最大元素 |设置 1
未排序数组中的第 K 个最小/最大元素 |组 2(预期线性时间)
未排序数组中的第 K 个最小/最大元素 |设置 3(最坏情况线性时间)
这里我们有一个流而不是整个数组,我们只能存储 k 个元素。

一个简单的解决方案是保留一个大小为 k 的数组。这个想法是保持数组排序,以便可以在 O(1) 时间内找到第 k 个最大元素(如果数组按递增顺序排序,我们只需要返回数组的第一个元素)
如何处理流的新元素?
对于流中的每个新元素,检查新元素是否小于当前第 k 大元素。如果是,则忽略它。如果否,则从数组中删除最小元素并按排序顺序插入新元素。处理一个新元素的时间复杂度是 O(k)。

更好的解决方案是使用大小为 k 的自平衡二叉搜索树。可以在 O(Logk) 时间内找到第 k 个最大的元素。
如何处理流的新元素?
对于流中的每个新元素,检查新元素是否小于当前第 k 大元素。如果是,则忽略它。如果不是,则从树中删除最小的元素并插入新元素。处理一个新元素的时间复杂度是 O(Logk)。

一个有效的解决方案是使用大小为 k 的最小堆来存储流的 k 个最大元素。第 k 个最大的元素总是在根处,可以在 O(1) 时间内找到。
如何处理流的新元素?
将新元素与堆的根进行比较。如果新元素较小,则忽略它。否则用新元素替换 root 并为修改后的堆的根调用 heapify。找到第 k 个最大元素的时间复杂度是 O(Logk)。

CPP
// A C++ program to find k'th
// smallest element in a stream
#include 
#include 
using namespace std;
 
// Prototype of a utility function
// to swap two integers
void swap(int* x, int* y);
 
// A class for Min Heap
class MinHeap {
    int* harr; // pointer to array of elements in heap
    int capacity; // maximum possible size of min heap
    int heap_size; // Current number of elements in min heap
public:
    MinHeap(int a[], int size); // Constructor
    void buildHeap();
    void MinHeapify(
        int i); // To minheapify subtree rooted with index i
    int parent(int i) { return (i - 1) / 2; }
    int left(int i) { return (2 * i + 1); }
    int right(int i) { return (2 * i + 2); }
    int extractMin(); // extracts root (minimum) element
    int getMin() { return harr[0]; }
 
    // to replace root with new node x and heapify() new
    // root
    void replaceMin(int x)
    {
        harr[0] = x;
        MinHeapify(0);
    }
};
 
MinHeap::MinHeap(int a[], int size)
{
    heap_size = size;
    harr = a; // store address of array
}
 
void MinHeap::buildHeap()
{
    int i = (heap_size - 1) / 2;
    while (i >= 0) {
        MinHeapify(i);
        i--;
    }
}
 
// Method to remove minimum element
// (or root) from min heap
int MinHeap::extractMin()
{
    if (heap_size == 0)
        return INT_MAX;
 
    // Store the minimum vakue.
    int root = harr[0];
 
    // If there are more than 1 items,
    // move the last item to
    // root and call heapify.
    if (heap_size > 1) {
        harr[0] = harr[heap_size - 1];
        MinHeapify(0);
    }
    heap_size--;
 
    return root;
}
 
// A recursive method to heapify a subtree with root at
// given index This method assumes that the subtrees are
// already heapified
void MinHeap::MinHeapify(int i)
{
    int l = left(i);
    int r = right(i);
    int smallest = i;
    if (l < heap_size && harr[l] < harr[i])
        smallest = l;
    if (r < heap_size && harr[r] < harr[smallest])
        smallest = r;
    if (smallest != i) {
        swap(&harr[i], &harr[smallest]);
        MinHeapify(smallest);
    }
}
 
// A utility function to swap two elements
void swap(int* x, int* y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}
 
// Function to return k'th largest element from input stream
void kthLargest(int k)
{
    // count is total no. of elements in stream seen so far
    int count = 0, x; // x is for new element
 
    // Create a min heap of size k
    int* arr = new int[k];
    MinHeap mh(arr, k);
 
    while (1) {
        // Take next element from stream
        cout << "Enter next element of stream ";
        cin >> x;
 
        // Nothing much to do for first k-1 elements
        if (count < k - 1) {
            arr[count] = x;
            count++;
        }
 
        else {
            // If this is k'th element, then store it
            // and build the heap created above
            if (count == k - 1) {
                arr[count] = x;
                mh.buildHeap();
            }
 
            else {
                // If next element is greater than
                // k'th largest, then replace the root
                if (x > mh.getMin())
                    mh.replaceMin(x); // replaceMin calls
                                      // heapify()
            }
 
            // Root of heap is k'th largest element
            cout << "K'th largest element is "
                 << mh.getMin() << endl;
            count++;
        }
    }
}
 
// Driver program to test above methods
int main()
{
    int k = 3;
    cout << "K is " << k << endl;
    kthLargest(k);
    return 0;
}


Java
// Java program for the above approach
import java.io.*;
import java.util.*;
 
class GFG {
   
  /*
  using min heap DS
 
  how data are stored in min Heap DS
         1
       2   3
  if k==3 , then top element of heap
  itself the kth largest largest element
 
  */
  static PriorityQueue min;
  static int k;
 
  static List getAllKthNumber(int arr[])
  {
     
    // list to store kth largest number
    List list = new ArrayList<>();
 
    // one by one adding values to the min heap
    for (int val : arr) {
 
      // if the heap size is less than k , we add to
      // the heap
      if (min.size() < k)
        min.add(val);
 
      /*
      otherwise ,
      first we  compare the current value with the
      min heap TOP value
 
      if TOP val > current element , no need to
      remove TOP , bocause it will be the largest kth
      element anyhow
 
      else  we need to update the kth largest element
      by removing the top lowest element
      */
 
      else {
        if (val > min.peek()) {
          min.poll();
          min.add(val);
        }
      }
 
      // if heap size >=k we add
      // kth largest element
      // otherwise -1
 
      if (min.size() >= k)
        list.add(min.peek());
      else
        list.add(-1);
    }
    return list;
  }
 
  // Driver Code
  public static void main(String[] args)
  {
    min = new PriorityQueue<>();
 
    k = 4;
 
    int arr[] = { 1, 2, 3, 4, 5, 6 };
 
    List res = getAllKthNumber(arr);
 
    for (int x : res)
      System.out.print(x + " ");
  }
 
    // This code is Contributed by Pradeep Mondal P
}


C++
// C++ program for the above approach
#include 
using namespace std;
 
vector kthLargest(int k, int arr[], int n)
{
    vector ans(n);
 
    // Creating a min-heap using priority queue
    priority_queue, greater > pq;
 
    // Iterating through each element
    for (int i = 0; i < n; i++) {
        // If size of priority
        // queue is less than k
        if (pq.size() < k)
            pq.push(arr[i]);
        else {
            if (arr[i] > pq.top()) {
                pq.pop();
                pq.push(arr[i]);
            }
        }
 
        // If size is less than k
        if (pq.size() < k)
            ans[i] = -1;
        else
            ans[i] = pq.top();
    }
 
    return ans;
}
 
// Driver Code
int main()
{
    int n = 6;
    int arr[n] = { 1, 2, 3, 4, 5, 6 };
    int k = 4;
   
    // Function call
    vector v = kthLargest(k, arr, n);
    for (auto it : v)
        cout << it << " ";
    return 0;
}


输出:

K is 3
Enter next element of stream 23
Enter next element of stream 10
Enter next element of stream 15
K'th largest element is 10
Enter next element of stream 70
K'th largest element is 15
Enter next element of stream 5
K'th largest element is 15
Enter next element of stream 80
K'th largest element is 23
Enter next element of stream 100
K'th largest element is 70
Enter next element of stream
CTRL + C pressed

使用优先队列实现:

C++

// C++ program for the above approach
#include 
using namespace std;
 
vector kthLargest(int k, int arr[], int n)
{
    vector ans(n);
 
    // Creating a min-heap using priority queue
    priority_queue, greater > pq;
 
    // Iterating through each element
    for (int i = 0; i < n; i++) {
        // If size of priority
        // queue is less than k
        if (pq.size() < k)
            pq.push(arr[i]);
        else {
            if (arr[i] > pq.top()) {
                pq.pop();
                pq.push(arr[i]);
            }
        }
 
        // If size is less than k
        if (pq.size() < k)
            ans[i] = -1;
        else
            ans[i] = pq.top();
    }
 
    return ans;
}
 
// Driver Code
int main()
{
    int n = 6;
    int arr[n] = { 1, 2, 3, 4, 5, 6 };
    int k = 4;
   
    // Function call
    vector v = kthLargest(k, arr, n);
    for (auto it : v)
        cout << it << " ";
    return 0;
}
输出
-1 -1 -1 1 2 3 

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程