4.Python数据结构与算法分析课后习题(第二版)__chapter4

chpater4_answer


参考链接:
https://blog.csdn.net/Morbidmuse/article/details/119641686 link
https://blog.csdn.net/fhlsyol/article/details/106439254 link
zhuanlan.zhihu.com/p/80682302 link

一、讨论题

略略略。

二、编程练习

1.写一个递归函数来计算数的阶乘。

def Factorial(n):
    if n == 1:
        return 1
    else:
        return n * Factorial(n-1)

if __name__ == '__main__':
    print(Factorial(3))

2.写一个递归函数来反转列表。

def reverse(alist):
    if len(alist) == 1:
        return alist[0]
    elif len(alist) == 2:
        alist[0], alist[1] = alist[1], alist[0]
        return alist
    else:
        return [alist[-1]] + reverse(alist[:-1])

if __name__ == '__main__':
    testlist = [1, 2, 3, 4, 5]
    print(reverse(testlist))   

3.采用下列一个或全部方法修改递归树程序。

  • 修改树枝的粗细程度,使得branchLen越小,线条越细。
  • 修改树枝的颜色,使得当branchLen非常小时,树枝看上去像叶子。
  • 修改小乌龟的转向角度,使得每一个分支的角度都是一定范围内的随机值,例如使角度取值范围是15~45度。运行程序,查看绘制结果。
  • 递归地修改branchLen,使其减去一定范围内地随机值,而不是固定值。
    如果实现上述所有改进方法,绘制出地树将十分真实。
    结果如图:请添加图片描述
    (有点小丑,说好地十分真实呢……)
from turtle import *
import random

t = Turtle()
tWin = t.getscreen()

def tree(branchLen, t):
    t.speed(100)

    if branchLen> 5:
        t.width(50 * branchLen / 110)
        if branchLen > 10:
            t.color('black')
        else:
            t.color('green')
        t.forward(branchLen)
        t.right(20)
        tree(branchLen-random.randrange(10, 21), t)
        t.left(40)
        tree(branchLen-random.randrange(5, 16), t)
        t.right(20)
        t.backward(branchLen)

if __name__ == '__main__':

    t.left(90)
    t.up()
    t.backward(300)
    t.down()
    t.color('green')
    tree(110, t)
    tWin.exitonclick()

4.找到一种绘制分形山的算法。提示:可以使用三角形。

参考:https://blog.csdn.net/Morbidmuse/article/details/119641686

from turtle import *
import random

def drawTriangle(points, color, myTurtle):
    myTurtle.color(color)
    myTurtle.up()
    myTurtle.goto(points[0])
    myTurtle.down()
    myTurtle.begin_fill()
    myTurtle.goto(points[1])
    myTurtle.goto(points[2])
    myTurtle.goto(points[0])
    myTurtle.end_fill()


def mountain(t, start_x, start_y, end_x, end_y, depth, color):
    y_offset = random.randint(10, 30)
    mid_x = (start_x + end_x) / 2
    mid_y = (start_y + end_y) / 2 + y_offset
    drawTriangle([(start_x, start_y),
                   (end_x, end_y),
                   (mid_x, mid_y)], color, t)
    if depth > 0:
        mountain(t, start_x, start_y, mid_x, mid_y, depth - 1, color)
        mountain(t, mid_x, mid_y, end_x, end_y, depth - 1, color)

if __name__ == '__main__':
    myTurtle = Turtle()
    myTurtle.speed(20)
    myWin = myTurtle.getscreen()
    x1, y1, x2, y2 = -400, -50, 400, -50
    colors = ['#DAE0DF', '#959998', '#505453', '#070707']
    for i in range(4):
        mountain(myTurtle, x1, y1 - i * 30, x2, y2 - i * 30, 6, colors[i])
    myWin.exitonclick()

5.写一个递归函数来计算斐波那契数列,并对比递归函数与循环函数的性能。

import datetime

