Python数据结构与算法分析课后习题(第二版) 第四章 课后练习

"""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)  # 画笔大小(50)
        if branchLen > 10:    # 大小〉10 
            t.color('black')  # 画笔颜色为black
        else:  # 反之
            t.color('green')# 画笔颜色为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)  # 左转90度
    t.up()  # 抬笔
    t.backward(300)  # 后退300的距离
    t.down()  # 落笔
    t.color('green')  # 定义画笔颜色
    tree(110, t)  # 调用函数
    tWin.exitonclick()  # 退出
"""4.找到一种绘制分形山的算法。提示:可以使用三角形"""
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):  # 画山(对象,起笔的xy坐标,结束的xy坐标,分裂几个三角形,颜色)
    y_offset = random.randint(10, 30)  # 山峰高度
    mid_x = (start_x + end_x) / 2  # 三角形顶部的x坐标
    mid_y = (start_y + end_y) / 2 + y_offset  # 三角形顶部的y坐标
    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()  # 速度
    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, 1, 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(30))  # 调用函数
    t2 = datetime.datetime.now()  # 结束时间
    print('递归算法用时:', t2 - t1)

    t3 = datetime.datetime.now()
    print(Fibonacci_cir(30))
    t4 = datetime.datetime.now()
    print('递推算法用时:', t4 - t3)
"""6.实现汉诺塔问题的一个解决方案,使用3个栈来记录盘子位置"""
class Stack:  # 一个类就是一个栈
    def __init__(self):  # 设置一个空栈
        self.items = []  # 需要items参数才能添加

    def isEmpty(self):  # 定义检查栈的方法(它只返回布尔值)
        return self.items == []

    def push(self, item):  # 定义给栈添加元素的方法
        self.items.append(item)  # 需要item参数才能添加

    def pop(self):  # 定义给栈删除元素的方法
        return self.items.pop()

    def peek(self):  # 返回栈顶端元素的方法
        return self.items[len(self.items)-1]

    def size(self):  # 返回栈元素个数的方法
        return len(self.items)


left = Stack()  # 左柱
mid = Stack()  # 中柱
right = Stack()  # 右柱
HEIGHT = 3  # 盘子数量
i = HEIGHT  # 记录盘子数量

while i > 0:  # 盘数大于0就无限循环
    left.push(i)  # 盘子入栈
    i -= 1  # 盘数-1

print("盘子数量 = %d" % HEIGHT)
print('left', left.items)  # 打印左栈
print('mid', mid.items)  # 打印中栈
print('right', right.items)  # 打印右栈


def moveTower(height, fromPole, toPole, withPole):  # 定义函数(三个按照当前的循序摆放的柱子)
    if height >= 1:  # 盘数>= 1(递归出口
        moveTower(height-1, fromPole, withPole, toPole)  # 更改循序调用自己
        moveDisk(fromPole, toPole)  # 调用显示移动函数
        moveTower(height-1, withPole, toPole, fromPole)  # 更改循序调用自己


def moveDisk(fp, tp):  # 显示移动函数(x柱,x柱)
    print("-----------------------------")
    print("正在移动磁盘从", fp, "到", tp)
    if fp == '左柱' and tp == '中柱':
        if not left.isEmpty():  # 当fp对象的栈不为空的时候
            mid.push(left.pop())  # 删除最后一个放入tp对象的栈
        print('left', left.items)  
        print('mid', mid.items)  # 输出三个栈
        print('right', right.items)

    elif fp == '左柱' and tp == '右柱':
        if not left.isEmpty():
            right.push(left.pop())
        print('left', left.items)
        print('mid', mid.items)
        print('right', right.items)

    elif fp == '中柱' and tp == '左柱':
        if not mid.isEmpty():
            left.push(mid.pop())
        print('left', left.items)
        print('mid', mid.items)
        print('right', right.items)

    elif fp == '中柱' and tp == '右柱':
        if not mid.isEmpty():
            right.push(mid.pop())
        print('left', left.items)
        print('mid', mid.items)
        print('right', right.items)

    elif fp == '右柱' and tp == '左柱':
        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, '左柱', '右柱', '中柱')
"""7.使用turtle绘图模块写一个递归程序,画出希尔伯特曲线 """

from turtle import *  # 导入绘图库

t = Turtle()  # 创建对象
tWin = t.getscreen()  # 获取屏幕

def hilbert(level, angle, step):  # 定义函数(阶数,角度,长度)
    if level == 0:
        pass  # 不执行
    else:  # 反之
        t.right(angle)  # 右转90
        hilbert(level - 1, -angle, step)
        t.forward(step)  # 向前step步
        t.left(angle)  # 左转90
        hilbert(level - 1, angle, step)
        t.forward(step)  # 向前step步
        hilbert(level - 1, angle, step)
        t.left(angle)  # 左转90
        t.forward(step)  # 向前step步
        hilbert(level - 1, -angle, step)
        t.right(angle)  # 右转90


if __name__ == '__main__':
    t.up()  # 抬笔
    t.backward(200)  # 退回200步
    t.left(90)  # 左转90度
    t.backward(200)  # 退回200步

    t.down()  # 落笔
    t.speed(10)  # 速度
    hilbert(2, 90, 20)  # 调用函数
    tWin.exitonclick()  # 退出屏幕
"""8.使用turtle绘图模块写一个递归程序,画出科赫雪花。"""

# 科赫雪花
# 1、任意画一个正三角形,并把每一边三等分;
# 2、取三等分后的一边中间一段为边向外作正三角形,并把这“中间一段”擦掉;
# 3、重复上述两步,画出更小的三角形。
# 4、一直重复,直到无穷,所画出的曲线叫做科赫曲线

import turtle as t  # 导入库
def koch(len, n):  # 定义函数(长度,阶)
    if n == 0:  # 出口
        t.fd(len)  # 画长度
    else:
        for i in [0, 60, -120, 60]:  # 画三角的角度
            t.left(i)  # 循环调用角度
            koch(len/3, n-1)  # 递归(1/3长度,阶层减一)
# level = int(input('输入阶数:'''))
def main():  # 定义函数
    t.penup()  # 抬笔
    t.goto(-250, 150)  # 移动到坐标
    t.pensize(2)  # 笔宽
    t.color('orange')  # 笔色
    t.pendown()  #落笔
    koch(500, 3)  # 调用函数
    t.right(120)  # 右转角度
    koch(500, 3)  # 调用函数
    t.right(120)  # 右转角度
    koch(500, 3)  # 调用函数
    t.right(120)  # 右转角度
    #t.hideturtle()  # 隐藏海龟
    t.done()  # 保存画面
main()  # 调用函数
"""9.写一个程序来解决这样一个问题:有2个坛子,其中一个的容量是4加仑,另一个的是3加仑
。坛子上都没有刻度线。可以用水泵将它们装满水。
如何使用4加仑的坛子最后装有2加仑的水?"""
# 10.扩展练习9中的程序,将坛子的容量和较大的坛子中最后的水量作为参数。


class Stack:  # 一个类就是一个栈
    def __init__(self):  # 设置一个空栈
        self.items = []  # 需要items参数才能添加

    def isEmpty(self):  # 定义检查栈的方法(它只返回布尔值)
        return self.items == []

    def push(self, item):  # 定义给栈添加元素的方法
        self.items.append(item)  # 需要item参数才能添加

    def pop(self):  # 定义给栈删除元素的方法
        return self.items.pop()

    def peek(self):  # 返回栈顶端元素的方法
        return self.items[len(self.items)-1]

    def size(self):  # 返回栈元素个数的方法
        return len(self.items)


s1 = Stack()  # 创建对象3升桶
s2 = Stack()  # 创建对象4升桶


def solution(a, b, c):  # 定义函数(3升桶,4升桶,要装几升)
    for i in range(b):
        s2.push('water')  # 把4升桶装满
    print('----------------------------------------------------')
    print('Initial')
    print('3升桶:', s1.items)
    print('4升桶:', s2.items)

    for i in range(a):  # 把3升桶装满
        s1.push(s2.pop())  # 把4升桶倒入3升桶
    print('----------------------------------------------------')
    print('把 4升桶 的水倒入 3升桶:')
    print('3升桶:', s1.items)
    print('4升桶:', s2.items)

    if not s2.size() == c:  # 4升桶不等于2升水(递归出口)
        for i in range(a):  # 把3升桶的水倒掉
            s1.pop()
        print('----------------------------------------------------')
        print('drop 3升桶:')
        print('3升桶:', s1.items)
        print('4升桶:', s2.items)

        s1.push(s2.pop())  # 把4升桶倒入3升桶
        return solution(2 * a - b, b, c)  # 递归
    else:
        print('----------------------------------------------------')
        return 'comleted'
        
print(solution(3, 4, 2))
"""11.写一个程序来解决这样一个问题:3个羚羊和3只狮子准备乘船过河
,河边有一艘能容纳2只动物的小船。
但是,如果两侧河岸上的狮子数量大于羚羊数量,羚羊就会被吃掉
。找到运送办法,使得所有动物都能安全渡河。"""

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:  # 右边的个数小于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 = 165  # 重复执行次数
    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 *
speed(1000)
t = Turtle()
global t1, t2, t3
height = 4
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) # 定义圆盘尺寸
        # 设置颜色
        # self.fillcolor(n / height, 0, 1 - n / height)
        self.fillcolor(color[n-1])# 定义颜色
        self.st()  # 显示箭头形状
        # self.speed(5)


class Tower(list):# 装列表的容器
    def __init__(self, x):  # 定义属性
        self.x = x
    # 取出盘子
    def pop(self):
        d = list.pop(self)  # 删除
        d.sety(150)  # 定义y坐标
        return d  # 返回方块

    # 加入盘子
    def push(self, d):
        d.setx(self.x)# 定义x坐标
        d.sety(-150+34*len(self))  # 定义y坐标
        self.append(d)  # 添加到列表

def moveTower(height, fromPole, toPole, withPole):  # 定义递归传入函数和坐标
    if height >= 1:  # 如果高度大于或等于1
        moveTower(height-1, fromPole, withPole, toPole)  # 继续递归
        toPole.push(fromPole.pop())  # 把第1个的最后一个删除然后放在第3个的最后
        moveTower(height-1, withPole, toPole, fromPole) # 继续递归

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()

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))# 在a柱处取出列表中的每个圆盘在画出
print('aaa')
moveTower(height, t1, t3, t2)  # 调用递归
mainloop() # 保存画面
"""13. 打印帕斯卡三角形(杨辉三角)"""

def Pascal_triangle(input_num):  # 定义函数(高度)
    list = []  # 二维列表
    for n in range(input_num):  # 有多高循环多少次(行)
        list.append([])  # 在list嵌套一个空列表
        list[n].append(1)  # 在每个空列表里尾部添加1
        for m in range(1, n):  # (列)
            list[n].append(list[n - 1][m - 1] + list[n - 1][m])  #  在上一行递推向前加然后把结果添加的当前行
        list[n].append(1)  # 在每个空列表里尾部添加1(就对称了)

    for n in range(input_num):  # 遍历二维列表(行)
        print("   " * (input_num - n), end=" ")  # (前面空多少格,不换行)
        for m in range(0, n + 1):  # (遍历当前行的每一列)
            print('{0:5}'.format(list[n][m]), end=" ")  # (打印出当前行的值,不换行)
        print()  # 换行

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

def fun(n, bag_weight, w, v):
    '''
    :param n: #物品数量
    :param bag_weight:  背包重量
    :param w: 每个物品重量
    :param v: 每个物品价值
    :return:
    '''

    # 初始化,先全部赋值为0,这样至少体积为0或者不选任何物品的时候是满足要求
    value = [[0 for j in range(bag_weight + 1)] for i in range(n + 1)]

    for i in range(1, n + 1):
        for j in range(1, bag_weight + 1):
            value[i][j] = value[i - 1][j]  # 第i个物品不选
            if j >= w[i-1]:  # 判断剩余背包容量是不是大于第i件物品的体积
                value[i][j] = max(value[i - 1][j], value[i - 1]
                                  [j - w[i - 1]] + v[i - 1])
    # for i in value:
    #     print(i)

    return value


def show(n, bag_weight, w, value):
    print('背包最大价值为:', value[n][bag_weight])
    x = [False for i in range(n)]
    j = bag_weight
    # 从尾遍历物品,当value大于上一行同样位置的value时,表示放进该物品
    for i in range(n, 0, -1):
        if value[i][j] > value[i - 1][j]:
            x[i - 1] = True
            j -= w[i - 1]
    print('背包中所装物品为:')
    for i in range(n):
        if x[i]:
            print('第', i + 1, '个', end='\t')


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

def stringeditdistance(a, b):  # 定义函数
    lena, lenb = len(a) + 1, len(b) + 1  # 定义两个字符串长度
    #二位数组应当如此声明
    dp = [[0] * lenb for _ in range(lena)]  # 定义全部为0的二维数组    
    for a in dp:  # 打印二维数组
        print(a)
    for i in range(lena):  # 在行里面循环
        dp[i][0] = i  # 每行填入值
    for i in range(lenb):  # 在列里面循环
        dp[0][i] = i  # 每列填入值

    # for a in dp:  # 打印二维数组
    #     print(a)

    for i in range(1, lena):  # 行
        for j in range(1, lenb):  # 列
            if a[i - 1] == b[j - 1]:  # 如果a串的第0个==如果b串的第0个
                dp[i][j] = dp[i - 1][j - 1]  # 就把第0行第0列的值赋值到第1行第1列(变换距离为0)
            else:  # 反之(取最小值+1)
                dp[i][j] = min(dp[i - 1][j], min(dp[i - 1] #(上,(左上,左)) 
                               [j - 1], dp[i][j - 1])) + 1
    for a in dp:  # 打印二维数组
        print(a)
    return dp[lena - 1][lenb - 1]  # 返回二维数组最后一个值


def stringeditdistancetest():
    print(stringeditdistance('ac', 'ag'))


stringeditdistancetest()

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值