ex23.md 4.9 KB
Newer Older
W
ex23  
wizardforcel 已提交
1 2 3 4 5 6 7 8 9 10
# 练习 23:三叉搜索树

> 原文:[Exercise 23: Ternary Search Trees](https://learncodethehardway.org/more-python-book/ex23.html)

> 译者:[飞龙](https://github.com/wizardforcel)

> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)

> 自豪地采用[谷歌翻译](https://translate.google.cn/)

W
ex23  
wizardforcel 已提交
11
我们将研究的最后一个数据结构称为三叉搜索树(TSTree),它可以在一组字符串中快速查找字符串。它类似于`BSTree`,但是它有三个子节点,而不是两个,每个子节点只是一个字符而不是整个字符串。在`BSTree`中,左子节点和右子节点是树的“小于”和“大于”的分支。在`TSTree`中,左子节点,中子节点和右子节点是“小于”,“等于”和“大于”的分支。这可以让你选取一个字符串,将其分解成字符,然后遍历`TSTree`,每次一个字符,直到找到它或者你到达了末尾。
W
ex23  
wizardforcel 已提交
12

W
ex23  
wizardforcel 已提交
13
通过将你要搜索的一组键拆成单个字符的节点,`TSTree`高效地使用空间换取时间。每一个这些节点将占用比`BSTree`更多的空间,但这允许你仅仅通过比较键中的字符来搜索键。使用`BSTree`,你必须比较每个节点的键和被搜索键中的大多数字符。使用`TSTree`,你只需要比较被搜索键的每个字母,当你到达末尾,就完成了。
W
ex23  
wizardforcel 已提交
14

W
ex23  
wizardforcel 已提交
15
`TSTree`的另一件不错的事情是,它知道一个键何时不存在于集合中。想象一下,你的键的长度为 10 个字符,你需要在一组其他的键中找到它,但是如果键不存在,则需要快速停止。使用`TSTree`,你可以在一到两个字符的地方停止,到达树的末尾,并且知道这个键不存在。你最多只能比较键中的 10 个字符来发现它,字符比较比`BSTree`少得多。
W
ex23  
wizardforcel 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

## 挑战练习

这个练习中,你打算完成另一个“代码大师复制”的一部分,之后独立完成`TSTree `。你所需的代码是:

```py
class TSTreeNode(object):

    def __init__(self, key, value, low, eq, high):
        self.key = key
        self.low = low
        self.eq = eq
        self.high = high
        self.value = value


class TSTree(object):

    def __init__(self):
        self.root = None

    def _get(self, node, keys):
        key = keys[0]
        if key < node.key:
            return self._get(node.low, keys)
        elif key == node.key:
            if len(keys) > 1:
                return self._get(node.eq, keys[1:])
            else:
                return node.value
        else:
            return self._get(node.high, keys)

    def get(self, key):
        keys = [x for x in key]
        return self._get(self.root, keys)

    def _set(self, node, keys, value):
        next_key = keys[0]

        if not node:
            # what happens if you add the value here?
            node = TSTreeNode(next_key, None, None, None, None)

        if next_key < node.key:
            node.low = self._set(node.low, keys, value)
        elif next_key == node.key:
            if len(keys) > 1:
                node.eq = self._set(node.eq, keys[1:], value)
            else:
                # what happens if you DO NOT add the value here?
                node.value = value
        else:
            node.high = self._set(node.high, keys, value)

        return node

    def set(self, key, value):
        keys = [x for x in key]
        self.root = self._set(self.root, keys, value)
```

W
ex23  
wizardforcel 已提交
78
你需要使用你学到的“代码大师复制”方法学习。要特别注意如何处理`node.eq`路径以及如何设置`node.value`。一旦你了解了`get``set`的工作方式,你将实现剩下的函数和所有的测试。要实现的函数有:
W
ex23  
wizardforcel 已提交
79 80 81

> `find_shortest`

W
ex23  
wizardforcel 已提交
82
> 给定一个关键字`K`,找到以`K`开头的最短键/值对。这意味着如果你的`set`中有`apple`和`application` ,那么调用`find_shortest("appl")`将返回关联`apple`的值。
W
ex23  
wizardforcel 已提交
83 84 85

> `find_longest`

W
ex23  
wizardforcel 已提交
86
> 给定一个关键字`K`,找到以`K`开头的最长键/值对。这意味着如果你的`set`中有`apple`和`application` ,那么调用`find_shortest("appl")`将返回关联`application`的值。
W
ex23  
wizardforcel 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99

> `find_all`

> 给定一个关键字`K`,找到以`K`开头的所有键/值对。我会先实现它,然后基于它实现`find_shortest`和`find_longest`。

> `find_part`

> 给定一个关键字`K`,找到最短的键,它拥有`K`的开头的一部分。研究如何以及在哪里设置`node.value`来使其生效。

## 研究性学习

+   查看原始代码的注释,看看在`_set`过程中,在哪里放置`value`。修改它会修改`get`的含义吗?为什么?
+   确保你使用随机数据来测试,并测量一些性能。
W
ex23  
wizardforcel 已提交
100
+   你也可以在`TSTree`中进行模糊匹配。我认为这是一个附加题,所以尝试实现它们,看看你想出了什么。部分匹配是,`'a.p.e'`匹配`"apple"``"anpxe"``"ajpqe"`
W
ex23  
wizardforcel 已提交
101
+   如何搜索字符串的结尾?提示:不要过度考虑它。