def Fibonacci_recursion(n):

    if n == 1 or n == 2:
        return 1
    else:
        return Fibonacci_recursion(n - 1) + Fibonacci_recursion(n - 2)

def Fibonacci_cir(n):
    n1, n2 = 1, 1
    for i in range(n - 1):
        n1, n2 = n2, n1 + n2
    return n1

if __name__ == '__main__':
    t1 = datetime.datetime.now()
    print(Fibonacci_recursion(35))
    t2 = datetime.datetime.now()
    print('Fibonacci_recursion need time:', t2 - t1)
    t3 = datetime.datetime.now()
    print(Fibonacci_cir(35))
    t4 = datetime.datetime.now()
    print('Fibonacci_circulate need time:', t4 - t3)

结果如图:
在这里插入图片描述
由结果知:能够用递归解决问题不代表递归就是最优解。

6.实现汉诺塔问题的一个解决方案,使用3个栈来记录盘子位置。

  代码有点臃肿,因为我想把每一步的栈也就是盘子的位置打印出来,这样看起来更直观。
结果如图:
在这里插入图片描述

from chapter3.stack import Stack

left = Stack()
mid = Stack()
right = Stack()
HEIGHT = 5
i = HEIGHT
while i > 0:
    left.push(i)
    i -= 1
print("Initial: height = %d" % HEIGHT)
print('left', left.items)
print('mid', mid.items)
print('right', right.items)

def moveTower(height, fromPole, toPole, withPole):
    if height >= 1:
        moveTower(height-1, fromPole, withPole, toPole)
        moveDisk(fromPole, toPole)
        moveTower(height-1, withPole, toPole, fromPole)

def moveDisk(fp, tp):
    print("-----------------------------")
    print("moving disk from", fp, "to", tp)
    if fp == 'p1' and tp == 'p2':
        if not left.isEmpty():
            mid.push(left.pop())
        print('left', left.items)
        print('mid', mid.items)
        print('right', right.items)
    elif fp == 'p1' and tp == 'p3':
        if not left.isEmpty():
            right.push(left.pop())
        print('left', left.items)
        print('mid', mid.items)
        print('right', right.items)
    elif fp == 'p2' and tp == 'p1':
        if not mid.isEmpty():
            left.push(mid.pop())
        print('left', left.items)
        print('mid',mid.items)
        print('right',right.items)
    elif fp == 'p2' and tp == 'p3':
        if not mid.isEmpty():
            right.push(mid.pop())
        print('left', left.items)
        print('mid',mid.items)
        print('right',right.items)
    elif fp == 'p3' and tp == 'p1':
        if not right.isEmpty():
            left.push(mid.pop())
        print('left', left.items)
        print('mid',right.items)
        print('right',right.items)
    else:
        if not right.isEmpty():
            mid.push(right.pop())
        print('left', left.items)
        print('mid',mid.items)
        print('right',right.items)

if __name__ == '__main__':

    moveTower(HEIGHT, 'p1', 'p3', 'p2')

7.使用turtle绘图模块写一个递归程序,画出希尔伯特曲线 。

结果如图:
在这里插入图片描述

from turtle import *

t = Turtle()
tWin = t.getscreen()

def hilbert(level, angle, step):
    if level == 0:
        pass
    else:
        t.right(angle)
        hilbert(level - 1, -angle, step)
        t.forward(step)
        t.left(angle)
        hilbert(level - 1, angle, step)
        t.forward(step)
        hilbert(level - 1, angle, step)
        t.left(angle)
        t.forward(step)
        hilbert(level - 1, -angle, step)
        t.right(angle)

if __name__ == '__main__':
    t.up()
    t.backward(300)
    t.left(90)
    t.backward(300)
    t.down()
    t.speed(50)
    hilbert(5, 90, 10)
    tWin.exitonclick()

8.使用turtle绘图模块写一个递归程序,画出科赫雪花。

结果如图:
在这里插入图片描述

from turtle import *

t = Turtle()
tWin = t.getscreen()

