[从头学数学] 第255节 Python实现数据结构:字典树(Trie)

剧情提要:

阿伟看到了一本比较有趣的书,是关于《计算几何》的,2008年由北清派出版。很好奇

它里面讲了些什么,就来看看啦。

正剧开始:

星历2016年08月03日 09:35:13, 银河系厄尔斯星球中华帝国江南行省。

[工程师阿伟]正在和[机器小伟]一起研究[计算几何]]。

关于字典树的实现,已经有非常多的博文涉及到了。但基本都是用内嵌数组/列表实现的。

本博文是它的递归实现。

<span style="font-size:18px;">###
# @usage   字典树
# @author  mw
# @date    2016年08月02日  星期二  08:56:34
# @param
# @return
#
###
class Trie:
    class TrieNode:
        def __init__(self,item,next = None, follows = None):
            self.item = item
            self.next = next
            self.follows = follows

        def __str__(self):
            return str(self.item);

        def getnext(self):
            return self.next;

        def setnext(self, next):
            self.next = next;            

        def getfollows(self):
            return self.follows;

        def setfollows(self, follows):
            self.follows = follows;

        def getitem(self):
            return self.item;

        def setitem(self, item):
            self.item = item;

        def __iter__(self):
            yield self.item;

            if (self.follows != None):
                for x in self.follows:
                    yield x;

        def iternext(self):
            yield self.item;

            if (self.next != None):
                for x in self.next.iternext():
                    yield x;

        #从两个方向观察节点信息,一是从它的后续看,这是看一个单词
        #另一个是从它的兄弟看,这是在这个点的各分支
        def info(self, direction = 0):
            s = '';

            if direction == 0:
                for x in self:
                    s += str(x)+'-->';
            else:
                for x in self.iternext():
                    s += str(x)+'-->';

            print(s);

    def __init__(self):
        self.start = Trie.TrieNode('^', None, None);

    def insert(self,item):
        self.start = Trie.__insert(self.start,item)

    def __contains__(self,item):
        return Trie.__contains(self.start,item)

    #生成诩根所在的结点
    def __genNode(item):
        if (len(item) == 1):
            return Trie.TrieNode(item[0], None, None);
        elif (len(item) > 1):
            return Trie.TrieNode(item[0], None, Trie.__genNode(item[1:]));
        else:
            return None;        

    def __insert(node,item):
        # This is the recursive insert function.
        if (item == None or item == ''):
            return None;
        elif (node == None):
            return Trie.__genNode(str(item));
        elif (node.item == item[0]):
            node.setfollows(Trie.__insert(node.getfollows(), item[1:]));
        else:
            node.setnext(Trie.__insert(node.getnext(), item));

        return node;

    def __contains(node, item):
        # This is the recursive membership test.
        #单词结尾用'$'分隔,当然,如果用其它分隔符,此处必须更改item+'$'
        if Trie.__getNode(node, item) != None:
            return True;
        else:
            return False;

    #一般都是从字典的根结点开始遍历才有意义
    def getNode(self, node, item):
        return Trie.__getNode(node, item);

    #找到某一节点
    def __getNode(node, item):
        if item == None or item == '':
            return None;
        elif node == None:
            return None;
        elif node.item != item[0]:
            return Trie.__getNode(node.next, item);
        else:
            if (len(item) > 1):
                return Trie.__getNode(node.follows, item[1:]);
            else:
                return node;

    #遍历查看字典
    def dict(self):
        #单词结束的末尾标记
        endChar = '$';

        count = 0;

        if (self.start != None):
            cursor = self.start;

            #具有后续节点的词段
            dict_1 = [];
            #最终版
            dict_2 = [];

            while cursor != None:
                #if (cursor.follows == None):
                if (cursor.follows == None):
                    #过滤掉词尾结束标记
                    dict_2.append(str(cursor.item)[:-1]);
                else:
                    dict_1.append(str(cursor.item));

                cursor = cursor.next;

            while (len(dict_1) > 0):
                a = dict_1.pop(0);

                cursor = Trie.__getNode(self.start, a);
                count+=1;
                if (cursor != None):
                    cursor = cursor.follows;

                    while cursor != None:
                        if (cursor.follows == None):
                            dict_2.append((a+str(cursor.item))[:-1]);
                        else:
                            dict_1.append(a+str(cursor.item));

                        cursor = cursor.next;

            print('找结点{0}次'.format(count));
            print('字典:', dict_2);
        else:
            print('字典为空');
</span>

用例:

<span style="font-size:18px;">def main():
    #计时开始
    startTime = time.clock();

    t = Trie();

    a = ['I', 'love', 'lot', 'lance', 'you', 'you', 'love', 'me',
         'i', 'have', 'a', 'book', 'you', 'have', 'a', 'pencil',
         'pen', 'paper', 'lottol', 'banana'];

    #插入单词,词尾加结束符
    for i in range(len(a)):
        t.insert(a[i]+'$');

    t.start.next.next.follows.next.info();
    t.start.info(1);     

    n1 = t.getNode(t.start, 'lottol');
    if (n1 != None):
        n1.info();
        n1.info(1);
    else:
        print('无该节点');

    t.dict();

    print('banana'+'$' in t);
    print('how'+'$' in t);  

    #计时结束
    endTime = time.clock();

    #打印结果
    print('操作用时:{0:.3e} s'.format(endTime-startTime));

