数据结构与算法 Python语言实现《Data Structures & Algorithms in Python》- 第一章课后答案

R-1.1 编写一个Python 函数 is multiple(n, m),用来接收两个整数值 n 和 m,如果 n 是 m 的倍数,即存在整数 i 使得n = mi,那么函数返回 True,否则返回 False。

提示:The modulo operator could be useful here.

def is_multiple(n, m):
    return n % m == 0

n1, m1 = 10, 2
print(is_multiple(n1, m1))  # True

n1, m1 = 10, 3
print(is_multiple(n1, m1))  # False

R-1.2 编写一个 Python 函数 is_even(k),用来接收一个整数 k,如果 k 是偶数返回 True,否则返回 False。但是,函数中不能使用乘法、除法或取余操作。

提示:Use bit operations.

def is_even(k):
    return bin(k)[-1] ==  '0'

print(is_even(2))  # True
print(is_even(3))  # False

R-1.3 编写一个 Python 函数 minmax(data),用来在数的序列中找出最小数和最大数,并以一个长度为 2 的元组的形式返回。注意:不能通过内置函数 min 和 max 来实现。

提示:Keep track of the smallest and largest value while looping.

def minmax(data):
    _max = _min = data[0]
    for i in data:
        if i > _max:
            _max = i
        if i < _min:
            _min = i
    return _min, _max

arr_1_3 = [1, 10, 3, 5, 7]
print(minmax(arr_1_3))  # (1, 10)

R-1.4 编写一个 Python 函数,用来接收正整数 n,返回 1 ~ n 的平方和。

R-1.5 基于 Python 的解析语法和内置函数 sum,写一个单独的命令来计算练习 R-1.4 中的和。

提示:How can you describe the range of integers for the sum?

def sum_squares(n):
    return sum(i ** 2 for i in range(0, n + 1))

print(sum_squares(3))  # 14
print(sum_squares(10))  # 385

R-1.6 编写一个 Python 函数,用来接收正整数 n,并返回 1 ~ n 中所有的奇数的平方和。

R-1.7 基于Python的解析语法和内置函数 sum,写一个单独的命令来计算练习 R-1.6 中的和。

提示:How can you describe the range of integers for the sum?

def sum_odd(n):
    return sum(i ** 2 for i in range(0, n + 1, 2))

print(sum_odd(3))  # 10
print(sum_odd(10))  # 165

R-1.8 Python 允许负整数作为序列的索引值,如一个长度为 n 的字符串 s,当索引值 -n ≤ k < 0 时,所指的元素为 s[k],那么求一个正整数索引值 j ≥ 0,使得 s[i] 指向的也是相同的元素。

提示:Give your answer in terms of n and k.

str_1_8 = "Python"
k = -1
print(str_1_8[k])  # "n"
j = len(str_1_8) - abs(k)
print(str_1_8[j])  # "n"

R-1.9 要生成一个值为 50, 60, 70, 80 的排列,求 range 构造函数的参数。

提示:Where does the sequence start and end? What is the step size?

print(list(range(50, 90, 10)))  # [50, 60, 70, 80]

R-1.10 要生成一个值为 8, 6, 4, 2, 0, -2, -4, -6, -8 的排列,求 range 构造函数中的参数。

提示:Use a negative step size.

print(list(range(8, -10, -2)))  # [8, 6, 4, 2, 0, -2, -4, -6, -8]

R-1.11 演示怎样使用 Python 列表解析语法来产生列表[1, 2, 4, 8, 16, 32, 64, 128, 256]。

提示:Those look like powers of two!

print([2 ** n for n in range(0, 9)])  # [1, 2, 4, 8, 16, 32, 64, 128, 256]

R-1.12 Python 的 random 模块包括一个函数 choice(data),可以从一个非空序列返回一个随机元素。Random 模块还包含一个更基本的 randrange 函数,参数化类似于内置的 range 函数,可以在给定范围内返回一个随机数。只使用 randrange 函数,实现自己的 choice 函数。

提示:Use randrange to pick the index of the chosen element.

def choice(data):
    return data[random.randrange(0, len(data))]

arr_1_12 = [1, 10, 3, 5, 7]
print(choice(arr_1_12))  # 10

C-1.13 编写一个函数的伪代码描述,该函数用来逆置 n 个整数的列表,使这些数以相反的顺序输出,并将该方法与可以实现相同功能的 Python 函数进行比较。

提示:The Python function does not need to be passed the value of n as an argument.

def func_1_13(data):
    return data[::-1]

arr_1_13 = [1, 10, 3, 5, 7]
print(func_1_13(arr_1_13))  # [7, 5, 3, 10, 1]

C-1.14 编写一个 Python 函数,用来接收一个整数序列,并判断该序列中是否存在一对乘积是奇数的互不相同的数。

