NOIP-算法实现 - 归并排序
研究了三本书的归并排序的写法
一:严蔚敏 数据结构;
写得太过于复杂,是这本书中写的最垃圾的算法,我一共研究过四遍吧,算法用错中复杂的参数和递归,令人看得非常头大。建议大家不要学习这本书的这个算法。因为是考研书,所以不得不看。
二:王红梅 算法设计与分析;
跟严蔚敏的书中算法差不多,不过该书漏了点东西,直接导致运算结果是错误的。所以不多说,这本书最多浏览一下其中的思想,不宜细看。因为是本人的的研究生课本,所以本人不得不研究。
三: Introduction to Algorithm
经典书籍,算法都是伪代码写的,不过基本上你有那么一点点编程基础的话,就可以非常容易的看懂并轻易把这些伪代码转换成为你熟悉的程序语言。其中的归并排序也是一目了然,很好懂。
下面给出严蔚敏的算法,下一遍文章再分析Introduction to algorithm的算法。比较一下这两个有什么不一样。
以下代码是简化了一点书本上的代码写成的。
[cpp] view plain copy print?
-
#include<iostream>
-
using namespace std;
-
const int MAXS = 20;
-
-
void merge(int t1[], int t2[], int s, int m, int t)
-
{
-
int i=s, j=m+1, k=s;
-
while(i<=m&&j<=t)
-
{
-
if(t1[i]<=t1[j])
-
{
-
t2[k++]=t1[i++];
-
}
-
else
-
{
-
t2[k++]=t1[j++];
-
}
-
}
-
while(i<=m)
-
{
-
t2[k++]=t1[i++];
-
}
-
while(j<=t)
-
{
-
t2[k++]=t1[j++];
-
}
-
}
-
-
void mergeSort(int t1[], int t2[], int s, int t)
-
{
-
int m;
-
int tr[MAXS];
-
if(s==t) t2[s]=t1[s];
-
else
-
{
-
m = (s+t)/2;
-
mergeSort(t1, tr, s, m);
-
mergeSort(t1, tr, m+1, t);
-
merge(tr, t2, s, m, t);
-
}
-
}
-
-
template<typename T>
-
class Print
-
{
-
public:
-
Print(){}
-
void inline operator()(const T& x) const{cout<<x<<" ";}
-
};//这就是函数对象,这里并没有带数据,只是一个简单的输出操作。
-
-
template<class C, class Oper>
-
void for_all(C &c, Oper op)
-
{
-
for(auto x:c) //C++新特性,记住其实就是一种方便循环遍历数据的写法
-
op(x);
-
}/*写一个通用的函数,增加其可重用性,配合下面的Print函数就可以打印各种不同的数据到屏幕,非常方便。*/
-
-
-
int main()
-
{
-
int a[4]={5,2,90,8};
-
int b[7]={2,10,7,30,9,4,5};
-
int c[3]={9,5,8};
-
mergeSort(b, b, 0, 6);
-
for_all(b,Print<int>());
-
cout<<endl;
-
mergeSort(a, a, 0, 3);
-
for_all(a,Print<int>());
-
cout<<endl;
-
mergeSort(c, c, 0, 2);
-
for_all(c,Print<int>());
-
cout<<endl;
-
return 0;
-
}
总结:
以上代码还不完善,主要是想对比一下这两本书中的代码的异同,不然也不写这本书的代码了。不过能把这个代码看懂的话,说明你还是很聪明的,因为的确是写的非常复杂。如果是面试的时候,或者其他有压力的场合下写的话,恐怕很难调试出来,除非熟记了,要自己按照他的思路来写的话我觉得不太现实。所以还是集中精力学Introduction to Algorithm这本书的代码吧。
归并排序2:
这次是按照 Introduction to Algorithm 的归并排序来写的C++实现算法,其中复用了前面用过的合并两个数组的算法。
STL函数:for_each(vecI1.begin(), vecI1.end(), Print<int>());
这次用了for_each这个STL中的标准函数,代替了前面自己写的for_all函数,用法也很简单,前面两个参数是数组的范围,第三个参数可以是函数也可以是函数对象,就是实现用这个函数操作每一个数组的元素。
我发现Introduction to Algorithm这本书虽然经典,但是它也没有很好的处理下标问题,所以我在编程的时候也不能直接抄它的下标,必须要自己认真核对,它处理的下标应该是有错的,下面我也标志处错处了。下面是C++代码,注释出了书本上的伪代码,比较一下吧:
包括了一个头文件
[cpp]
-
#pragma once
-
/*****
-
////////////Merge.h
-
已知两线性表中的数据元素按值非递减排列,归并两线性表到新的线性表,是的数据元素也按值非递减排列
-
*****/
-
#include<iostream>
-
#include<vector>
-
using namespace std;
-
-
template<typename T>
-
void mergeList(vector<T> &t, vector<T> &t1, vector<T> &t2)
-
{
-
t.clear();
-
auto iter1=t1.begin(), iter2=t2.begin(), iter=t.begin();
-
while(iter1!=t1.end() && iter2!=t2.end())
-
{
-
if(*iter1<=*iter2)
-
{
-
t.push_back(*iter1);
-
++iter1;
-
}
-
else
-
{
-
t.push_back(*iter2);
-
++iter2;
-
}
-
}
-
while(iter1!=t1.end())
-
{
-
t.push_back(*iter1);
-
iter1++;
-
}
-
while(iter2!=t2.end())
-
{
-
t.push_back(*iter2);
-
iter2++;
-
}
-
}
下面是主文件代码:
[cpp]
-
#include<iostream>
-
#include<vector>
-
#include<algorithm>
-
#include"Merge.h"
-
-
using namespace std;
-
-
template<typename T>
-
void myMerge(vector<T> &t, int p, int q, int r)
-
{
-
int n1 = q-p+1;
-
int n2 = r-q+1;
-
int k;
-
vector<T> leftT(t.begin()+p-1, t.begin()+q);
-
vector<T> rightT(t.begin()+q, t.begin()+r);
-
vector<T> tempT;
-
mergeList(tempT, leftT, rightT);
-
auto itTemp = tempT.begin();
-
for(k=p-1; itTemp!=tempT.end(); k++, itTemp++)
-
{
-
t.at(k)=*itTemp;
-
}
-
}
-
/* Introduction to Algorithm 中的算法
-
MERGE(A, p, q, r)
-
1 n1 = q - p + 1
-
2 n2 = r - q //这里的下表按照C++的思维习惯的话,应该是不对的,应该是n2=r-q+1
-
3 letL[1.. n1 + 1] and R[1.. n2 + 1] be new arrays
-
4 for i = 1 to n1
-
5 L[i] = A[p + i - 1]//这里是正确的
-
6 for j = 1 to n2
-
7 R[j] = A[q + j ] //这里也应该不对,改为:A[q+j-1]
-
8 L[n1 + 1] = 无穷大//其实下面就是mergeList的算法,不过有点不一样罢了,效果一样
-
9 R[n2 + 1] = 无穷大
-
10 i = 1
-
11 j = 1
-
12 for k = p to r
-
13 if L[i] <= R[j]
-
14 A[k] = L[i]
-
15 i = i + 1
-
16 else A[k] = R[j]
-
17 j = j + 1
-
*/
-
-
template<typename T>
-
void mergeSort(vector<T>& t, int p, int r)
-
{
-
if(p<r)
-
{
-
int q = (p+r)/2;
-
mergeSort(t,p,q);
-
mergeSort(t,q+1,r);
-
myMerge(t,p,q,r);
-
}
-
}//下标从1开始
-
-
template<typename T>
-
void mergeS(vector<T>& t, int p, int r)
-
{
-
p++;
-
mergeSort(t,p,r);
-
}//下表从0开始,比较符合C/C++的编程习惯
-
-
template<typename T>
-
void mergeSort(vector<T> &t)
-
{
-
mergeSort(t, 1, t.size());
-
}
-
-
template<typename T>
-
class Print
-
{
-
public:
-
Print(){}
-
void inline operator()(const T& x) const{cout<<x<<" ";}
-
};//这就是函数对象,这里并没有带数据,只是一个简单的输出操作。
-
-
-
int main()
-
{
-
int a[4]={5,2,90,8};
-
vector<int> vecI1(a,a+4);
-
int b[7]={2,10,7,30,9,4,5};
-
vector<int> vecI2(b,b+7);
-
-
mergeSort(vecI2, 1, 7);
-
for(auto x:vecI2)
-
{//C++11标准遍历数组,非常方便
-
cout<<x<<" ";
-
}
-
cout<<endl;
-
-
mergeS(vecI2, 0, 7);
-
for(auto x:vecI2)
-
{
-
cout<<x<<" ";
-
}
-
cout<<endl;
-
-
mergeSort(vecI1);
-
for_each(vecI1.begin(), vecI1.end(), Print<int>());
-
cout<<endl;
-
//这里利用stl中的函数实现输出
-
return 0;
-
}
总结:
本算法一直都觉得很难理解的,因为递归本来就难理解,而且还拆分那么对次,很难跟踪他的思路的,尤其是严蔚敏等一大批书关于这个算法的程序都按照一个思路来写的,非常糟糕。
Introduction to Algorithm这本书就简化了参数,好理解很多了,传入的数组参数只有一个,非常简明。
还有值得注意的就是下标问题,非常头疼的问题,所以写程序的时候要非常注意。一定要一个一个下标跟踪好,否则很容易下标溢出。
NOIP信息学视频地址
视频地址
链接:https://pan.baidu.com/s/1tHo1DFMaDuMZAemNH60dmw
提取码:7jgr