def koch(size, level):
    if level == 0:
        t.fd(size)
    else:
        for i in [0, 60, -120, 60]:
            t.left(i)
            koch(size/3, level-1)

if __name__ == '__main__':
    t.speed(50)
    level = 4
    t.penup()
    t.goto(-250, 150)
    t.pensize(2)
    t.pendown()
    koch(500, level)
    t.right(120)
    koch(500, level)
    t.right(120)
    koch(500, level)
    t.right(120)
    tWin.exitonclick()

9.写一个程序来解决这样一个问题:有2个坛子,其中一个的容量是4加仑,另一个的是3加仑。坛子上都没有刻度线。可以用水泵将它们装满水。如何使用4加仑的坛子最后装有2加仑的水?

&

10.扩展练习9中的程序,将坛子的容量和较大的坛子中最后的水量作为参数。

结果如图:
在这里插入图片描述

from chapter3.stack import Stack
s1 = Stack()
s2 = Stack()


def solution(a, b, c):
    for i in range(b):
        s2.push('water')
    print('----------------------------------------------------')
    print('Initial')
    print('s1:', s1.items)
    print('s2:', s2.items)
    for i in range(a):
        s1.push(s2.pop())
    print('----------------------------------------------------')
    print('from s2 to s1:')
    print('s1:', s1.items)
    print('s2:', s2.items)
    if not s2.size() == c:
        for i in range(a):
            s1.pop()
        print('----------------------------------------------------')
        print('drop s1:')
        print('s1:', s1.items)
        print('s2:', s2.items)
        s1.push(s2.pop())
        return solution(2 * a - b, b, c)
    else:
        print('----------------------------------------------------')
        return 'comleted'
# a,b,c=small,big,target
print(solution(3, 4, 2))

11.写一个程序来解决这样一个问题:3个羚羊和3只狮子准备乘船过河,河边有一艘能容纳2只动物的小船。但是,如果两侧河岸上的狮子数量大于羚羊数量,羚羊就会被吃掉。找到运送办法,使得所有动物都能安全渡河。

参考链接:https://blog.csdn.net/fhlsyol/article/details/106439254link(ps:这个代码的输出结果含义是小船每次一来一回载的动物)

  搜了一下过河问题大多都是用图解决,还没复习到,先插个眼回头看。

from collections import Counter
from random import sample, randint


class Solution:
    """解决问题的方案类"""

    def __init__(self):
        """初始化属性"""
        self.left = ['羚羊'] * 3 + ['狮子'] * 3  # 后面用到了Counter,所以这里可以用字符串表示,不用0,1表示,更直观一点
        self.left_checkpoint = []  # 左边的存档,用于试错后恢复
        self.right = []
        self.right_checkpoint = []
        self.result = [[]]  # 结果,给个初始值是为了避免out of index的情况,取结果的时候切片即可
        self.result_checkpoint = []
        self.r_direction = True  # True为右,False为左

    def go(self):
        """渡河"""
        if self.r_direction:  # 向右渡河
            boat = sample(self.left, 2)
            for i in boat:
                self.left.remove(i)
                self.right.append(i)
        else:  # 向左渡河
            if len(self.right) > 1:  # 这里判断是为了避免sample取的时候越界(从1个里面取2个)
                boat = sample(self.right, randint(1, 2))
            else:
                boat = sample(self.right, 1)
            for i in boat:
                self.right.remove(i)
                self.left.append(i)
        return boat

    def judge(self):
        """判断"""
        if self.left and self.right:
            left_counter = Counter(self.left)
            right_counter = Counter(self.right)
            if (left_counter['羚羊'] and left_counter['羚羊'] < left_counter['狮子']) or \
                    (right_counter['羚羊'] and right_counter['羚羊'] < right_counter['狮子']):
                return False
        return True

    def checkpoint(self):
        """检查点"""
        self.left_checkpoint, self.right_checkpoint, self.result_checkpoint = \
            self.left.copy(), self.right.copy(), self.result.copy()

    def reset(self):
        """读档"""
        self.left, self.right, self.result = \
            self.left_checkpoint.copy(), self.right_checkpoint.copy(), self.result_checkpoint.copy()

    def get_result(self):
        """模拟渡河过程,获取结果"""
        while len(self.right) < 6:
            self.checkpoint()  # 存档
            boat = self.go()  # 渡河
            boat.sort()
            if self.judge() and boat != self.result[-1]:  # 这里判断是为了避免相同的人来回的情况,以求尽可能少的解
                self.r_direction = not self.r_direction  # 调转船头
                self.result.append(boat)
            else:
                self.reset()  # 读档
        return self.result[1:]


