数据结构与算法【进阶】

贪心算法

在对问题求解时,总市做出当前看来时最好的选择。

  1. 不从整体最优上考虑
  2. 局部最优解

找零问题

假设商店老板需要找零n元钱,钱币的面额:100元、50元、20元、5元、1元,如何找零使得所有钱币数量最少?

t = [100,50,20,5,1]
def change(t,n):
    m = [0 for _ in range(len(t))]
	for i,money in enumerate(t):
        m[i] = n // money
        n = n % money
    return m,n

背包问题

一个小偷在某个商店发现右n个商品,第i个商品价值vi元,重wi千克。他希望拿走的价值尽量高,但他的背包子最多只能容纳W千克的东西。他应该拿走哪些商品?

0-1背包:对于一个商品,小偷要么把它完整拿走,要么留下。不能只拿走一部分,或把一个商品拿走多次。(商品为金条)

分数背包:对于一个商品,小偷可以拿走其中任意一部分。(商品为金砂)

goods = [(60,10),(100,20),(120,30)] #(价格,重量)
goods.sort(key=lambda x:x[0]/x[1],reverse=True)

def fractional_backpack(goods,w):
    m = [0 for _ in range(len(goods))]
    for i,(price,weight) in enumerate(goods):
        if w >= weight:
            m[i]=1
            w -= weight
        else:
            m[i] =  w / weight
            w = 0
            break
     return m   

拼接最大数字问题

有n个非负整数,将其按照字符串拼接的方式拼接为一个整数。如何拼接可以使得得到的整数最大?

from functools import cmp_to_key
li = [32,94,238,1286,6,71]

def xy_cmp(x,y):
    if x+y < y+x:
        return 1
    elif x+y > y+x:
        return -1
    else:
        return 0

def number_join(li):
	li = list(map(str,li))
    li.sort(key=cmp_to_key(xy_cmp))
    return "".join(li)

活动选择问题

假设有n个活动,这些活动要占用同一片场地,而场地在某时刻只能共一个活动使用

每个活动都有一个开始时间si和结束时间fi,表示活动在[si,fi)区间占用场地

问:安排哪些活动能够使该场地举办的活动的个数最多?

i 1 2 3 4 5 6 7 8 9 10 11
si 1 3 0 5 3 5 6 8 8 2 12
fi 4 5 6 7 9 9 10 11 12 14 16

贪心结论:最先结束的活动一定是最优解的一部分

