#P2198. 第2题-输出区间内第k小的数
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 106
            Accepted: 30
            Difficulty: 6
            
          
          
          
                       所属公司 : 
                              京东
                                
            
                        
              时间 :2024年10月19日
                              
                      
          
 
- 
                        算法标签>堆          
 
第2题-输出区间内第k小的数
题面描述
给定一个长度为n的数组a下标从1开始对于所有i∈[1,n]∩Z,求出区间 [1,i] 中第k小的数。其中k为给定常数。如果区间内的数的数量不足k个,请输出−1。
思路
1. 使用最大堆 为了高效地求出每个前缀区间 [1,i] 的第 k 小的数,我们可以使用最大堆来动态维护前 k 小的元素。
最大堆:最大堆的特性是堆顶元素是堆中最大的元素。我们通过维护一个大小为 k 的最大堆,可以确保堆中保存的是当前区间的前 k 小的元素,而堆顶即为该区间的第 k 小元素。
2. 具体实现步骤
插入元素到堆:对于每个元素 a[i],我们将其插入到最大堆中。此时堆中可能有超过 k 个元素。
维护堆的大小:当堆中的元素超过 k 个时,我们弹出堆顶元素(即当前最大的元素)。这样堆中的元素始终保持为前 k 小的元素。
输出结果:在遍历每个前缀时,如果当前前缀长度不足 k,输出 -1;如果前缀长度大于等于 k,输出最大堆的堆顶元素(即第 k 小的数)。
3. 处理边界情况
如果当前前缀长度小于 k,直接输出 -1。 当前缀长度大于等于 k 时,输出最大堆的堆顶元素。
代码说明
代码说明(通用语言)
1.输入处理:
首先读取输入,包括数组长度 n 和所需的第 k 小的元素 k。 然后读取数组 a 的元素。数组的长度为 n,每个元素都被存储在一个列表或数组结构中。
2.最大堆的使用:
我们使用一个最大堆(通常用 priority_queue 实现)来动态维护当前区间内的前 k 小的元素。最大堆的特点是堆顶始终是当前堆中最大的元素。 对于每个元素 a[i],将其插入到最大堆中。 当堆的大小超过 k 时,我们从堆中弹出堆顶元素,即最大的那个元素。这样可以确保堆中只保留当前区间中最小的 k 个元素。
3.结果输出:
对于每个元素 a[i]: 如果当前区间的长度(即 i + 1)小于 k,则输出 -1,因为区间中的元素数量不足 k。 如果当前区间的长度大于或等于 k,则输出最大堆的堆顶元素。堆顶元素即为当前区间中的第 k 小的元素。 将每个 i 的结果存储在一个列表或数组中,最后按要求输出结果。
4.时间复杂度分析:
对于每个元素插入堆的操作,时间复杂度为 O(log k),因为堆的大小不会超过 k。 在数组遍历过程中,总共有 n 个元素,因此总的时间复杂度为 O(n log k),其中 n 是数组的长度,k 是需要找的第 k 小的元素。
cpp
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main() {
    int n, k;
    cin >> n >> k;  // 输入数组长度n 和 第k小的数
    vector<int> a(n);  // 存储数组
    for (int i = 0; i < n; ++i) {
        cin >> a[i];  // 读取数组元素
    }
    // 定义最大堆,使用 priority_queue 实现
    // 最大堆用于保存当前区间前 k 小的元素
    priority_queue<int> max_heap;
    vector<int> result(n);  // 存储每个 i 的结果
    
    // 遍历数组,动态维护区间 [1, i] 中的第 k 小数
    for (int i = 0; i < n; ++i) {
        max_heap.push(a[i]);  // 将当前元素插入最大堆
        // 如果最大堆中的元素超过 k 个,弹出堆顶元素(即最大元素)
        if (max_heap.size() > k) {
            max_heap.pop();
        }
        // 当 i < k-1 时,区间 [1, i] 的元素数量不足 k,输出 -1
        if (i < k - 1) {
            result[i] = -1;
        } else {
            // 当 i >= k-1 时,堆顶元素即为区间 [1, i] 中的第 k 小元素
            result[i] = max_heap.top();
        }
    }
    // 输出结果
    for (int i = 0; i < n; ++i) {
        if (i > 0) cout << " ";  // 输出时用空格分隔结果
        cout << result[i];
    }
    cout << endl;
    return 0;
}
python
import heapq
def find_kth_smallest(n, k, arr):
    max_heap = []  # 最大堆,用于保存当前区间前 k 小的元素
    result = []  # 存储每个前缀的结果
    for i in range(n):
        heapq.heappush(max_heap, -arr[i])  # 插入元素时取相反数,模拟最大堆
        # 如果最大堆中的元素超过 k 个,弹出堆顶元素(即最大元素)
        if len(max_heap) > k:
            heapq.heappop(max_heap)
        # 当 i < k-1 时,区间 [1, i] 的元素数量不足 k,输出 -1
        if i < k - 1:
            result.append(-1)
        else:
            # 堆顶元素即为区间 [1, i] 中的第 k 小元素,取反数恢复原值
            result.append(-max_heap[0])
    return result
# 读取输入
n, k = map(int, input().split())
arr = list(map(int, input().split()))
# 获取结果
output = find_kth_smallest(n, k, arr)
# 输出结果
print(" ".join(map(str, output)))
java
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int n = sc.nextInt();  // 读取数组长度 n
        int k = sc.nextInt();  // 读取第 k 小的数 k
        int[] a = new int[n];  // 存储数组元素
        
        for (int i = 0; i < n; i++) {
            a[i] = sc.nextInt();  // 读取数组元素
        }
        // 定义最大堆,优先队列实现
        // 最大堆用于保存当前区间前 k 小的元素
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
        int[] result = new int[n];  // 存储每个前缀的结果
        
        // 遍历数组,动态维护区间 [1, i] 中的第 k 小数
        for (int i = 0; i < n; i++) {
            maxHeap.offer(a[i]);  // 将当前元素插入最大堆
            // 如果最大堆中的元素超过 k 个,弹出堆顶元素(即最大元素)
            if (maxHeap.size() > k) {
                maxHeap.poll();
            }
            // 当 i < k-1 时,区间 [1, i] 的元素数量不足 k,输出 -1
            if (i < k - 1) {
                result[i] = -1;
            } else {
                // 当 i >= k-1 时,堆顶元素即为区间 [1, i] 中的第 k 小元素
                result[i] = maxHeap.peek();
            }
        }
        // 输出结果
        for (int i = 0; i < n; i++) {
            if (i > 0) System.out.print(" ");  // 输出时用空格分隔结果
            System.out.print(result[i]);
        }
        System.out.println();  // 输出结束行
        
        sc.close();
    }
}
        题目内容
给定一个长度为 n 的数组 a (下标从 1 开始),对于所有 i∈[1,n]∩Z ,求出区间 [1,i] 中第 k 小的数。其中 k 为给定常数。
输入描述
第一行两个整数 n,k(1<=k<n<=105) ,表示数组的长度及两个给定常数。
第二行 n 个整数,第 2 个整数为 ai 的值(1<=ai<=n,且 ai 互不相等)
输出描述
输出一行 n 个数,第 1 个数为区间 [1,i] 中第 k 小的数,以空格分隔。
若区间内的数的数量不足 k 个,请输出 −1 。
样例1
输入
5 2
5 4 3 2 1
输出
-1 5 4 3 2