[2021-Fall] Lab08 of CS61A of UCB


Write a function convert_link that takes in a linked list and returns the sequence as a Python list. You may assume that the input list is shallow; that is none of the elements is another linked list.

Try to find both an iterative and recursive solution for this problem!

迭代的算法很簡單, 我們只要創建一個 result list 來存儲結果, 遍歷鏈表的同時記住訪問過的數即可

def convert_link(link):
    """Takes a linked list and returns a Python list with the same elements.
    """
    result = []
    while link is not Link.empty:
        result.append(int(link.first))
        link = link.rest
    return result

遞歸的算法也簡單, base case 就是我們遇到了空結點的時候, 這個時候返回的是空的值, 其他情況我們遞歸分解: 當前結點 + 鏈表的剩余結點

def convert_link(link):
    """Takes a linked list and returns a Python list with the same elements.
    """
    # recursive solution
    if link is Link.empty:
        return []
    else:
        return [int(link.first)] + convert_link(link.rest)

Trees


Q4: Square

Write a function label_squarer that mutates a Tree with numerical labels so that each label is squared.

遍歷樹的所有節點, 將它的 label 都改為 label 的平方

def label_squarer(t):
    """Mutates a Tree t by squaring all its elements.
    """
    # base case
    if t.is_leaf():
        t.label = t.label ** 2

    # check every branch
    for b in t.branches:
        t.label = t.label ** 2          # change the current node's label
        label_squarer(b)                # change branches

Q5: Cumulative Mul

Write a function cumulative_mul that mutates the Tree t so that each node's label becomes the product of its label and all labels in the subtrees rooted at the node.

這一題的意思是: 我們要讓每個節點的 label 等於它的所有孩子節點的 label 的乘積, 顯然, 這是一個遞歸問題

base case 就是葉子結點, 此時返回葉子節點的 label. 遞歸分解問題就是遍歷節點的每一個子樹, 獲得每個子節點返回值再乘以當前結點的 label.

注意我這里用到了 math.prod 這個函數返回可迭代對象的連乘結果, 如果你也要這樣使用, 你應該在文件的開頭寫 import math

def cumulative_mul(t):
    """Mutates t so that each node's label becomes the product of all labels in
    """
    # base case
    if t.is_leaf():
        return t.label
    # get all label value in subtree
    vals = [cumulative_mul(b) for b in t.branches]

    # calculate
    t.label *= math.prod(vals)

Q6: Add Leaves

Implement add_d_leaves, a function that takes in a Tree instance t and a number v.

We define the depth of a node in t to be the number of edges from the root to that node. The depth of root is therefore 0.

For each node in the tree, you should add d leaves to it, where d is the depth of the node. Every added leaf should have a label of v. If the node at this depth has existing branches, you should add these leaves to the end of that list of branches.

For example, you should be adding 1 leaf with label v to each node at depth 1, 2 leaves to each node at depth 2, and so on.

Here is an example of a tree t(shown on the left) and the result after add_d_leaves is applied with v as 5.

翻譯一下這一道題的意思: 我們要根據結點在樹中的高度(根結點高度為 0)來給結點添加子節點, 這里說的符合條件是 label 為 v. 高度為 d 就增加 d 個子節點.

這題第一個困難是要如何獲取當前結點的高度, 因為有了高度我們才能知道要增加多少個子節點, 我們可以維護一個參數表示當前結點的高度. 每當我們往樹的更深一層前進的時候我們就將它 +1.

base case 就是在葉子結點, 我們根據它的高度增加子結點. 然后對於當前的節點, 我們需要對它的每個孩子節點重復這個步驟, 同時也要判斷當前節點是否需要添加子節點

def add_d_leaves(t, v):
    """Add d leaves containing v to each node at every depth d.
    """
    def helper(t, v, depth):
        # base case
        if t.is_leaf():
            for i in range(depth):
                t.branches.append(Tree(v))
            return

        # check every branch
        for b in t.branches:
            helper(b, v, depth + 1)
        

        # check current node
        for i in range(depth):
            t.branches.append(Tree(v))

    helper(t, v, 0)

Optional Questions


Q7: Every Other

Implement every_other, which takes a linked list s. It mutates s such that all of the odd-indexed elements (using 0-based indexing) are removed from the list. For example:

>>> s = Link('a', Link('b', Link('c', Link('d'))))
>>> every_other(s)
>>> s.first
'a'
>>> s.rest.first
'c'
>>> s.rest.rest is Link.empty
True

If s contains fewer than two elements, s remains unchanged.

Do not return anything! every_other should mutate the original list.

我們可以用迭代的方法來處理這個問題, 首先要處理的是長度的問題, 如果鏈表的長度小於 2, 那么直接返回.

否則我們就維護兩個指針指向當前位置和上一個訪問的位置, 當我們要刪除索引為奇數(索引從 0 開始)的點的時候就讓上一個節點直接指向當前這個節點的下一個節點即可.

def every_other(s):
    """Mutates a linked list so that all the odd-indiced elements are removed
    """
    # if it contains fewer than 2, do nothing
    if s is Link.empty or s.rest is Link.empty:
        return

    last_pos, pos = s, s.rest
    current_index = 1               # start from 2nd position
    while pos is not Link.empty:
        if current_index % 2 == 1: 
            last_pos.rest = pos.rest
        last_pos = pos
        pos = pos.rest
        current_index += 1

Q8: Prune Small

Complete the function prune_small that takes in a Tree t and a number n and prunes t mutatively. If t or any of its branches has more than n branches, the n branches with the smallest labels should be kept and any other branches should be pruned, or removed, from the tree.

這一道題要我們裁剪這棵樹, 具體要求是才見到每個節點最多 n 個子樹, 如果超過了 n 就優先移除 label 比較大的, 其實這一道題已經為我們提供了代碼, 只要挖空填寫就好了.

不難相處, 我們應該自頂向下(從根節點出發)裁剪, 所以第一個 while 循環要完成的動作是如果分支數 > n, 那么找到最大的刪掉

而后面的 for 循環則是要讓我們到子樹中遞歸裁減

def prune_small(t, n):
    """Prune the tree mutatively, keeping only the n branches
    of each node with the smallest label.
    """
    while len(t.branches) > n:
        largest = max(t.branches, key=lambda x: x.label)
        t.branches.remove(largest)

    for b in t.branches:
        prune_small(b, n)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM