算法导论::第一部分::第2章:文中程序和课后练习

前言

  最近看了下算法导论,分享下文中的程序和思路,同时也包括一些习题的答案和程序,希望对大家有所帮助。前面有些程序用python和C++都写了遍,可能是当时闲得慌,后面可能只用C++了,当然练习就全用C++了。C++使用的相关库,可能会做相应说明?
  文中会对本书的某些内容阐述自己的理解,如果有任何的错误,望大家指正。有些题目太过简单就看心情了,可能后面会补上去。
  然后第一章就不写了,大家自己看看就好了。

第一部分:基础知识

2 算法基础

2.1 插入排序

  插入排序在元素数量少的情况下比较有效,再加上有原址性,所以一般一些递归算法的小型排序都可以用到。
  INSERTION-SORT
  Python

def InsertionSort(A):
    for i in range(1,len(A)):
        key=A[i]
        j=i-1
        while j>=0 and A[j]>key:
        	A[j+1]=A[j]
        	j-=1
        A[j+1]=key
        print(i)
	return (A)

  C++

#include<vector>
using namespace std;
void InsertionSort(vector<double>&input){
	for (int i=1;i<input.size();i++){
		auto key=input[i];
		int j=i-1;
		for (;j>=0&&input[j]>key;j--){
			input[j+1]=input[j];
		}
		input[j+1]=key;
	}
}

  原址性:在输入数组中仅有常数个元素需要在排序过程中存储在数组

练习2.1-1

  其过程如下:
  |31|41,59,26,41,58 → \rightarrow 31,41|59,26,41,58 → \rightarrow |31,41,59|26,41,58 → \rightarrow
  313,412,591|26|41,58 → \rightarrow 26,31,41,591|41|58 → \rightarrow 26,31,41,41,591|58| → \rightarrow
  26,31,41,41,58,59
  元素表示此元素发生位置交换;
  元素 i i i上标表示红色元素与此元素交换的次序为 i i i

练习2.1-2
#include<vector>
using namespace std;
void InsertionSort(vector<double>&input){
	for (int i=1 ; i< input.size(); i++){
		auto key=input[i];
		int j=i-1;
		for (;j>=0&&input[j]<key;j--){
			input[j+1]=input[j];
		}
		input[j+1]=key;
	}
}
练习2.1-3
#include<vector>
#include<iostream>
using namespace std;
int Find(vector<double>&input,double f ){
	for (int i=0;i<input.size();i++){
		if (input[i]==f)
			return i;
	}
	cerr<<"No such number "<<f<<" in input"<<endl;
	return -1;
}

  循环不变式的证明
  初始化:将数组中的第一个元素和输入数字进行比较,如果相等,返回元素位置,不等则继续循环。
  保持:显然,如果找不到与输入相等的元素,循环会一直进行下去,直到终止条件。
  终止:如果在循环中能找到相等的元素,则返回元素位置,跳出循环。否则为未找到输入元素,输出未找到元素。

练习2.1-4
#include<vector>
#include<iostream>
using namespace std;
vector<int> Tran10TOX(int num,int X){
	vector<int> ret;
	int result=num;
	while (result!=0){
		int residue=result%X;
		ret.push_back(residue);
		result/=X;
	}
	return ret;
}
int main(int argc,char** argv){
	int a=9,b=10;
	auto A=Tran10TOX(a,2),B=Tran10TOX(b,2),C=Tran10TOX(a+b,2);
	cout<<A.size()<<','<<B.size()<<","<<C.size()<<endl;
}
//注意:高位数储存在vector后面。如:std::vector<int> A[i]是X进制的i+1位。

2.2 分析算法

练习2.2-1

  显然是 Θ \Theta Θ(n3)……

练习2.2-2
#include<vector>
using namespace std;
void ChioseSort(vector<double>&input){
	for (int i=0;i< input.size();i++){
		for (int j=i+1;j< input.size();j++){
			if (input[j]<input[i]){
				auto temp=input[i];
				input[i]=input[j];
				input[j]=temp;
			}
		}
	}
}

  循环不变式的证明:
  初始化:将A的第一个元素和其他n-1个元素进行比较,如果某个元素小于第一个元素,进行交换,继续循环,最后数组第一个元素是数组中最小的。
  保持:若循环进行到第i个,前面已经是按次序排好,第i个元素将于后面n-i个元素进行比较,将后面组的最小值换置到第i个元素上。
  终止:显然当循环到最后一个元素时,其是数组最大值,而且,数组已按升序排好。
  为什么只要和n-1个元素运行?:因为其是用n个元素中的一个元素为参照进行对比的,显然没有必要自己和自己对比。
  其最好和最坏情况下的运行时间均是 Θ ( n 2 ) \Theta(n^2) Θ(n2),因为不管数组顺序如何,其总要进行 ( n − 1 ) ⋅ n 2 \frac{(n-1)\cdot n}2 2(n1)n比较。

