题目1004:Median

本文解析了STL中nth_element算法的工作原理及其在寻找两个已排序序列中位数问题的应用。介绍了_Nth_element、_Unguarded_partition、_Median和_Med3四个辅助函数的作用,展示了如何利用这些函数高效地找到第n个元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

    Given an increasing sequence S of N integers, the median is the number at the middle position. For example, the median of S1={11, 12, 13, 14} is 12, and the median of S2={9, 10, 15, 16, 17} is 15. The median of two sequences is defined to be the median of the non-decreasing sequence which contains all the elements of both sequences. For example, the median of S1 and S2 is 13.
    Given two increasing sequences of integers, you are asked to find their median.

输入:

    Each input file may contain more than one test case.
    Each case occupies 2 lines, each gives the information of a sequence. For each sequence, the first positive integer N (≤1000000) is the size of that sequence. Then N integers follow, separated by a space.
    It is guaranteed that all the integers are in the range of long int.

输出:

    For each test case you should output the median of the two given sequences in a line.

样例输入:
4 11 12 13 14
5 9 10 15 16 17
样例输出:
13
来源:

2011年浙江大学计算机及软件工程研究生机试


题目很简单,因为两个字符串都是排序排好的,那么就可以用O(n)的算法直接合并了。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 1000002
int a[N],b[N],c[N*2];

int main()
{
    int n1,n2,i,j,k;
    while(scanf("%d",&n1)!=EOF)
    {
        for(i=0;i<n1;i++)
            scanf("%d",&a[i]);

        scanf("%d",&n2);
        for(i=0;i<n2;i++)
            scanf("%d",&b[i]);
            
        int n=n1+n2;
        j=k=0;
        for(i=0;j<n1&&k<n2;i++)
        {
            if(a[j]<b[k])
            {
                c[i]=a[j++];
            }
            else
            {
                c[i]=b[k++];
            }
        }
        while(j<n1)
        {
            c[i++]=a[j++];
        }
    
        while(k<n2)
        {
            c[i++]=b[k++];
        }
        
        printf("%d\n",c[(n-1)/2]);
    }
    return 0;
}

不过我后来发现了一个STL很神奇的函数 nth_element

#include <algorithm>

nth_element作用为求第n小的元素,并把它放在第n位置上,下标是从0开始计数的,也就是说求第0小的元素就是最小的数。

#include "iostream"
#include "algorithm"
#include "string"
using namespace std;
long a[1000002];
int main()
{
       long n1,n2,i,mid;
        while(cin>>n1)
        {
              for(i=0;i<n1;i++)
                   cin>>a[i];
              cin>>n2;
              for(;i<n1+n2;i++)
                   cin>>a[i];
              mid=(i-1)/2;
              nth_element(a,a+mid,a+i);
              cout<<a[mid]<<endl;
         }
    return 0;
}

它的速度很快,不会对整体排序,对无序的字符串 也可以实现

具体的原理我在百度文库中找到,但是原作者已经不知道是谁了,

在这里请允许我转载过来。


nth_element 算法注解
nth_element 是 STL 提供的一个算法,用于找出序列中
的第 n 大元素。这个算法涉及下面4个辅助函数:
• _Nth_element
• _Unguarded_partition
• _Median
• _Med3

