第一章 算法引论
1.1 算法与程序
-
算法定义:解决问题的方法或过程
-
算法的性质:
- (1)输入:有零个或多个外部量作为算法的输入
- (2)输出:算法产生至少一个量作为输出
- (3)确定性:组成算法的每条指令是清晰的,无歧义的
- (4)有限性:算法中每条指令的执行次数有限,执行每条指令的时间也有限
- 有时还会加入通用性或可行性
-
程序的定义:是算法用某种程序设计语言的具体实现。
-
程序与算法的区别:程序可以不满足算法的第四点性质即有限性。例如操作系统,是在无限循环中执行的程序。
1.2 表达算法的抽象机制
- 为了将顶层算法与底层算法隔开,使二者在设计时不互相牵制,互相影响,必须对二者的接口进行抽象。让底层只通过接口为顶层服务,顶层也只通过接口调用底层运算。这个接口就是抽象数据类型(ADT)。
1.3 描述算法
- 有多种方式,如:自然语言方式,表格方式,高级程序语言方式等…
1.4 算法复杂性分析
- 算法分析的目的:分析算法占用计算机资源的情况,对算法做出比较和评价,设计出更好的算法
- 算法的复杂性是算法运行时所需的计算机资源的量,需要时间资源的量称为时间复杂性,需要空间资源的量称为空间复杂性。
- C=F(N,I,A),用N,I,A分别表示算法要解的问题的规模,算法的输入和算法本身,F表示是上诉N,I,A的确定的三元函数,C表示复杂性
- 一般只考虑3种情况下的时间复杂性,即最坏情况,最好情况,平均情况
- 实践表明,可操作性最好且最有实际价值的是最坏情况下的时间复杂性。
- 复杂性渐进性态:
对于T(N),如果存在~T(N),使得当N→∞时有(T(N)-~T(N))/T(N)→0,那么就说/~T(N)是T(N)当N→∞时的渐进性态。
- 如果存在正的常数C和自然数N0,使得当N≥N0时有f(N)≤Cg(N),则称函数f(N)当N充分大时上有界,且g(N)是它的一个上界,记为f(N)=O(g(N))。这时还说f(N)的阶不高于g(N)的阶。
- 对于符号O,有如下运算规则:
- O(f)+O(g)=O(max(f,g))
- O(f)+O(g)=O(f+g)
- O(f)O(g)=O(fg)
- 如果g(N)=O(f(N)),则O(f)+O(g)=O(f)
- O(Cf(N))=O(f(N)),其中C是一个正的常数
- f=O(f)
第二章 递归与分治策略
2.1 递归的概念
- 直接或间接地调用自身的算法称为递归算法。
- 用函数自身给出定义的函数称为
递归函数
- 递归函数的两个要素:
边界条件
和递归方程
- 阶乘函数:
#include<iostream>
using namespace std;
int factorial(int n){
if(n==0) return 1;
else{
return n*factorial(n-1);
}
}
- Fibonacci数列:
#include<iostream>
using namespace std;
int fibonacci(int n){
if(n<=1) return 1;
else return fibonacci(n-1)+fibonacci(n-2);
}
- hanoi塔:
def hanoi(n,a,b,c):
#将a上的n个圆盘经过c移动到b
if(n>0):
hanoi(n-1,a,c,b)
move(a,b)
hanoi(n-1,c,b,a)
- 递归算法的优点:结构清晰,可读性强,容易用数学归纳法来证明算法的正确性
- 递归算法的缺点:运行效率低,无论是耗费的计算时间还是占用的存储空间都比非递归算法多。
- 消除递归的方法:
- ①采用一个用户定义的栈来模拟系统的递归调用工作栈,从而达到将递归算法改为非递归算法的目的
- ②用递推来实现递归函数
2.2 分治法的基本思想
-
分治法的基本思想:将一个规模为n的问题
分解
为k个规模较小
的子问题,这些子问题相互独立
且与原问题相同
。递归地
解这些子问题,然后将各子问题的解合并
得到原问题的解。 -
分治法的适用条件:
- ①该问题的规模缩小到一定程度容易解决。
- ②该问题可以分解为若干个规模较小的相同问题。即该问题具有最优子结构性质。
- ③该问题分解出的子问题的解可以合并为该问题的解。
- ④子问题间不包含公共的子问题(各子问题相互独立)
-
分治法的步骤:
- 划分
- 解决
- 合并
2.3 二分搜索技术
def binarySearch(a,x):
#a是数组,x是要搜索的数
a=sorted(a)
#a要求有序(从小到大)
n=len(a)
left,right=0,n-1
while(left<=right):
middle=(left+right)//2
if(x==a[middle]):
return middle
elif(x>a[middle]):
left=middle+1
else:
right=middle-1
#未找到
return -1
- 最坏情况下,时间复杂度是O(logn)
2.4 大整数乘法
- 设x和y都是n位的二进制整数,现在要计算他们的乘积xy。如果直接相乘,需要O(n^2)步,
- 而其
分治法
是:将n位二进制整数X和Y都分为2段,每段的长为n/2位:
X=[A][B],Y=[C][D],其中X,Y有n位;A,B,C,D均有n/2位
由此可以得到:
X=A*2^(n/2)+B , Y=C*2^(n/2)+D
XY=(A*2^(n/2)+B)(C*2^(n/2)+D)
=A*C*2^n+(A*D+C*B)*2^(n/2)+B*D
=A*C*2^n+((A-B)(D-C)+A*C+B*D)*2^(n/2)+B*D
最后一个式子看起来似乎复杂了,但是它仅需做3次n/2位整数的乘法,6次加减法和2次移位
2.5 Strassen矩阵乘法
- 对于方阵(nn)A,B,C,有C=AB,将它们都分块成4个大小相等的子矩阵,每个子矩阵都是(n/2)*(n/2)的方阵
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8iLmTMe7-1596025346681)(https://s2.ax1x.com/2019/11/20/MhaKxS.png)]
2.7 合并排序
def merge(arr,left,mid,right):
#left,right为需要合并的数组范围
#mid为中间下标,左边比中值小,右边比中值大
i=left
j=mid+1
#复制一个临时数组
aux=arr[:]
for k in range(left,right+1):
#如果左指针超过mid,即右边还有剩余
if(i>mid):
arr[k]=aux[j]
j=j+1
#如果右指针超过right,即左边还有剩余
elif(j>right):
arr[k]=aux[i]
i=i+1
#如果左边小,则左边合并
elif(aux[i]<aux[j]):
arr[k]=aux[i]
i=i+1
#如果右边小
else:
arr[k]=aux[j]
j=j+1
def mergeSort(arr,left,right):
#如果已经遍历完
if(left>=right):
return ;
#取中值,拆成左右两边
mid=(left+right)//2
#对左半边进行归并排序
mergeSort(arr,left,mid)
#对右半边进行归并排序
mergeSort(arr,mid+1,right)
#合并算法
merge(arr,left,mid,right)
- 最坏情况下的时间复杂度为O(nlogn)
2.8 快速排序
- 步骤:分解,递归求解,合并
def quicksort(arr,low,high):
if low<high :
index=getindex(arr,low,high)
quicksort(arr,low,index-1)
quicksort(arr,index+1,high)
#快速排序算法核心
#作用:将小于基准值的数放在其左边,大于在右边
def getindex(arr,low,high):
#默认第一个数字为标准值
temp=arr[low]
#当未遍历完,即左右指针未相遇
while(low<high):
#如果右边大于标准值,右指针左移
while((low<high)and(arr[high]>=temp)):
high=high-1
#此时右指针对应值小于标准值,将其复制给左指针位置
arr[low]=arr[high]
#当左边小于标准值,左指针右移
while((low<high)and(arr[low]<=temp)):
low=low+1
#此时左指针对应值大于标准值,将其复制给右指针位置
arr[high]=arr[low]
#将标准值赋值给左右指针相遇的位置
arr[low]=temp
#此时low左边全部小于等于arr[low],low右边全部大于等于arr[low]
return low
- 快排平均情况下的时间复杂度是O(nlogn),最坏情况下的时间复杂度是O(n^2)
2.9 线性时间选择
- 找出一组数中,第X大(小)的数
- 采用了随机划分算法
2.10 最近点对问题
- 时间复杂度分析O(nlogn)
"""
Copyright: Copyright (c) 2019
Author: Justlovesmile
Title: 最近点对问题
"""
#按x坐标排序的点
class Point1:
#x,y为坐标,id为序号
def __init__(self,xx,yy,index):
self.x=xx
self.y=yy
self.id=index
#按y坐标排序的点
class Point2(Point1):
#x,y为坐标,id为该点按x排序时的序号
def __init__(self,xx,yy,index):
self.x=xx
self.y=yy
self.id=index
#表示输出的平面点对
class Pair:
#a,b为点,dist为距离
def __init__(self, aa, bb,dd):
self.a=aa
self.b=bb
self.dist=dd
#求平面上任意两点u,v的距离
def dist(u,v):
dx=u.x-v.x
dy=u.y-v.y
return dx*dx+dy*dy
#归并排序
def merge(S,order,left,mid,right):
i=left
j=mid+1
aux=S[:]
#按x排序
if(order=='x'):
for k in range(left,right+1):
if(i>mid):
S[k]=aux[j]
j=j+1
elif(j>right):
S[k]=aux[i]
i=i+1
elif(S[i].x<aux[j].x):
S[k]=aux[i]
i=i+1
else:
S[k]=aux[j]
j=j+1
#按y排序
elif(order=='y'):
for k in range(left,right+1):
if(i>mid):
S[k]=aux[j]
j=j+1
elif(j>right):
S[k]=aux[i]
i=i+1
elif(S[i].y<aux[j].y):
S[k]=aux[i]
i=i+1
else:
S[k]=aux[j]
j=j+1
#归并排序
def mergeSort(S,x,left,right):
if(left>=right):
return ;
mid=(left+right)//2
mergeSort(S,x,left,mid)
mergeSort(S,x,mid+1,right)
merge(S,x,left,mid,right)
#计算最接近点对
def closePair(S,Y,Z,l,r):
#两个点
if(r-l==1):
return Pair(S[l],S[r],dist(S[l],S[r]))
#三个点
if(r-l==2):
d1=dist(S[l],S[l+1])
d2=dist(S[l+1],S[r])
d3=dist(S[l],S[r])
if((d1<=d2)and(d1<=d3)):
return Pair(S[l],S[l+1],d1)
if(d2<=d3):
return Pair(S[l+1],S[r],d2)
else:
return Pair(S[l],S[r],d3)
#多于三个点
m=(l+r)//2
f=l
g=m+1
for i in range(l,r+1):
if(Y[i].id>m):
Z[g]=Y[i]
g=g+1
else:
Z[f]=Y[i]
f=f+1
#递归求解
best = closePair(S,Z,Y,l,m)
right = closePair(S,Z,Y,m+1,r)
#选最近的点对
if(right.dist<best.dist):
best=right
merge(Y,"y",l,m,r)
k=l
#距离中线最近的
for i in range(l,r+1):
if(abs(S[m].x-Y[i].x)<best.dist):
Z[k]=Y[i]
k=k+1
for i in range(l,k):
for j in range(i+1,k):
if(Z[j].y-Z[i].y<best.dist):
dp=dist(Z[i],Z[j])
if(dp<best.dist):
best=Pair(S[Z[i].id],S[Z[j].id],dp)
#返回最近点对
return best
#一维点集
def cpair1(S):
#先设为正无穷
min_d=float("inf")
S=sorted(S)
for i in range(1,len(S)):
dist=abs(S[i]-S[i-1])
if(dist<min_d):
pair=[]
min_d=dist
pair.append([S[i-1],S[i]])
elif(dist==min_d):
pair.append([S[i-1],S[i]])
print("Closest point:")
for i in pair:
print(i,end=" ")
print("\nMin_dist:",min_d)
#二维点集
def cpair2(S):
Y=[]
n=len(S)
if(n<2):
return ;
#按X坐标排序
mergeSort(S,"x",0,n-1)
#以Point2类型赋值
for i in range(n):
p=Point2(S[i].x,S[i].y,i)
Y.append(p)
#按y坐标排序
mergeSort(Y,"y",0,n-1)
Z=Y[:]
return closePair(S,Y,Z,0,n-1)
def main():
#输入一维还是二维点平面
model=input("Please choose model of '1' or '2':").split()[0]
S=[]
#一维点对
if(model == '1'):
point=input("Please input a group of number in order:\n").split()
#如果输入空点对
if(len(point)==0):
raise ValueError("您输入了空点对!")
#转换类型
for i in range(len(point)):
S.append(int(point[i]))
#输出最近点对
cpair1(S)
#二维点对
elif(model == '2'):
#输入点数
n=int(input("Please input how many points:\n"))
if(n==0):
raise ValueError("您输入了0个点!")
for i in range(n):
words=f"please input the No.{i+1} point (like: x y) in x order:"
point=input(words).split()
p=Point1(int(point[