[算法导论]第二章《算法基础》

来源:互联网 发布:js 函数触发submit 编辑:程序博客网 时间:2024/06/07 02:40

刚开始学习算法导论(第三版),目前打算把书上的伪代码转换成C++代码。有错或者其他累赘求指教。


插入排序算法伪代码

INSERTION-SORT(A)
1          for j=2 to A.length[A]
2             do key=A[j]
3                  //Insert A[j] into the sorted sequence A[1..j-1]

4                  i=j-1

5                  while i>0 and A[i]>key
6                      do A[i+1] = A[i]
7                          i=i-1

8                  A[i+1] =key

循环不变式:证明算法正确性的一个重要工具。对于循环不变式,必须证明它的三个性质:

初始化:循环的第一轮迭代开始之前,它为真。

保持:如果在循环的某一次迭代开始之前它为真,那么下次迭代开始之前它仍为真。

终止:当循环终止时,不变式给了我们一个有用的性质,它有助于表明算法是正确的。

运用循环不变式对插入排序算法的正确性进行证明:

初始化:j=2,子数组A[1..j-1]只包含一个元素A[1],显然它是已排序的。

保持:若A[1..j-1]是已排序的,则按照大小确定了插入元素A[j]位置之后的数组A[1..j]显然也是已排序的。

终止:当j=n+1时,退出循环,此时已排序的数组是由A[1],A[2],A[3]…A[n]组成的A[1..n],此即原始数组A。

算法的C++代码实现:

#include <iostream>#include <vector>using namespace std;void input (vector<int> &A){cout<<"输入数据(crtl+a回车结束)\n";    int data;    while(cin>>data)    {A.push_back(data);}}void insertion_sort(vector<int> &A){for(int j=1;j<A.size();++j){int key=A[j];int i=j-1;while(i>=0&&key<A[i]){    A[i+1]=A[i];--i;}A[i+1]=key;}}void output(vector<int> &A){cout<<"排序后:";for(vector<int>::iterator iter=A.begin();iter!=A.end();++iter){cout<<*iter<<" ";}cout<<endl;}int main(int argc, char *argv[]){vector<int> A;    input(A);insertion_sort(A);output(A);    return 0;}


2.3.1.分治法

有很多算法在结构上是递归的:为了解决一个给定的问题,算法要一次或多次地递归调用其自身来解决相关的问题。这些算法通常采用分治策略:将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

容易确定运行时间,是分治算法的有点之一。

分治模式在每一层递归上都有三个步骤:

分解:将原问题分解成一系列子问题;

解决:递归地解各子问题。若子问题足够小,则直接求解;

合并:将子问题的结果合并成原问题的解。


归并排序算法完全依照了分治模式。

分解:将n个元素分成各含n/2个元素的子序列;

解决:用合并排序法对两个子序列递归地排序;

合并:合并两个已排序的子序列以得到排序结果。

在对子序列排序时,其长度为1时递归结束。单个元素被视为是已排好序的。

合并排序的关键步骤在于合并步骤中的合并两个已排序子序列。为做合并,引入一个辅助过程MERGE(A,p,q,r),其中A是个数组,p、q和r是下标,满足 。该过程假设子数组A[p..q]和A[q+1..r]都已排好序,并将他们合并成一个已排好序的子数组代替当前子数组A[p..r]。

MERGE过程的时间代价为Θ(n),其中n=r-p+1是待合并的元素个数。

MERGE过程:

MERGE(A,p,q,r)

1       n1=q-p+1

2       n2=r-q

3       //Let L[1..n1+1] and R[1..n2+1] be new arrays

4       for i=1 to n1

5           do L[i] =A[p+i-1]

6       for j=1 to n2

7           do R[j] ← A[q+j]

8       L[ n1+1] =无穷

9       R[ n2+1] =无穷

10    i=1

11    j=1

12    for k=p to r

13         if L[i]<= R[j]

14            then A[k] =L[i]

15                 i=i+1

16            else A[k] =R[j]

17                 j=j+1

MERGE过程正确性的证明

初始化:第一轮循环,k=p,i=1,j=1,已排序数组L、R,比较两数组中最小元素L[i]、R[j],取较小的置于A[p],此时子数组A[p..p]不仅是已排序的(仅有一个元素),而且是所有待排序元素中最小的。若最小元素是L[i],取i=i+1,即i指向L中未排入A的所有元素中最小的一个;同理,j之于R数组也是如此。

保持:若A[p..k]是已排序的,由计算方法知,L中i所指、R中j所指及其后任意元素均大于等于A[p..k]中最大元素A[k],当k=k+1,A[k+1]中存入的是L[i]、R[j]中较小的一个,但是仍有A[k] <= A[k+1],而此时,子数组A[p..k+1]也必是有序的,i、j仍是分别指向L、R中未排入A的所有元素中最小的一个。

终止: k=r+1时终止跳出循环,此时,A[p..r]是已排序的,且显有A[p] A[p+1] .. A[r]。此即原待排序子数组,故算法正确。

MERGE-SORT(A,p,r)

1       if p<r

2           then q=[(p+r)/2]

3           MERGE-SORT(A,p,r)

4           MERGE-SORT(A,q+1,r)

5           MERGE-SORT(A,p,q,r)

归并排序算法的C++实现代码:

#include <iostream>#include <vector>#define INFINITE 10000000using namespace std;void input (vector<int> &A){cout<<"输入数据(crtl+a回车结束)\n";    int data;    while(cin>>data)    {A.push_back(data);}}void MERGE (vector<int> &A, int p, int q, int r){vector<int> L,R;int n1 = q-p+1;int n2 = r-q;    for(int i=p;i<=q;++i)L.push_back(A[i]);    L.push_back(INFINITE);    for(int j=q+1;j<=r;++j)    R.push_back(A[j]);    R.push_back(INFINITE);for (int k=p,m=0,n=0;k<=r ;++k ){if (L[m]<R[n]){A[k]=L[m++];}else{A[k]=R[n++];}}}void MERGE_SORT(vector<int> &A,int p,int r){if (p<r){int q=(p+r)/2;MERGE_SORT(A,p,q);MERGE_SORT(A,q+1,r);MERGE(A,p,q,r);}}void output(vector<int> &A){cout<<"排序后:";for(vector<int>::iterator iter=A.begin();iter!=A.end();++iter){cout<<*iter<<" ";}cout<<endl;}int main(int argc, char *argv[]){vector<int> A;    input(A);MERGE_SORT(A,0,A.size()-1);output(A);system ("pause");    return 0;}



原创粉丝点击