题目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);
}

CH341A编程器是一款广泛应用的通用编程设备,尤其在电子工程和嵌入式系统开发领域中,它被用来烧录各种类型的微控制器、存储器和其他IC芯片。这款编程器的最新版本为1.3,它的一个显著特点是增加了对25Q256等32M芯片的支持。 25Q256是一种串行EEPROM(电可擦可编程只读存储器)芯片,通常用于存储程序代码、配置数据或其他非易失性信息。32M在这里指的是存储容量,即该芯片可以存储32兆位(Mbit)的数据,换算成字节数就是4MB。这种大容量的存储器在许多嵌入式系统中都有应用,例如汽车电子、工业控制、消费电子设备等。 CH341A编程器的1.3版更新,意味着它可以与更多的芯片型号兼容,特别是针对32M容量的芯片进行了优化,提高了编程效率和稳定性。26系列芯片通常指的是Microchip公司的25系列SPI(串行外围接口)EEPROM产品线,这些芯片广泛应用于各种需要小体积、低功耗和非易失性存储的应用场景。 全功能版的CH341A编程器不仅支持25Q256,还支持其他大容量芯片,这意味着它具有广泛的兼容性,能够满足不同项目的需求。这包括但不限于微控制器、EPROM、EEPROM、闪存、逻辑门电路等多种类型芯片的编程。 使用CH341A编程器进行编程操作时,首先需要将设备通过USB连接到计算机,然后安装相应的驱动程序和编程软件。在本例中,压缩包中的"CH341A_1.30"很可能是编程软件的安装程序。安装后,用户可以通过软件界面选择需要编程的芯片类型,加载待烧录的固件或数据,然后执行编程操作。编程过程中需要注意的是,确保正确设置芯片的电压、时钟频率等参数,以防止损坏芯片。 CH341A编程器1.3版是面向电子爱好者和专业工程师的一款实用工具,其强大的兼容性和易用性使其在众多编程器中脱颖而出。对于需要处理25Q256等32M芯片的项目,或者26系列芯片的编程工作,CH341A编程器是理想的选择。通过持续的软件更新和升级,它保持了与现代电子技术同步,确保用户能方便地对各种芯片进行编程和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值