练习2.2-3

  合适元素在第i位的出现概率为
(1) P { X = i } = 1 n P\{X=i\}=\frac1{n} \tag{1} P{X=i}=n1(1)
  所以其位置的期望
(2) E ( X ) = ∑ i = 1 n i ⋅ 1 n = n + 1 2 E(X)=\sum_{i=1}^n i\cdot\frac{1}{n}=\frac{n+1}2 \tag{2} E(X)=i=1nin1=2n+1(2)
  所以平均情况下为 Θ \Theta Θ(n)
  在最坏情况下,输入元素需要与数组中所有元素比较,所以其时间复杂度也是为 Θ \Theta Θ(n)。

练习2.2-4

  略…

2.3 设计算法

  MERGE-SORT
  Python

def merge(A,p,q,r):
    maxA=max(A);
    lenLeft=q-p+1;
    lenRight=r-q;
    leftArray=[];
    rightArray=[];
    for i in range(lenLeft):
        leftArray.append(A[p+i]);
    for i in range(lenRight):
        rightArray.append(A[q+i+1]);
    leftArray.append(maxA+100);
    rightArray.append(maxA+100);
    i=j=0;
    for k in range(r-p+1):
        if leftArray[i]<=rightArray[j]:
            A[p+k]=leftArray[i];
            i=i+1;
        else:
            A[p+k]=rightArray[j];
            j=j+1;
    print(A)
    return A

def sortMerge(A,p,r):
    q=int((r+p)/2);
    if r>p:
        sortMerge(A,p,q);
        sortMerge(A,q+1,r);
        A=merge(A,p,q,r);
        return A;

if __name__=='__main__':
    A=[2,1,5,3,6,4];
    print(sortMerge(A,0,len(A)-1));

  C++

#include<iostream>
#include<vector>

using namespace std;

void print (vector <double> a)
{
	for (auto beg=a.begin(),end=a.end();beg!=end;beg++)
	{
		if (beg!=end-1)
		{
			cout<<*beg<<",";
		}
		else
		{
			cout<<(*beg)<<endl;
		}
	}
}
void Merge(vector<double>& A,int p,int q,int r){
	if (p>q||q>r){
		cout<<"Please enter parameters as ascending."<<endl;
		return;
	}
	int n1=q-p+1;
	int n2=r-q;
	vector<double> L,R;
	for (int i=0;i<n1;i++){
		L.push_back(A[p-1+i]);
	}
	for (int i=0;i<n2;i++){
		R.push_back(A[q-1+i+1]);
	}
	int ii=0,ij=0,c=1000;
	L.push_back(c);
	R.push_back(c);
	for (int i=p-1;i<r;i++){
		if (R[ii]<L[ij]){
			A[i]=R[ii];
			ii++;
		}else{
			A[i]=L[ij];
			ij++;
		}
	}
}
//If you want to sort vector A, you should put (A,0,A.size()-1) \
//in function MergeSort.
void MergeSort(vector<double>& A,int p,int r){
	if (r>p){
		int q=(r+p)/2;
		MergeSort(A,p,q);
		MergeSort(A,q+1,r);
		Merge(A,p,q,r);
	}
}
练习 2.3-1

  过程如下图:

3
41
52
26
38
57
9
49
3 , 41
26 , 52
38 , 57
9 , 49
3 , 26 , 41 ,52
9 , 38 , 49 ,57
3 , 9 , 26 , 38 , 41 , 49 , 52 , 57
练习 2.3-2

  程序如下:

#include<iostream>
#include<vector>

using namespace std;

void Merge(vector<double>& A,int p,int q,int r){
	if (p>q||q>r){
		cout<<"Please enter parameters as ascending."<<endl;
		return;
	}
	int n1=q-p+1;
	int n2=r-q;
	vector<double> L,R;
	for (int i=0;i<n1;i++){
		L.push_back(A[p-1+i]);
	}
	for (int i=0;i<n2;i++){
		R.push_back(A[q-1+i+1]);
	}
	int ii=0,ij=0;
	for (int i=p-1;i<r;i++){
		if ((R[ii]<L[ij]||ij>L.size()-1)&&ii<R.size()){
			A[i]=R[ii];
			ii++;
		}else{
			A[i]=L[ij];
			ij++;
		}
	}
}
void MergeSort(vector<double>& A,int p,int r){
	if (r>p){
		int q=(r+p)/2;
		MergeSort(A,p,q);
		MergeSort(A,q+1,r);
		Merge(A,p,q,r);
	}
}
练习2.3-3

  略

练习2.3-4

  略

练习2.3-5

  注意输入必须是个已经排序好的数组。