def main():
    """主函数"""

    repeat = 10000  # 重复执行次数
    result_set = set()  # 解的集合
    solution = Solution()

    for _ in range(repeat):
        result = solution.get_result()
        result_set.add(str(result))
        solution.__init__()

    print(f'经{repeat}次执行,共得到{len(result_set)}种不同的结果,结果如下:', end='\n\n')
    for result in result_set:

        print(result)


if __name__ == '__main__':
    main()


12.利用turtle绘图模块修改汉诺塔程序,将盘子的移动过程可视化。提示:可以创建多只小乌龟,并将它们的形状改为长方形。

结果如下:
在这里插入图片描述

from turtle import *

t = Turtle()
global t1, t2, t3
height = 5
color = ["red", "orange", "yellow","green", "blue","purple", "pink",  "black",  'gray']
# 圆盘
class Disc(Turtle):
    def __init__(self, n):
        Turtle.__init__(self, shape="square", visible=False)
        self.pu()
        # 矩形大小
        self.shapesize(1.5, n*1.5, 2) # square-->rectangle
        # 设置颜色
        # self.fillcolor(n / height, 0, 1 - n / height)
        self.fillcolor(color[n-1])
        self.st()
        self.speed(5)


class Tower(list):
    "Hanoi tower, a subclass of built-in type list"
    def __init__(self, x):
        "create an empty tower. x is x-position of peg"
        self.x = x
    # 加入盘子
    def push(self, d):
        d.setx(self.x)
        d.sety(-150+34*len(self))
        self.append(d)
    # 取出盘子
    def pop(self):
        d = list.pop(self)
        d.sety(150)
        return d


def moveTower(height, fromPole, toPole, withPole):
    if height >= 1:
        moveTower(height-1, fromPole, withPole, toPole)
        toPole.push(fromPole.pop())
        moveTower(height-1, withPole, toPole, fromPole)

def play():
    onkey(None,"space")
    clear()
    try:
        moveTower(height, t1, t3, t2)
        write("press STOP button to exit",
              align="center", font=("Courier", 16, "bold"))
    except Terminator:
        pass  # turtledemo user pressed STOP



def DrawPoles():
    '''画三个柱子'''
    t.speed(0)
    t.up()
    t.left(90)
    t.pensize(10)
    DrawOnePole(-250, -200)
    DrawOnePole(0, -200)
    DrawOnePole(250, -200)

def DrawOnePole(x, y):
    '''画一个柱子'''
    t.goto(x, y)
    t.down()
    t.fd(300)
    t.up()

if __name__ == '__main__':
    t1 = Tower(-250)
    t2 = Tower(0)
    t3 = Tower(250)

    ht()
    penup()
    goto(0, -225)

    DrawPoles()

    for i in range(height,0,-1):
        t1.push(Disc(i))
    # prepare spartanic user interface ;-)
    write("press spacebar to start game",
          align="center", font=("Courier", 16, "bold"))
    onkey(play, "space")
    listen()
    mainloop()

13. 打印帕斯卡三角形(杨辉三角)。

  帕斯卡三角形由数字组成,其中的数字交错摆放,使得
a n r = n ! r ! ( n − r ) ! a_{nr}=\frac{n!}{r!(n-r)!} anr=r!(nr)!n!
  这是计算二项式系数的等式。在帕斯卡三角形中,每个数等于其上方两数之和,如下所示。
