DreamJudge-1383-查找第K小的数

1.题目描述

?Time Limit: 1000 ms
Memory Limit: 256 mb

查找一个数组的第K小的数,注意同样大小算一样大。 如 2 1 3 4 5 2 第三小数为3。

输入输出格式

输入描述:

输入有多组数据。
每组输入n,然后输入n个整数(1<=n<=1000),再输入k。

输出描述:

输出第k小的整数。

输入输出样例

输入样例#:

6
2 1 3 5 2 2
3

输出样例#:

3

题目来源
北京邮电大学

2.题解

注意这一题不同于【深基9.例4】求第 k 小的数, 因为题目要求查找一个数组的第K小的数,注意同样大小算一样大

2.1 set集合去重 + nth_element寻找第k小的数

思路

这里因为nth_element会将重复的数也纳入计算,所以先要进行去重
这里使用set去重后,nth_element要求连续数组,所以还要将set转化为vector数组

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;

int main() {
    int n;
    while (cin >> n) {
    	// 注意这里nth_element将重复的数每个都进行了计数,与题目要求的只看成一个不符,所以这里进行去重 
        set<int> unique_numbers;
        for (int i = 0; i < n; ++i) {
            int number;
            cin >> number;
            unique_numbers.insert(number);
        }
        int k;
        cin >> k;

        // 将set转换为vector
        vector<int> numbers(unique_numbers.begin(), unique_numbers.end());
        // 使用 nth_element(只能使用连续的vector数组) 进行部分排序!!! 
        nth_element(numbers.begin(), numbers.begin() + k - 1, numbers.end());
        // 输出第 k 小的元素
        cout << numbers[k - 1] << endl;
    }
    return 0;
}

2.2 sort排序 + unique(形式去重,只是将重复元素全部放到数组末尾了)

思路

先排好,再去重,然后直接定位第k位即可!

代码

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n, k, a[100];
    cin>>n;
    for(int i=1;i<=n;i++)   cin>>a[i];
    sort(a+1,a+1+n);           // 先排好
    unique(a+1,a+1+n);      // 再去重
    cin>>k;
    cout<<a[k]<<endl;
    return 0;
 }

2.3 分治算法-实现nth_element

思路

参考【深基9.例4】求第 k 小的数
这里因为实际上要进行去重,所以这样做反而画蛇添足了,纯作为了解一下把!

代码

#include<bits/stdc++.h>
using namespace std;
int k, ans;
//这里arr必须设置为全局变量,不然每次都作为函数输入参数,会消耗大量资源和时间爱你 
vector<int> arr;

// 这里由于cin读入过慢,而这里n( 1≤<5000000且n为奇数),所以使用快读缩短读取时间爱
inline int read(){	//快读 
    char ch=getchar();
	int x=0,f=1;
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    } 
	while('0'<=ch&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    } 
	return x*f;
}

void sortNum(int begin, int end){
	// 递归终止条件 
	if(begin == end){
		ans = arr[begin];
		return;
	}
	
	// 初始化 
    int l = begin, r = end;
    // 确定一个基准判断点, 只固定值,不固定位置!!! 
    int currNum = arr[(end - begin) / 2 + begin];
    while(l <= r){
    	//由于我取的currNum是中位数,所以l第一次最多停留在中位数,r同理 
    	//由于后面if中的交换,l指针左边的左半区间必都是小于等于currNum的值,r指针右边的右半区间必是大于等于currNum的值,都是一种"有序"状态 
        while(arr[l] < currNum) l++; //l指向第一个不小于currNum的值 
        while(arr[r] > currNum) r--; //r指向第一个不大于currNum的值
        if(l <= r){
            swap(arr[l++], arr[r--]); // 自己不需要的数正是对方需要的 
        }
    }
    
	// k ——我们要取的第k小的数,此时必然有 l > r
	// 但是最终由于最后一次的l++,r--; l指向第一个大于等于currNum的值,r指向第一个小于等于currNum的值,故为 sortNum(l, end)和 sortNum(begin, r)
    if(k >= l)  sortNum(l, end); // 表明在右区间 
    else if(k <= r) sortNum(begin, r); //表明在左区间 
	//这种情况就是(r<k<l), 如果现在已经有 l = r, l++, r--,说明此时是arr[l] = arr[r] = currNum; 就有可能 r + 1 = k = l - 1; 
    else {
        sortNum(r+1 , l-1);
	}
}

int main(){
    int n;
    n = read();
    arr.resize(n);
    set<int> st; 
    for(int i = 0; i < n; i++){
    	int num = read();
    	st.emplace(num);
    }
    k = read() + 1;
    arr.assign(st.begin(), st.end());
    sortNum(0, n - 1);
    cout << ans;
    return 0;
}

2.3 大根堆

思路

由于建堆和更新堆时间复杂度都是O(n),我们考虑使用大根堆
思路很简单,我们总是维护一个大小为k+1(这里k从0开始,其实是下标,真正个数为k+1!!!)的大根堆,遍历数组的同时不断更新堆,保证大根堆中是当前遍历到的元素中最小的k+1个
而因为是大根堆,所以根节点值是这k+1个元素中最大的,也就是说,在遍历完数组所有元素后,小根堆的根节点值就是我们要求的第K大的元素值!

代码

#include<bits/stdc++.h>
using namespace std;
vector<int> heap; 

// 这里由于cin读入过慢,而这里n( 1≤<5000000且n为奇数),所以使用快读缩短读取时间
inline int read(){	//快读 
    char ch=getchar();
	int x=0,f=1;
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    } 
	while('0'<=ch&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    } return x*f;
}

void heapAdjust(int curIndex, int len){
	int child = curIndex * 2 + 1;
	int curValue = heap[curIndex];
	while(child < len){
		// 判断左右节点的选择 
		if(child + 1 < len && heap[child] < heap[child + 1]){
			child++;
		}
		if(curValue < heap[child]){
			heap[curIndex] = heap[child];
			curIndex = child;
			child = curIndex * 2 + 1;
		}else{
			break;
		}
	} 
	// 更新当前Index对应的value值(前面更新的都是child换上去的值,最后一个被换过来的需要单独处理) 
	heap[curIndex] = curValue;
}

int main(){
	int n, k;
	n = read();
	k = read();
	vector<int> nums(n);
	for(int i = 0; i < n; i++){
		nums[i] = read();
	}

	heap.assign(nums.begin(), nums.begin() + k + 1);
	int sublen = heap.size(), len = n;

	for(int i = sublen / 2 - 1; i >= 0; i--){
		heapAdjust(i, sublen);
	}

	for(int j = k + 1; j < len; j++){
		if(nums[j] < heap[0]){
			heap[0] = nums[j];
			heapAdjust(0, sublen);
		}
	}
	cout << heap[0];
	return 0; 
}
posted @   DawnTraveler  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示