提示:Note that both numbers in the pair must be odd.

def func_1_14(data: list):
    for i in data[0:-1]:
        for j in data[data.index(i) + 1:]:
            if i != j and i * j % 2:
                return True
    return False

arr_1_14_1 = [1, 1, 2, 1, 4]
print(func_1_14(arr_1_14_1))  # 不存在一对乘积是奇数的互不相同的数 False

arr_1_14_2 = [1, 10, 4, 6, 8]
print(func_1_14(arr_1_14_2))  # 不存在一对乘积是奇数的互不相同的数 False

arr_1_14_3 = [1, 10, 3, 5, 7]
print(func_1_14(arr_1_14_3))  # True

C-1.15 编写一个 Python 函数,用来接收一个数字序列,并判断是否所有数字都互相不同 (即它们是不同的)。

提示:The simple solution just checks each number against every other one, but we will discuss better solutions later in the book. But make sure you don’t compare a number to itself.

def func_1_15(data: list):
    return len(data) == len(set(data))

arr_1_15_1 = [1, 10, 3, 3, 7]
print(func_1_15(arr_1_15_1))  # False
arr_1_15_2 = [1, 10, 3, 5, 7]
print(func_1_15(arr_1_15_2))  # True

C-1.16 在 1.5.1节 scale 函数的实现中,循环体内执行的命令 data[j] *= factor。我们已经说过这个数字类型是不可变的,操作符 *= 在这种背景下使用是创建了一个新的实例(而不是现有实例的变化)。那么 scale 函数是如何实现改变调用者发送的实际参数呢?

提示:Think about the semantics of data[j] = data[j] ∗ factor.

# 创建一个 data[j] * factor 的实例,后将这个 data[j] 的地址指向这个实例

C-1.17 1.5.1 节 scale 函数的实现如下。它能正常工作吗? 请给出原因。

def scale(data, factor):
    for val in data:
        val *= factor

提示:Try it out and see if it works!

def scale(data, factor):
    for val in data:
        val *= factor

arr_1_17 = [1, 10, 3, 5, 7]
scale(arr_1_17, 3)
print(arr_1_17)  # 不能正常工作,val 是当前循环过程中创建的新实例,改变的只是这个实例,不改变原序列

C-1.18 演示如何使用 Python 列表解析语法来产生列表 [0, 2, 6, 1, 20, 3, 42, 56, 72, 90]。

提示:What are the factors of each number?

print(list(sum(range(0, i * 2 + 1, 2)) for i in range(10)))  # [0, 2, 6, 12, 20, 30, 42, 56, 72, 90]

C-1.19 演示如何使用 Python 列表解析语法在不输入所有 26 个英文字母的情况下产生列表 ['a', 'b', 'c', ···, 'z']。

提示:Use the chr function with appropriate range.

print(list(chr(i) for i in range(97, 123)))
# ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

C-1.20 Python 的 random 模块包括一个函数 shuffle(data),它可以接收一个元素的列表和一个随机的重新排列元素,以使每个可能的序列发生概率相等。random 模块还包括一个更基本的函数randint(a,b),它可以返回一个从 a 到 b(包括两个端点)的随机整数。只使用 randint 函数实现自己的 shuffle 函数。

提示:Consider randomly swapping an element to the first position, then randomly swapping a remaining element to the second position, and so on.

def shuffle(data: list):
    result = []
    while i := len(data):
        index = random.randint(0, i - 1)
        result.append(data.pop(index))
    return result


arr_1_20 = [1, 10, 3, 5, 7]
print(shuffle(arr_1_20))  # [3, 7, 10, 1, 5]

C-1.21 编写一个Python 程序,反复从标准输入读取一行直到抛出 EOFError 异常,然后以相反的顺序输出这些行(用户可以通过键按 Ctrl+D 结束输入)。

提示:Use a list to store all the lines.

def func_1_21():
    result = []
    while 1:
        try:
            result.append(input("请输入:"))
        except EOFError:
            return result[::-1]


print(func_1_21())
# 请输入:1
# 请输入:2
# 请输入:3
# 请输入:4
# 请输入:5
# 请输入:^D
# ['5', '4', '3', '2', '1']

C-1.22 编写一个 Python 程序,用来接收长度为 n 的两个整型数组 a 和 b 并返回数组 a 和 b 的点积。也就是返回一个长度为 n 的数组 c,即 c[i] = a[i] · b[i],for i = 0,···,n-1。

提示:Go back to the definition of dot product and write a for loop that matches it.

def func_1_22(a: list, b: list):
    return [a[i] * b[i] for i in range(len(a))]


func_1_22_a = [1, 10, 3, 5, 7]
func_1_22_b = [1, 10, 3, 5, 7]

print(func_1_22(func_1_22_a, func_1_22_b))  # [1, 100, 9, 25, 49]