#include<vector>
#include<iostream>

using namespace std;

void find(const vector<double>input,int begin,int end,const double f){
	int half=(begin+end)/2;
	if (input[half]==f){
		cout<<"Find "<<f<<" in vector."<<endl;
		return ;
	}else if (end-begin<2){
		cerr<<"Not Find"<<endl;
		return ;
	}else if (input[half]<f){
		find(input,half,end,f);
	}else if (input[half]>f){
		find(input,begin,half,f);
	}
}

  因为采用分治方法 T ( n ) = T ( n 2 ) + Θ ( 1 ) T(n)=T(\frac {n}2)+\Theta(1) T(n)=T(2n)+Θ(1),很容易证明最坏运行时间复杂度为 Θ ( l o g 2 ( n ) ) \Theta (log_2(n)) Θ(log2(n))

练习2.3-6

  是不可以的。在最坏情况下,即使使用find()函数找到了一个未插入的元素在已经排好的数组中的位置,要使得这个元素插入数组中需要的时间复杂度是 Θ ( n ) \Theta (n) Θ(n),所以总的时间复杂度为 Θ ( n ( n + l o g 2 ( n ) ) ) \Theta(n(n+log_2(n))) Θ(n(n+log2(n)))

练习2.3-7

  需要上面的MERGE-SORT中的两个函数Merge和MergeSort,就不贴在下面了。

#include<vector>
#include<iostream>

using namespace std;

int find(const vector<double>input,int begin,int end,const double f){
	int half=(begin+end)/2;
	int output=0;
	if (input[half]==f){
		output=half;
		return output;
	}else if (end-begin<2){
		output=begin;
		return output;
	}else if (input[half]<f){
		output=find(input,half,end,f);
	}else if (input[half]>f){
		output=find(input,begin,half,f);
	}
	return output;
}
void Match(const vector<double>input,int begin,int end,const double f){
	for (int i=begin,j=end;i<j;){
		double temp=input[i]+input[j];
		if (temp==f){
			cout<<"Find "<<f<<" is sum of "<<input[i]<<" and "<<input[j]
			<<"."<<endl;
			return;
		}else if(temp>f){
			j--;
		}else if(temp<f){
			i++;
		}
	}
	cout<<"No match."<<endl;
}
int main(int argc,char** argv){
	vector<double> a={12,3,423,546,123,633456,12312};
	int f=125;
	MergeSort(a,0,a.size()-1);
	int pos=find(a,0,a.size()-1,f);
	Match(a,0,pos,f);
	return 0;
}

  上面算法分析:
  总的时间复杂度=MergeSort的时间复杂度+find的时间复杂度+Match的时间复杂度。
  前面的分析可以知道:
  MergeSort的时间复杂度= Θ ( n l o g 2 ( n ) ) \Theta (nlog_2(n)) Θ(nlog2(n))
  find的时间复杂度= Θ ( l o g 2 ( n ) ) \Theta (log_2(n)) Θ(log2(n))
  显然Match的时间复杂度== Θ ( n ) \Theta (n) Θ(n)
  所以总的时间复杂度= Θ ( n l o g 2 ( n ) ) \Theta (nlog_2(n)) Θ(nlog2(n))

思考题

思考题2-1

  略

思考题2-2

  略

思考题2-3

  略

思考题2-4

  2-4.d
  改下MergeSort即可。

#include <iostream>
#include <vector>

using namespace std;

void Merge(vector<double>& A,int p,int q,int r,int&count){
	int n1=q-p+1;
	int n2=r-q;
	vector<double> L,R;
	for (int i=0;i<n1;i++){
		L.push_back(A[p-1+i]);
	}
	for (int i=0;i<n2;i++){
		R.push_back(A[q-1+i+1]);
	}
	int ii=0,ij=0,cnt=0;
	for (int i=p-1;i<r;i++){
		if (ij>L.size()-1&&ii<R.size()){
			A[i]=R[ii];
			ii++;
		}else if(ij<L.size()&&ii==R.size()){
			A[i]=L[ij];
			ij++;
			count+=ii;
		}else if (L[ij]>R[ii]){
			A[i]=R[ii];
			ii++;
			cnt++;
		}else if (L[ij]<=R[ii]){
			count+=cnt;
			A[i]=L[ij];
			ij++;
		}
		
	}
}
int CountReverse(vector<double>& A,int p,int r){
	int count=0;
	if (r>p){
		int q=(r+p)/2;
		CountReverse(A,p,q);
		CountReverse(A,q+1,r);
		Merge(A,p,q,r,count);
	}
	return count;
}

int main(int argc,char**){
	vector<double> a={3,4,6,8,1,2,5,7};
	int count=CountReverse(a,1,a.size());
	cout<<count<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值