在这里插入图片描述
  将行数作为参数,写一个输出帕斯卡三角形的程序。

def Pascal_triangle(input_num):
    list = []  # an empty list
    for n in range(input_num):
        list.append([])
        list[n].append(1)
        for m in range(1, n):
            list[n].append(list[n - 1][m - 1] + list[n - 1][m])
        list[n].append(1)

    for n in range(input_num):
        print("   " * (input_num - n), end=" ", sep=" ")
        for m in range(0, n + 1):
            print('{0:5}'.format(list[n][m]), end=" ", sep=" ")
        print( )

if __name__ == '__main__':
    height = 5
    Pascal_triangle(height)

14.假设一个计算机科学家兼艺术大盗闯入美术馆。他只能用一个容量为W磅的背包来装盗取的艺术品,并且他对每一件艺术品的价值和重量了如指掌。他会如何写一个动态规划程序来帮助自己最大程度地获利呢?下面的例子可以帮助你思考:假设背包容量是20磅,艺术品为5件。

艺术品重量价值
123
234
348
458
5910
def dpMaxValue(objectsValueDic, weights, maxValue, objectsUsed):
    for weight in range(weights + 1):
        nowValue = objectsValueDic[0]
        newWeight = 0
        for j in [c for c in objectsValueDic.keys() if c <= weight]:
            if maxValue[weight - j] + objectsValueDic[j] > nowValue:
                nowValue = maxValue[weight - j] + objectsValueDic[j]
                newWeight = j
        maxValue[weight] = nowValue
        objectsUsed[weight] = newWeight

    return maxValue[weights]

def printWeight(objectsUsed, weights):
    weight = weights
    out = []
    while weight > 0:
        thisWeight = objectsUsed[weight]
        if thisWeight == 0:
            break
        out.append(thisWeight)
        weight -= thisWeight
    print('weights:',out)

if __name__ == '__main__':
    objectsValueDic = {0 : 0, 2 : 3, 3 : 4, 4 : 8, 5 : 8, 9 : 10}
    weights = 20
    objectsUsed = [0] * (weights+1)
    print('value :',dpMaxValue(objectsValueDic, weights, [0] * (weights+1), objectsUsed))
    printWeight(objectsUsed, weights)

15.请尝试解决字符串编辑距离问题,它在很多研究领域中非常有用。假设要把单词algorithm转换成alligator。对于每一个字母,可以用5个单位的代价将其从一个单词复制到另一个,也可以用20个单位的总代价将其删除或插入。拼写检查程序利用将一个单词转换为另一个的总代价来提供拼写建议。请设计一个动态规划算法,给出任意两个单词之间的最小编辑距离。

参考链接:https://zhuanlan.zhihu.com/p/80682302link
  写的非常好~👍
  btw,本题中用20个单位的总代价将其删除或插入可以理解为(存疑):将alligator复制到algorithm为algorithmalligator,从中再删除algorithm变成alligator共走了20step,那么用5个单位的代价将其从一个单词复制到另一个怎么理解???????😥有没有小伙伴探讨一下

def minDistance(s1, s2):

    memo = dict()
    def dp(i, j):
        if (i, j) in memo:
            return memo[(i, j)]
        # base case
        if i == -1: return j + 1
        if j == -1: return i + 1

        if s1[i] == s2[j]:
            memo[(i, j)] = dp(i - 1, j - 1)  # 啥都不做
        else:
            memo[(i, j)] = min(
                dp(i, j - 1) + 1,    # 插入
                dp(i - 1, j) + 1,    # 删除
                dp(i - 1, j - 1) + 1 # 替换
            )
        return memo[(i, j)]


    # i,j 初始化指向最后一个索引
    return dp(len(s1) - 1, len(s2) - 1)

if __name__ == '__main__':
    s1 = 'algorithm'
    s2 = 'alligator'
    print(minDistance(s1, s2))

三、总结

主要学习了递归(!=最优解),turtle绘图模块,动态规划等。

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值