</span>

短短数行代码,写得非常头痛。用递归是不是比用列表有更大好处,阿伟也不清楚。

但是有一点可以肯定,用递归可以把整个字典连成一棵树,而列表做不到。

即将奥运会了,阿伟决定好好地去研究一下奥运会,另外,最近也出了不少好看的电视剧,

也要抽点时间去看看。

本节到此结束,欲知后事如何,请看下回分解。

时间: 2024-11-08 13:29:09

[从头学数学] 第255节 Python实现数据结构:字典树(Trie)的相关文章

[从头学数学] 第174节 算法初步

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了结丹中期的修炼, 这次要修炼的目标是[算法初步]. 正剧开始: 星历2016年04月12日 08:54:58, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起研究[算法初步]. [人叫板老师]指点小伟说:"这金丹要想大成,顺利进入元婴期,就必须进行九转培炼. 这什么是九转培炼法门呢?就是要先快速的修炼[天地人正册]进入后期,不要管各种辅修 功法,然后从头游历[天地人列国],在游历中增长见闻,精炼神通,最后再修炼[术.

[从头学数学] 第223节 带着计算机去高考(十五)

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第八转的修炼.设想一个场景: 如果允许你带一台不连网的计算机去参加高考,你会放弃选择一个手拿计算器和草稿本吗 ?阿伟决定和小伟来尝试一下用计算机算高考题会是怎样的感觉. 正剧开始: 星历2016年05月26日 10:23:46, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起做着2014年的江苏省数学高考题]. 这一年的题和上一年一样的难,阿伟决定再交一次白卷. 好,卷子贴完,下面进入这次的主题. 这是

[从头学数学] 第219节 带着计算机去高考(十一)

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第八转的修炼.设想一个场景: 如果允许你带一台不连网的计算机去参加高考,你会放弃选择一个手拿计算器和草稿本吗 ?阿伟决定和小伟来尝试一下用计算机算高考题会是怎样的感觉. 正剧开始: 星历2016年05月24日 12:49:39, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起做着2010年的江苏省数学高考题]. 2010年江苏的这张高考试卷,是大家公认的一张难卷,难出了风格, 难出了特色,也难出了名.

[从头学数学] 第215节 带着计算机去高考(七)

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第八转的修炼.设想一个场景: 如果允许你带一台不连网的计算机去参加高考,你会放弃选择一个手拿计算器和草稿本吗 ?阿伟决定和小伟来尝试一下用计算机算高考题会是怎样的感觉. 正剧开始: 星历2016年05月20日 17:13:35, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起做着2006年的江苏省数学高考题]. 这一年,江苏重新使用了全国卷,并且这张试卷的难度也比较高,可以说, 也是打了考生一个措手不及

[从头学数学] 第192节 导数及其应用

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第五转的修炼. 这次要研究的是[导数及其应用]. 正剧开始: 星历2016年04月23日 16:32:36, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起研究[导数及其应用]. <span style="font-size:18px;">>>> [-3.000001001396413, -2.999998999442255] [4.999998999721811

[从头学数学] 第214节 带着计算机去高考(六)

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第八转的修炼.设想一个场景: 如果允许你带一台不连网的计算机去参加高考,你会放弃选择一个手拿计算器和草稿本吗 ?阿伟决定和小伟来尝试一下用计算机算高考题会是怎样的感觉. 正剧开始: 星历2016年05月20日 11:40:58, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起做着2005年的江苏省数学高考题]. 总体来说,这次的难度和上一年持平,都是很厚道的那种, 不过上一年的好多题都像闹着玩似的,这次

[从头学数学] 第220节 带着计算机去高考(十二)

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第八转的修炼.设想一个场景: 如果允许你带一台不连网的计算机去参加高考,你会放弃选择一个手拿计算器和草稿本吗 ?阿伟决定和小伟来尝试一下用计算机算高考题会是怎样的感觉. 正剧开始: 星历2016年05月24日 17:11:11, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起做着2011年的江苏省数学高考题]. 2011年的卷子,难度比上一年的稍小一点,但阿伟觉得也达到了5.5环的难度. 这次的特色,是

[从头学数学] 第179节 三角初等变换

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了结丹中期的修炼, 这次要修炼的目标是[三角初等变换]. 正剧开始: 星历2016年04月15日 15:32:35, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起研究[三角初等变换]. <span style="font-size:18px;"> if (1) { var mathText = new MathText(); var s = [ '和角.差角公式', 'sin(A+B) = si

[从头学数学] 第216节 带着计算机去高考(八)

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第八转的修炼.设想一个场景: 如果允许你带一台不连网的计算机去参加高考,你会放弃选择一个手拿计算器和草稿本吗 ?阿伟决定和小伟来尝试一下用计算机算高考题会是怎样的感觉. 正剧开始: 星历2016年05月21日 11:31:19, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起做着2007年的江苏省数学高考题]. 这一年的卷子又回到了比较低的难度,感觉这就是正弦曲线的节奏. <span style="