/*********************************************
**********************************************
*
_Nth_element
使序列中第 n 大的元素位于第 n 个位子上,使用 < 比
较元素。
基本思想:
(1) 找到一个包含 n 的足够小的区间[a,b),使得[a,b)
作为一个大粒度的元素处于序列的有序位置。
(2) 对[a,b)部分进行排序。
nth_element 查找区间的方式体现了二分(折半)查找的 思 想 。 它 的 核 心 是 ungarded_partition 算 法 ,
ungarded_partition 算法能够找出随机序列的近似的位
置中间值。
**********************************************
**********************************************
*/
template<class _RanIt> inline
void _Nth_element(_RanIt _First, _RanIt _Nth, _RanIt
_Last)
{ // order Nth element, using operator<
_DEBUG_RANGE(_First, _Last);
/* 逐步缩小[_First, _Last)的区间,直至尺寸小到可
以直接对局部进行排序。
_ISORT_MAX 是一个阀值,在<algorithm>中被定
义为32,表示适于插入法排序的序列的最大长度。*/
for (; _ISORT_MAX < _Last - _First; )
{ // divide and conquer, ordering partition containing
Nth
/* 把序列的当前部分划分为有序的三份。*/
pair<_RanIt, _RanIt> _Mid =_Unguarded_partition(_First, _Last);
/* 确定下一步搜索区间。*/
if (_Mid.second <= _Nth)
_First = _Mid.second;
else if (_Mid.first <= _Nth)
return; // Nth inside fat pivot, done
else
_Last = _Mid.first;
}
/* 对[_First, _Last)排序,第 n 大元素就到了第 n 个
位置上。*/
_Insertion_sort(_First, _Last); // sort any remainder
}
/*********************************************
**********************************************
*
_Unguarded_partition
把 序 列 划 分 为 有 序 的 三 份 : [_First, Mid.First),[Mid.First, Mid.Second), [Mid.Second, _Last) , 其 中
[Mid.First, Mid.Second)内的元素相等。
注意,不是等分:
(1) 不保证[Mid.First, Mid.Second )在位置上处于序列
的中间。
(2) 不保证[Mid.First, Mid.Second )在元素值上处于序
列的中间。
(3) [_First, Mid.First)和[Mid.Second, _Last)的长度有可
能为0。
(4) [_First, Mid.First) 中的 所 有 元 素 ( 若 有 ) 小 于
[Mid.First, Mid.Second )中的任一元素。 [Mid.Second,
_Last)中的则大于。
**********************************************
**********************************************
/
template<class _RanIt> inline
pair<_RanIt, _RanIt> _Unguarded_partition(_RanIt
_First, _RanIt _Last)
{ // partition [_First, _Last), using operator<
_RanIt _Mid = _First + (_Last - _First) / 2; // sort
median to _Mid/* 选择序列的位置平均值(近似),作为枢轴值
(pivot)。 */
_Median(_First, _Mid, _Last - 1);
/* [_Pfirst, _Plast) 就是要找的区间。*/
_RanIt _Pfirst = _Mid;
_RanIt _Plast = _Pfirst + 1;
/* 以枢轴值为起点,向序列的两头扫描,把相邻的
与枢轴值相等的元素合并到[_Pfirst, _Plast)中。
宏 _DEBUG_LT(x,y) 定义为 ((x)<(y))。
值得注意的是,下面的代码用两次 < 比较实现 ==
比较。*/
while (_First < _Pfirst
&& !_DEBUG_LT(*(_Pfirst - 1), *_Pfirst)
&& !(*_Pfirst < *(_Pfirst - 1)))
--_Pfirst;
while (_Plast < _Last
&& !_DEBUG_LT(*_Plast, *_Pfirst)
&& !(*_Pfirst < *_Plast))
++_Plast;/* 分别以[_Pfirst, _Plast)的起、止位置为起点,向
序列的两头扫描。*/
_RanIt _Gfirst = _Plast;
_RanIt _Glast = _Pfirst;
for (; ; )
{ // partition
for (; _Gfirst < _Last; ++_Gfirst)
if (_DEBUG_LT(*_Pfirst, *_Gfirst))
;
else if (*_Gfirst < *_Pfirst)
break;
else
std::iter_swap(_Plast++, _Gfirst);
/* 除非 _Gfirst == _Last,否则 *_Gfirst 应该调
到[_Pfirst, _Plast)的前面。*/
for (; _First < _Glast; --_Glast)
if (_DEBUG_LT(*(_Glast - 1), *_Pfirst))
;
else if (*_Pfirst < *(_Glast - 1))break;
else
std::iter_swap(--_Pfirst, _Glast - 1);
/* 除非 _Glast == _First,否则 *(_Glast-1) 应该
调到[_Pfirst, _Plast)的后面。*/
if (_Glast == _First && _Gfirst == _Last)
return (pair<_RanIt, _RanIt>(_Pfirst, _Plast));
/* 如果 *_Gfirst 和 *(_Glast-1) 都需要 调 到
[_Pfirst, _Plast)的对面,交换它俩就行了。
如果只有其中一个(记为 G)要调整,那就交换
它和[_Pfirst, _Plast)——实际上要复杂些,有两种情
况:
(1) G 与[_Pfirst, _Plast)相邻。这时只需要交换 G
和其对面的区间边界。
(2) G 与[_Pfirst, _Plast)之间有其它元素。这时需
要三方交换(两次):第一次交换使枢轴区间移动一个
单位(通过交换区间的内部边界和对面的外部边界来
实现),第二次交换完成调整任务。*/
if (_Glast == _First)
{ // no room at bottom, rotate pivot upwardif (_Plast != _Gfirst)
std::iter_swap(_Pfirst, _Plast);
++_Plast;
std::iter_swap(_Pfirst++, _Gfirst++);
}
else if (_Gfirst == _Last)
{ // no room at top, rotate pivot downward
if (--_Glast != --_Pfirst)
std::iter_swap(_Glast, _Pfirst);
std::iter_swap(_Pfirst, --_Plast);
}
else
std::iter_swap(_Gfirst++, --_Glast);
}
}
/*********************************************
**********************************************
_Median
找序列的近似位置平均值。
********************************************************************************************
/
template<class _RanIt> inline
void _Median(_RanIt _First, _RanIt _Mid, _RanIt _Last)
{ // sort median element to middle
if (40 < _Last - _First)
{ // median of nine
size_t _Step = (_Last - _First + 1) / 8;
_Med3(_First, _First + _Step, _First + 2 * _Step);
_Med3(_Mid - _Step, _Mid, _Mid + _Step);
_Med3(_Last - 2 * _Step, _Last - _Step, _Last);
_Med3(_First + _Step, _Mid, _Last - _Step);
}
else
_Med3(_First, _Mid, _Last);
}
/*********************************************
**********************************************
*
_Med3对三个元素排序。
**********************************************
**********************************************
/
template<class _RanIt> inline
void _Med3(_RanIt _First, _RanIt _Mid, _RanIt _Last)
{ // sort median of three elements to middle
if (_DEBUG_LT(*_Mid, *_First))
std::iter_swap(_Mid, _First);
if (_DEBUG_LT(*_Last, *_Mid))
std::iter_swap(_Last, _Mid);
if (_DEBUG_LT(*_Mid, *_First))
std::iter_swap(_Mid, _First);
}

1. 用户与权限管理模块 角色管理: 学生:查看实验室信息、预约设备、提交耗材申请、参与安全考核 教师:管理课题组预约、审批学生耗材申请、查看本课题组使用记录 管理员:设备全生命周期管理、审核预约、耗材采购与分发、安全检查 用户操作: 登录认证:统一身份认证(对接学号 / 工号系统,模拟实现),支持密码重置 信息管理:学生 / 教师维护个人信息(联系方式、所属院系),管理员管理所有用户 权限控制:不同角色仅可见对应功能(如学生不可删除设备信息) 2. 实验室与设备管理模块 实验室信息管理: 基础信息:实验室编号、名称、位置、容纳人数、开放时间、负责人 功能分类:按学科(计算机实验室 / 电子实验室 / 化学实验室)标记,关联可开展实验类型 状态展示:实时显示当前使用人数、设备运行状态(正常 / 故障) 设备管理: 设备档案:名称、型号、规格、购置日期、单价、生产厂家、存放位置、责任人 全生命周期管理: 入库登记:管理员录入新设备信息,生成唯一资产编号 维护记录:记录维修、校准、保养信息(时间、内容、执行人) 报废处理:登记报废原因、时间,更新设备状态为 "已报废" 设备查询:支持按名称、型号、状态多条件检索,显示设备当前可用情况 3. 预约与使用模块 预约管理: 预约规则:学生可预约未来 7 天内的设备 / 实验室,单次最长 4 小时(可设置) 预约流程:选择实验室→选择设备→选择时间段→提交申请(需填写实验目的) 审核机制:普通实验自动通过,高危实验(如化学实验)需教师审核 使用记录: 签到 / 签退:到达实验室后扫码签到,离开时签退,系统自动记录实际使用时长 使用登记:填写实验内容、设备运行情况(正常 / 异常),异常情况需详细描述 违规管理:迟到 15 分钟自动取消预约,多次违规限制预约权限 4. 耗材与安全管理模块 耗材管理: 耗材档案:名称、规格、数量、存放位置、
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值