前言:
merge类算法的前提依然是区间是有序的,主要是用来合并两个容器的元素或者是单个容器两段区间的元素,一说到这里,我们就会想起归并排序,对,我们也可以用这个算法来简化我们的代码量,具体看后面的例子。
merge
//default (1)
template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result);
//custom (2)
template <class InputIterator1, class InputIterator2,
class OutputIterator, class Compare>
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
merge函数的作用是:将两个有序的序列合并为一个有序的序列。函数参数:merge(first1,last1,first2,last2,result,compare);
firs1t为第一个容器的首迭代器,last1为第一个容器的末迭代器,first2为第二个容器的首迭代器,last2为容器的末迭代器,result为存放结果的容器,comapre为比较函数(可略写,默认为合并为一个升序序列)。
还有就是前面两对迭代器可以指向同一个容器的不同段,但是后面那个就不允许指向前面的容器。
源码
template <class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result)
{
while (true) {
if (first1==last1) return std::copy(first2,last2,result);
if (first2==last2) return std::copy(first1,last1,result);
*result++ = (*first2<*first1)? *first2++ : *first1++;
}
}
举个例子:
// merge algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::merge, std::sort
#include <vector> // std::vector
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
std::vector<int> v(10);
std::sort (first,first+5);
std::sort (second,second+5);
std::merge (first,first+5,second,second+5,v.begin());
std::cout << "The resulting vector contains:";
for (std::vector<int>::iterator it=v.begin(); it!=v.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
inplace_merge
这个的话,跟前面用法没什么两样,就是一个容器内分两个有序的部分归并到本身的容器,唯一区别就是参数不一样
其次:inplace_merge在额外内存足够的情况下,恰好做 N - 1 次对比(N其实就是两段有序列长度之和 ),否则(没有足够内存用来缓存)复杂度就是 N*log(N),即若尝试分配缓存失败就会用比较低效的算法来执行.
实际上它也是需要申请空间来做的,但申请失败就是低效的排序算法来用。
//default (1)
template <class BidirectionalIterator>
void inplace_merge (BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last);
//custom (2)
template <class BidirectionalIterator, class Compare>
void inplace_merge (BidirectionalIterator first, BidirectionalIterator middle,
BidirectionalIterator last, Compare comp);
举个例子:
// inplace_merge example
#include <iostream> // std::cout
#include <algorithm> // std::inplace_merge, std::sort, std::copy
#include <vector> // std::vector
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
std::vector<int> v(10);
std::vector<int>::iterator it;
std::sort (first,first+5);
std::sort (second,second+5);
it=std::copy (first, first+5, v.begin());
std::copy (second,second+5,it);
std::inplace_merge (v.begin(),v.begin()+5,v.end());
std::cout << "The resulting vector contains:";
for (it=v.begin(); it!=v.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
总结:
假设M,N分别为两段有序列的长度.
merge时间复杂度为O(min(M,N)),空间复杂度O(M+N).
inplace_merge,在分配缓存成功时,时间复杂度O(M+N),空间复杂度看分配的缓存,即可能为O(M),O(N)或O(M+N),否则,时间复杂度为O(N*log(N)),空间复杂度为O(1).
归并排序实例
给定你一个长度为n的整数数列。
请你使用归并排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在1~109范围内),表示整个数列。
输出格式
输出共一行,包含 n 个整数,表示排好序的数列。
数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
思路:没啥说的,模板题
没用STL的做法
# include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n;
int a[N],q[N];
void merge(int left,int right)
{
if(left>=right) return;
int mid = (left+right)>>1;
merge(left,mid);
merge(mid+1,right);
//合并两段数据
int i=left,j = mid+1;
int k = 0;
while(i<=mid && j<=right){
if(a[i]<=a[j]){
q[k++] = a[i];
++i;
}
else{
q[k++] = a[j];
++j;
}
}
while(i<=mid){
q[k++] = a[i++];
}
while(j<=right){
q[k++] = a[j++];
}
for(int i=0;left<=right;++left){
a[left] = q[i++];
}
return ;
}
int main(void)
{
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
merge(1,n);
for(int i=1;i<=n;++i){
cout<<a[i]<<' ';
}
return 0 ;
}
用inplace_merge算法
# include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n;
int a[N],q[N];
void merge(int left,int right)
{
if(left>=right) return;
int mid = (left+right)>>1;
merge(left,mid);
merge(mid+1,right);
//合并两段数据
inplace_merge(begin(a)+left,begin(a)+mid+1,begin(a)+right+1);
//因为这里范围是 [begin,middle) , [middle,end)
return ;
}
int main(void)
{
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
merge(1,n);
for(int i=1;i<=n;++i){
cout<<a[i]<<' ';
}
return 0 ;
}