C-1.23 给出一个 Python 代码片段的例子,编写一个索引可能越界的元素列表。如果索引越界,程序应该捕获异常结果并打印以下错误消息:

"Don't try buffer overflow attacks in Python!"
提示:Use a try-except structure.

def func_1_23():
    arr = "Hi"
    i = 0
    try:
        while 1:
            print(arr[i])
            i += 1
    except IndexError:
        print("Don't try buffer overflow attacks in Python!")


func_1_23()
# H
# i
# Don't try buffer overflow attacks in Python!

C-1.24 编写一个 Python 函数,计算所给字符串中元音字母的个数

提示:You can use the condition ch in 'aeiou' to test if a character is a vowel.

def func_1_24(data: str):
    count = 0
    for i in data:
        if i in ["a", "e", "i", "o", "u", "A", "E", "I", "O", "U"]:
            count += 1
    return count


str_1_24 = "Don't try buffer overflow attacks in Python!"
print(func_1_24(str_1_24))  # 10

C-1.25 编写一个 Python 函数,接收一个表示一个句子的字符串 s,然后返回该字符串的删除了所有标点符号的副本。例如,给定字符串"Let's try, Mike.",这个函数将返回"Lets try Mike"。

提示:Consider each character one at a time.

def func_1_25(data: str):
    result = ""
    for i in data[:]:
        if i.isalpha() or i == " ":
            result += i
    return result


str_1_25 = "Let's try, Mike."
print(func_1_25(str_1_25))

C-1.26 编写一个程序,需要从控制台输入 3 个整数 a、b、C,并确定它们是否可以在一个正确的算术公式(在给定的顺序)下成立,如 "a + b = c"、"a = b - c" 或 "a * b = c"。

提示:Try a case analysis for each pair of integers and an operator.

def func_1_26():
    if is_open_26 is False:
        return

    a, b, c = input("三个数用,隔开:").split(",")
    print(a, b, c)
    for operate in ["+", "-", "*", "/"]:
        equation = f"{a} {operate} {b} == {c}"
        if eval(equation):
            print(equation + "成立")
            return True

        equation = f"{a} == {b} {operate} {c}"
        if eval(equation):
            print(equation + "成立")
            return True


func_1_26()
# 三个数用,隔开:10,2,5
# 10 2 5
# 10 == 2 * 5成立

C-1.27 在 1.8 节中,我们对于计算所给整数的因子时提供了 3 种不同的生成器的实现方法。1.8 节末尾处的第三种方法是最有效的,但我们注意到,它没有按递增顺序来产生因子。修改生成器使得其按递增顺序来产生因子,同时保持其性能优势。

提示:Either buffer the bigger value from each pair of factors, or repeat the loop in reverse to avoid the buffer.

