《算法分析与设计》笔记总结

文章详细介绍了各种算法的设计和分析,包括递归、分治法、动态规划、贪心算法、回溯法和分支限界法。讨论了它们的性质、适用场景和实例,如0-1背包问题、最小生成树、哈夫曼编码等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一章 算法引论

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],其中XY有n位;ABCD均有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[
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值