activities = [(1,4),(3,5),(8,6),(5,7),(3,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
# 保证活动是按结束时间排号序的
activities.sort(key=lambda x:x[1])

def activity_slection(a):
    res = [a[0]]
    for i in range(1,len(a)):
        if a[i][0]>=res[-1][1]: # 当前活动的开始时间小于等于最后一个入选活动的结束时间
            # 不冲突
            res.append(a[i])
    return res
            

动态规划

  1. 最优子结构(递推式)
  2. 重复子问题,将重复的子问题存起来

斐波那契数列

  1. Fn=Fn-1+Fn-2

  2. 递归求解第n项

    def fib(n):
        if n == 1 or n == 2:
            return 1
        else:
        	return fib(n-1)+fib(n-2)
    
    '''
    为什么递归效率低?
    因为有重复计算
    f(5)=f(4)-f(3)
    f(4)=f(3)-f(2) 
    '''    
    # 此处循环方法使用的动态规划思想    
    def fib_no_rec(n):
        f = [0,1,1]
        if n > 2:
            for i in range(n-2):
                num = f[-1]+f[-2]
                f.append(num)
        return f[n]   
    

钢条切割问题

某公司出售钢条,出售价格与钢条长度之间的关系如下表:

长度i 1 2 3 4 5 6 7 8 9 10
价格pi 1 5 8 9 10 17 17 20 24 30

问题:现有一段长度为n的钢条和上面的价格表,求切割钢条方案,使得总收益最大

思考:长度为n的钢条的不同切割方案有几种

image

image

image

p = [0,1,5,8,9,10,17,17,20,24,30]
# 递归方法
def cut_rod_recurision_1(p,n):
    if n==0:
        return 0
    else:
        res = p[n]
        for i in range(1,n):
            res = max(res,cut_rod_recurision_1(p,i)+cut_rod_recurision_1(p,n-i))
        return res  

# 自顶向下实现
def cut_rod_recurision_2(p,n):
    if n == 0:
        return 0
    else:
        res = 0
        for i in range(1,n+1):
            res = max(res,p[i]+cut_rod_recurision_2(p,n-i))
        return res  

image

# 自底向上实现
def cur_rod_dp(p,n):
    r = [0]
    for i in range(1,n+1):
		q = 0
        for j in range(1,i+1):
            q = max(q,p[j]+r[i-j])
        r.append(q)
    return r[n]   

# 最优解+最优切割方案
def cur_rod_extend(p,n):
    r = [0]
    s = [0]
    for i in range(1,n+1):
        res_r = 0	# 价格的最大值
        res_s = 0 	# 价格最大值对应方案的左边不切割部分的长度
        for j in range(1,i+1):
            if p[j] + r[i-j] > res_r:
                res_r = p[j]+r[i-j]
                res_s = j
        r.append(res_r)
        s.append(res_s)
    return r[n],s
def cut_rod_solution(p,n):
    r,s = cur_rod_extend(p,n)
    ans = []
    while n > 0:
        ans.append(s[n])
        n -= s[n]
    return ans    

时间复杂度O(n²)

image

最长公共子序列

image

image

image

image

# 动态规划法
def lcs_length(x,y):
    m = len(x)
   	n = len(y)
    
    c = [[0 for _ in range(n+1)] for _ in range(m+1)]
    for i in range(1,m+1):
        for j in range(1,n+1):
            if x[i-1] == y[j-1]:	# i j 位置上的字符匹配的时候,来自于左上方+1
            	c[i][j] = c[i-1][j-1]+1
            else:
                c[i][j] = max(c[i-1][j],c[i][j-1])
    return c[m][n]

# 回溯法
def lcs(x,y):
    m = len(x)
   	n = len(y)
    
    c = [[0 for _ in range(n+1)] for _ in range(m+1)]
    b = [[0 for _ in range(n+1)] for _ in range(m+1)]
    for i in range(1,m+1):
        for j in range(1,n+1):
            if x[i-1] == y[j-1]:	# i j 位置上的字符匹配的时候,来自于左上方+1
            	c[i][j] = c[i-1][j-1]+1
                b[i][j] = 1
            elif c[i-1][j] > c[i][j-1]: # 来自上方
                c[i][j] = c[i-1][j]
                b[i][j] = 2
            else:	# 来自左方
                c[i][j] = c[i][j-1]
                b[i][j] = 3
    return c[m][n],b
def lcs_trackback(x,y):
    c,b = lcs(x,y)
    i = len(x)
    j = len(y)
    res = []
    while i>0 and j>0:
        if b[i][j] == 1:	# 来自左上方
            res.append(x[i-1])
            i -= 1
            j -= 1
        elif b[i][j] == 2:	# 来自上方
            i -= 1
        else:
            j -= 1
    
	return "".join(reversed(res))           

欧几里得算法

最大公约数

gcd(a,b) = gcd(b,a mod b)

def gcd(a,b):
	if b == 0:
        return a
    else:
        return gcd(b,a % b)
def gcd2(a,b):
    while b>0:
        r = a % b
        a = b 
        b = r
    return a

实现分数计算

class Fraction:
	def __init__(self,a,b):
		self.a=a
        self.b=b
        x = self.gcd(a,b)
        self.a /= x
        self.b /= x

    def gcd(a,b):
        while b > 0:
            r = a % b
			a = b 
            b = r
        return a
    
    def __str__(self):
        retunr "%d%d" %(%self.a,self.b)
        
    def zxgs(a,b):	# 最小公倍数
       	x = self.gcd(a,b)
    	return a*b/x
        
    
    def __add__(self,other):
        a = self.a
        b = self.b
        c = other.a
        d = other.b
        fenmu = self.zxgs(b,d)
        fenzi = a * fenmu / b + c * fenmu / d
        return Fraction(fenzi,fenmu)

RSA算法

现代密码系统:加密算法是公开的,密钥是秘密的

  1. 对称加密
  2. 非对称加密

image

过程

image

image

posted @ 2022-03-18 16:14  注入灵魂  阅读(72)  评论(0)    收藏  举报