算法导论读书笔记-第一部分-基础知识
文章目录
算法导论读书笔记-第一部分-基础知识
本文章仅记录自己学习算法导论过程中遇到的不会的以及不熟悉的问题和知识点
有时间的话博主以后再放假的时候会将具体内容加以完善重点记录算法实现、数据结构细节、例题练习分析等等
算法在计算中的应用
讲了算法知识在数据科学、生物科学、军事、遗传学等等领域的一些应用,普及了算法的基本知识
这一章目前来讲没啥好记的,以后回顾的时候再看看哈
算法基础
练习题分析
2.3-7查找和为x的两数
题目描述:
描述一个运行时间为 Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)的算法,给定n个整数集合S和另外一个整数x,该算法能确定S中是否存在两个元素其和恰好等于x,
存在则输出
Exists two number!
,否则输出None exists!
进阶:查找和为x的三数、四数?(四数可以先对两个数之和进行枚举排序,再对剩余两个数进行二分查找)
方法1-分治
这两个数的存在位置只可能有三种情况:
- 均位于左侧数组
- 均位于右侧数组
- 一左一右
其中第三种情况放在归并的时候实现,但是直接遍历的话时间复杂度过高,可以先对其就行归并排序,排序完成好再对left数组中的元素在right数组中进行一次二分查找(错啦!应该是先查找再排序!),所以总时间复杂度为 Θ ( n l o g 2 n ) \Theta(nlog^2n) Θ(nlog2n),几乎等价于 Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)
int binary_search(int *array,int left,int right,int target){//表示在array数组的left-right范围中查找target,如果查到返回1,否则返回0 int low,mid,high; low=left;high=right; while(low<=high){ mid=(low+high)/2; if(array[mid]<target) low=mid+1; else if(array[mid]>target) high=mid-1; else return 1; } return 0; } int merge(int *array,int *x,int left,int center,int right,int target){//return值与binary_search同理 int i,j,k,flag; flag=0; //注意是先查找再排序 for(i=left;i<=center;i++){//对left数组进行二分查找 if(binary_search(array,center+1,right,target-array[i])){ flag=1; break; } } i=k=left;j=center+1; while(i<=center&&j<=right) x[k++]=(array[i]<=array[j])?array[i++]:array[j++]; while(i<=center) x[k++]=array[i++]; while(j<=right) x[k++]=array[j++]; for(i=left;i<=right;i++) array[i]=x[i]; //排序完成 return flag; } int merge_sort(int *array,int *x,int left,int right,int target){//可以返回匹配对象的个数 if(left>=right) return 0; int center=(left+right)/2; return (merge_sort(array,x,left,center,target)+merge_sort(array,x,center+1,right,target)+merge(array,x,left,center,right,target)>0)?1:0; }
方法2-排序+二分查找
思路非常简单,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
int compar(const void* p1,const void* p2){ return (*(int*)p1>*(int*)p2)?1:-1; } int binary_search(int *array,int left,int right,int target){//表示在array数组的left-right范围中查找target,如果查到返回1,否则返回0 int low,mid,high; low=left;high=right; while(low<=high){ mid=(low+high)/2; if(array[mid]<target) low=mid+1; else if(array[mid]>target) high=mid-1; else return 1; } return 0; } qsort(array,n,sizeof(int),compar); for(int i=0;i<n;i++){ if(binary_search(array,0,n-1,target-array[i])){ flag=1; break; }//不过有一个小瑕疵,就是如果2*array[i]=target的话也会被认定为可行(当然这个问题非常容易解决) }//flag=1代表存在
方法3-hash表
该方法虽然快捷,时间复杂度仅为 O ( n ) O(n) O(n),但是限制条件过多,不推荐使用
void array_input(FILE *in,int *array,int n){ int tmp=0; for(int i=0;i<n;i++){ fscanf(in,"%d",&tmp); array[tmp]=1;//表示存在tmp这个值 } } for(int i=0;i<maxsize;i++){ if(target-i>=maxsize||target-i<0) continue; if(array[i]==1&&array[target-i]==1){ flag=1; break; } }
思考题分析
2.3Horner规则
简而言之就是九章算法的计算规则,不知道看到多少遍了
2.4逆序对
已经实现过了也同样不重新写了
函数的增长
给出了函数增长率表示的一些约定符号
符号 | 含义 |
---|---|
T ( n ) = Ω ( f ( n ) ) T(n)=\Omega(f(n)) T(n)=Ω(f(n)) | T ( n ) T(n) T(n)的增长率大于等于 f ( n ) f(n) f(n) |
T ( n ) = ω ( f ( n ) ) T(n)=\omega(f(n)) T(n)=ω(f(n)) | T ( n ) T(n) T(n)的增长率大于 f ( n ) f(n) f(n) |
T ( n ) = Θ ( f ( n ) ) T(n)=\Theta(f(n)) T(n)=Θ(f(n)) | T ( n ) T(n) T(n)的增长率等于 f ( n ) f(n) f(n) |
T ( n ) = o ( f ( n ) ) T(n)=o(f(n)) T(n)=o(f(n)) | T ( n ) T(n) T(n)的增长率小于 f ( n ) f(n) f(n) |
T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n)) | T ( n ) T(n) T(n)的增长率小于等于 f ( n ) f(n) f(n) |
⌊ x ⌋ \lfloor x \rfloor ⌊x⌋ | 向下取整 |
⌈ x ⌉ \lceil x \rceil ⌈x⌉ | 向上取整 |
注: l o g k N ≈ O ( N ) log^kN\approx O(N) logkN≈O(N),这足以说明对数级别的增长是多么缓慢
由此可得: O ( N 2 l o g ( N ) ) 与 O ( N 2 ) O(N^2log(N))与O(N^2) O(N2log(N))与O(N2)差距不大。
标准记号与标准符号
斐波那契数与黄金分割率
斐波那契数的表示:
F
0
=
0
F
1
=
1
F
i
=
F
i
−
1
+
F
i
−
2
F_0=0\\F_1=1\\F_i=F_{i-1}+F_{i-2}
F0=0F1=1Fi=Fi−1+Fi−2
斐波那契数与**黄金分割率
ϕ
\phi
ϕ以及其共轭数
ϕ
^
\hat{\phi}
ϕ^**有关
黄金分割率方程 x 2 + x + 1 = 0 x^2+x+1=0 x2+x+1=0
{ ϕ = 1 + 5 2 ≈ 1.61803 ϕ ^ = 1 − 5 2 ≈ − 0.61803 \begin{cases}\phi=\frac{1+\sqrt{5}}{2}&\approx 1.61803\\\hat{\phi}=\frac{1-\sqrt{5}}{2}&\approx -0.61803\end{cases} {ϕ=21+5 ϕ^=21−5 ≈1.61803≈−0.61803
二者关系: F i = ϕ i − ϕ ^ i 5 F_i=\frac{\phi^i-\hat{\phi}^i}{\sqrt{5}} Fi=5 ϕi−ϕ^i
该关系可由数学归纳法证明,且由此可得,斐波那契数列以指数形式增长
分治策略
解决问题基本形式:Recursion
解决问题基本步骤:Divide->Comquer->Combine
最大子数组问题
与一些股票交易问题等价:需要一个差量转化的操作将其转化为最大子数组问题
同样,之前已经在c中实现过了
并且解决此类问题的最好方法其实是线性动态规划
矩阵乘法的Strassen算法
没啥好说的,之前也使用c实现过一次了,现在就优化一下代码得了
基本矩阵递归乘法实现
时间复杂度上没有任何优化,但是如果转化一下就不一样了
但是局限性也很大,至少就目前所学只能够处理 2 n ∗ 2 n 2^n*2^n 2n∗2n型的矩阵
//函数实现的效果,是C矩阵(n*n)的内容变成A矩阵乘以B矩阵的值
//限定条件为n是2的幂次
//采用下标定位的方式(虽然比较麻烦)
//改进以下,直接使用下标和二维数组传参
//矩阵乘法二度修改,使其可以接受更大的参数,并且传参更加方便
void MERGE_SQUARE_MULTIPLY(int **A,int **B,int **C,int Ai,int Aj,int Bi,int Bj,int n){
if(n==1)
C[Ai][Bj]+=A[Ai][Aj]*B[Bi][Bj];
else{
MERGE_SQUARE_MULTIPLY(A,B,C,Ai,Aj,Bi,Bj,n/2);//A11*B11
MERGE_SQUARE_MULTIPLY(A,B,C,Ai,Aj+n/2,Bi+n/2,Bj,n/2);//A12*B21
MERGE_SQUARE_MULTIPLY(A,B,C,Ai,Aj,Bi,Bj+n/2,n/2);//A11*B12
MERGE_SQUARE_MULTIPLY(A,B,C,Ai,Aj+n/2,Bi+n/2,Bj+n/2,n/2);//A12*B22
MERGE_SQUARE_MULTIPLY(A,B,C,Ai+n/2,Aj,Bi,Bj,n/2);//A21*B11
MERGE_SQUARE_MULTIPLY(A,B,C,Ai+n/2,Aj+n/2,Bi+n/2,Bj,n/2);//A22*B21
MERGE_SQUARE_MULTIPLY(A,B,C,Ai+n/2,Aj,Bi,Bj+n/2,n/2);//A21*B12
MERGE_SQUARE_MULTIPLY(A,B,C,Ai+n/2,Aj+n/2,Bi+n/2,Bj+n/2,n/2);//A22*B22
}
}
void init(int **A,int **B,int n){
FILE *in=fopen("random_square_matrix.txt","r");
int i,j;
for(i=0;i<n;i++){
for(j=0;j<n;j++)
fscanf(in,"%d",&A[i][j]);
}
for(i=0;i<n;i++){
for(j=0;j<n;j++)
fscanf(in,"%d",&B[i][j]);
}
fclose(in);
}
void output_result(int **C,int n){
int i,j;
for(i=0;i<n;i++){
for(j=0;j<n;j++)
printf("%-5d%c",C[i][j],(j==n-1)?'\n':'\0');
}
}
int main(){
int n;
printf("Plesas input the size of matrix you want to calculate:");
scanf("%d",&n);
int **A=(int**)calloc(sizeof(int*),n);
int **B=(int**)calloc(sizeof(int*),n);
int **C=(int**)calloc(sizeof(int*),n);
for(int i=0;i<n;i++){
A[i]=(int*)calloc(sizeof(int),n);
B[i]=(int*)calloc(sizeof(int),n);
C[i]=(int*)calloc(sizeof(int),n);
}
init(A,B,n);//初始化
MERGE_SQUARE_MULTIPLY(A,B,C,0,0,0,0,n);//矩阵乘法计算
output_result(C,n);//结果输出
return 0;
}
Strassen矩阵乘法实现
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<ctype.h> #include<math.h> #include<time.h> #define N 8//非常不幸的一点是,大于64就直接超范围了(需要使用malloc才行) clock_t start,end; void MATRIX_MULTIPLY(int A[][N],int B[][N],int C[][N]){//C=A*B int i,j,t; for(i=0;i<2;i++) for(j=0;j<2;j++){ C[i][j]=0; for(t=0;t<2;t++) C[i][j]+=A[i][t]*B[t][j]; } } void MATRIX_ADD(int n,int X[][N],int Y[][N],int Z[][N]){//Z=X+Y int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++) Z[i][j]=X[i][j]+Y[i][j]; } void MATRIX_SUB(int n,int X[][N],int Y[][N],int Z[][N]){//Z=X-Y int i,j; for(i=0;i<n;i++) for(j=0;j<n;j++) Z[i][j]=X[i][j]-Y[i][j]; } void STRASSEN(int n,int A[][N],int B[][N],int C[][N]){ if (n==2){ MATRIX_MULTIPLY(A,B,C); return; } //array init int A11[N][N],A12[N][N],A21[N][N],A22[N][N]; int B11[N][N],B12[N][N],B21[N][N],B22[N][N]; int C11[N][N],C12[N][N],C21[N][N],C22[N][N]; int S1[N][N],S2[N][N],S3[N][N],S4[N][N],S5[N][N],S6[N][N],S7[N][N],S8[N][N],S9[N][N],S10[N][N]; int P1[N][N],P2[N][N],P3[N][N],P4[N][N],P5[N][N],P6[N][N],P7[N][N]; int i,j; //A,B subarray init for(i=0;i<n/2;i++) for(j=0;j<n/2;j++){ A11[i][j]=A[i][j]; A12[i][j]=A[i][j+n/2]; A21[i][j]=A[i+n/2][j]; A22[i][j]=A[i+n/2][j+n/2]; B11[i][j]=B[i][j]; B12[i][j]=B[i][j+n/2]; B21[i][j]=B[i+n/2][j]; B22[i][j]=B[i+n/2][j+n/2]; } //S1-S10 init MATRIX_SUB(n/2,B12,B22,S1); MATRIX_ADD(n/2,A11,A12,S2); MATRIX_ADD(n/2,A21,A22,S3); MATRIX_SUB(n/2,B21,B11,S4); MATRIX_ADD(n/2,A11,A22,S5); MATRIX_ADD(n/2,B11,B22,S6); MATRIX_SUB(n/2,A12,A22,S7); MATRIX_ADD(n/2,B21,B22,S8); MATRIX_SUB(n/2,A11,A21,S9); MATRIX_ADD(n/2,B11,B12,S10); //P1-P7 init STRASSEN(n/2,A11,S1,P1); STRASSEN(n/2,S2,B22,P2); STRASSEN(n/2,S3,B11,P3); STRASSEN(n/2,A22,S4,P4); STRASSEN(n/2,S5,S6,P5); STRASSEN(n/2,S7,S8,P6); STRASSEN(n/2,S9,S10,P7); //final_calculate //C11 MATRIX_ADD(n/2,P5,P4,C11); MATRIX_SUB(n/2,C11,P2,C11); MATRIX_ADD(n/2,C11,P6,C11); //C12 MATRIX_ADD(n/2,P1,P2,C12); //C21 MATRIX_ADD(n/2,P3,P4,C21); //C22 MATRIX_ADD(n/2,P5,P1,C22); MATRIX_SUB(n/2,C22,P3,C22); MATRIX_SUB(n/2,C22,P7,C22); //final C for(i=0;i<n/2;i++) for(j=0;j<n/2;j++){ C[i][j]=C11[i][j]; C[i][j+n/2]=C12[i][j]; C[i+n/2][j]=C21[i][j]; C[i+n/2][j+n/2]=C22[i][j]; } } void init(int A[][N],int B[][N]){ FILE *in=fopen("random_square_matrix.txt","r"); int i,j; for(i=0;i<N;i++){ for(j=0;j<N;j++) fscanf(in,"%d",&A[i][j]); } for(i=0;i<N;i++){ for(j=0;j<N;j++) fscanf(in,"%d",&B[i][j]); } fclose(in); } void OUTPUT(int R[][N]){ int i,j; for(i=0;i<N;i++){ for(j=0;j<N;j++) printf("%-3d%c",R[i][j],(j==N-1)?'\n':'\0'); } } int main(){ int A[N][N]={0}; int B[N][N]={0}; int C[N][N]={0}; init(A,B); start=clock(); STRASSEN(N,A,B,C); end=clock(); OUTPUT(C); printf("The function time:%lf ms\n",(double)(end-start)); return 0; }
该算法实现较为繁琐,需要更加细心才行
文章暂时告一段落,以后再来更新。
推荐阅读
-
复习C++基础知识-----“我的第一本C++”读书笔记2
-
Python基础教程(第3版)读书笔记:第一章基础知识
-
算法导论读书笔记-第一部分-基础知识
-
复习C++基础知识-----“我的第一本C++”读书笔记1
-
复习C++基础知识-----“我的第一本C++”读书笔记3
-
《Java并发编程实战》总结 - 第一部分 基础知识
-
温习Android基础知识——《第一行代码(第三版)》读书笔记 Chapter 14 高级技巧
-
第一部分:趣味算法入门;第五题 :兔子产子问题
-
算法(第四版)读书笔记 第一章
-
温习Android基础知识——《第一行代码(第三版)》读书笔记 Chapter 10 Service