def factors(n):
    arr = []
    k = 1
    while k * k < n:
        if n % k == 0:
            yield k
            arr.append(n // k)
        k += 1
    if k * k == n:
        yield k
    for i in arr[::-1]:
        yield i


print(list(factors(100)))

C-1.28 在 n 维空间定义一个向量 v = (${v_1, v_2, ···, v_n}$) 的 p 范数,如下所示:

\[||v|| = \sqrt[p]{v^p_1 + v^p_2 + ··· + v^p_n} \]

对于 p = 2 的特殊情况,这就成了传统的欧几里得范数,表示向量的长度。例如,一个二维向量坐标为 (4, 3) 的欧儿里得范数为 \(\sqrt{ 4^2 + 3^2 }\) = \(\sqrt{ 16 + 9 }\) = \(\sqrt{ 25 }\) = 5。编写 norm 函数,即 norm(v, p) 返回向量 v 的 p 范数的值,norm(v),返回向量的欧几里得范数。你可以假定是一个数字列表。


提示:Use the ∗∗ operator to compute powers.

def norm(v: list, p: int):
    return sum(pow(i, p) for i in v) ** (1 / p)


arr_1_28 = [1, 5, 9]
int_1_28 = 2
print(norm(arr_1_28, int_1_28))  # 10.344080432788601

P-1.29 编写一个 Python 程序,输出由字母 'c', 'a', 't', 'd', 'o', 'g' 组成的所有可能的字符串(每个字母只使用1次)。

提示:There are many solutions. If you know about recursion, the easiest solution uses this technique. Otherwise, consider using a list to hold solutions. If this still seems to hard, then consider using six nested loops (but avoid repeating characters and make sure you allow all string lengths).

def func_1_29(nums: list):
    # 全排列-回溯法
    path = []  # 一次的数据
    res = []  # 所有的结果
    nums_list = len(nums)  # 最大递归层级

    def func():
        # 如果深度等于数组长度,则说明已经遍历到树的叶子节点
        if len(path) == nums_list:
            res.append("".join(path))
            return

        # 遍历数据
        for i in nums:
            # 判断当前元素是否使用过,如果用过则跳出
            if i in path:
                continue

            path.append(i)
            func()
            path.pop()

    func()
    return res


result_1_29 = func_1_29(["c", "a", "t", "d", "o", "g"])
print(len(result_1_29))  # 720

P-1.30 编写一个 Python 程序,输入一个大于 2 的正整数,求将该数反复被 2 整除直到商小于2为止的次数。

提示:This is the same as the logarithm, but you can use recursion here rather than calling the log function.

def func_1_30(data: int):
    return len(str(bin(data))[2:]) - 1


print(func_1_30(100))  # 6

P-1.31 编写一个可以 “找零钱” 的 Python 程序。程序应该将两个数字作为输人,一个是需要支付的钱数,另一个是你给的钱数。当你需要支付的和所给的钱数不同时,它应该返回所找的纸币和硬币的数量。纸币和硬币的值可以基于之前或现任政府的货币体系。试设计程序,以便返回尽可能少的纸币和硬币。

提示:While not always optimal, you can design your algorithm so that it always returns the largest coin possible until the value of the change is met.

def func_1_31(cost: Union[int, float], pay: Union[int, float]):
    if cost >= pay:
        return "不用找"

    dict_1 = {
        "纸币": sorted([100, 50, 20, 10, 5, 1], reverse=True),
        "硬币": sorted([0.5, 0.1], reverse=True),
    }
    paper_money, coin = "", ""

    back = pay - cost
    for type_, quota_list in dict_1.items():
        for quota in quota_list:
            if back >= quota:
                count = int(back // quota)
                back -= count * quota
                if type_ == "纸币":
                    paper_money += f"{quota}元{count}张"
                else:
                    coin += f"{quota}元{count}个"

    return f"纸币:{paper_money or 0},硬币:{coin or 0}"


print(func_1_31(cost=2, pay=4.5))  # 纸币:1元2张,硬币:0.5元1个

P-1.32 编写一个 Python 程序来模拟一个简单的计算器,使用控制台作为输入和输出的专用设备。也就是说,计算器的每一次输入做一个单独的行,它可以输人一个数字(如 1034 或 12.34)或操作符(如 + 或 =)。每一次输入后,应该输出计算器显示的结果并将其输出到 Python 控制台。

提示:Do a case analysis to categorize each line of input.

P-1.33 编写一个 Python 程序来模拟一个手持计算器,程序应该可以处理来自 Python 控制台(表示 push 按钮)的输入,每个操作执行完毕后将内容输出到屏幕。计算器至少应该能够处理基本的算术运算和复位 / 清除操作。

提示:Write your program to loop continually until a quit operation is entered. In each iteration, collect a sequence of button pushes, and then output the result from processing that sequence of pushes.

def func_1_33():
    input_list = []
    result = ""
    while 1:
        try:
            input_ = str(input("请输入:"))
        except EOFError:
            print("退出")
            break

        if input_ == "退出":
            print("退出")
            break

        if input_ == "复位":
            input_list = []
            print("已复位")
            continue

        if input_ == "清除":
            input_list.pop()
            print("".join(input_list))
            continue

        if input_ == "保留":
            input_list = [result]
            print("".join(input_list))
            continue

        input_list.append(input_)
        print("".join(input_list))

        if len(input_list) > 1:
            try:
                result = str(eval(''.join(input_list)))
            except Exception:
                continue

            print(f"执行结果:{result}")
            print("可继续进行计算,或输入 保留 则将结果作为下次运算初始值")


func_1_33()

P-1.34 一种惩罚学生的常见方法是让他们将一个句子写很多次。编写独立的 Python 程序,将以下句子 “I will never spam my friends again.” 写 100 次。程序应该对每个句子进行计数,另外,应该有 8 次不同的随机输入错误。

提示:Define a way of indexing all the sentences and the location in each one and then work out a way of picking eight of these locations for a typo.

1

P-1.35 生日悖论是说,当房间中人数 n 超过 23 时,那么该房间里有两个人生日相同的可能性是一半以上。这其实不是一个悖论,但许多人觉得不可思议。设计一个 Python 程序,可以通过一系列随机生成的生日的实验来测试这个悖论,例如可以 n = 5, 10, 15, 20, ···, 100 测试这个悖论

提示:Use a two-dimensional list to keep track of the statistics and a one-dimensional list for each experiment.

1

P-1.36 编写一个 Python 程序,输人一个由空格分隔的单词列表,并输出列表中的每个单词出现的次数。在这一点上,你不需要担心效率,因为这个问题会在这本书后面的部分予以解决。

提示:You need some way of telling when you have seen the same word you have before. Feel free to just search through your list of words to do this here.

1
posted @ 2023-02-05 22:13  NotZY  阅读(66)  评论(0编辑  收藏  举报