常用算法实现(二)——前缀树TrieTree

一.概述(多个关键词-实体检索-查询)

         TrieTree(前缀树),又被称为字典树、单词查找树,是一种比较常见的数据存储结构与算法。

         顾名思义,前缀树便是公共的字符只保存一次的多路树。如你所见,它的基本思想是以时间换空间,时间复杂度为logN,效果还不错。不过,我觉得它应用广泛的另外一个原因是它保存了字符的顺序。

         应用: 字符串检索、查询与排序,前缀与公共前缀等。

        github地址: https://github.com/yongzhuo/Tookit-Sihui/blob/master/tookit_sihui/ml_common/trie_tree/trie_tree.py

二.实现(多个关键词-实体检索-查询)

            实现的是: 从句中中查找并提取关键词或者是实体

# -*- coding: UTF-8 -*-
# !/usr/bin/python
# @time     :2019/6/27 16:40
# @author   :Mo
# @function :TrieTree of keywords find, 只返回查全的情况, 查找句子中的关键词(例如影视名、人名、关键词、实体等)


class TrieNode:
    """
        前缀树节点-链表
    """
    def __init__(self):
        self.child = {}


class TrieTree:
    """
        前缀树构建、新增关键词、关键词词语查找等
    """
    def __init__(self):
        self.root = TrieNode()

    def add_keyword(self, keyword):
        """
            新增一个关键词
        :param keyword: str,构建的关键词
        :return: None
        """
        node_curr = self.root
        for word in keyword:
            if node_curr.child.get(word) is None:
                node_next = TrieNode()
                node_curr.child[word] = node_next
            node_curr = node_curr.child[word]
        # 每个关键词词后边,加入end标志位
        if node_curr.child.get('[END]') is None:
            node_next = TrieNode()
            node_curr.child['[END]'] = node_next
        node_curr = node_curr.child['[END]']

    def add_keywords_from_list(self, keywords):
        """
            新增关键词s, 格式为list
        :param keyword: list, 构建的关键词
        :return: None
        """
        for keyword in keywords:
            self.add_keyword(keyword)

    def find_keyword(self, sentence):
        """
            从句子中提取关键词, 可提取多个
        :param sentence: str, 输入的句子
        :return: list, 提取到的关键词
        """
        assert type(sentence) == str
        if not sentence: # 空格字符不取
            return []

        node_curr = self.root # 关键词的头, 每遍历完一遍后需要重新初始化
        index_last = len(sentence)
        keyword_list = []
        keyword = ''
        count = 0
        for word in sentence:
            count += 1
            if node_curr.child.get(word) is None: # 查看有无后缀, 即匹配到一个关键词最后一个字符的时候
                if keyword: # 提取到的关键词(也可能是前面的几位)
                    if node_curr.child.get('[END]') is not None: # 取以end结尾的关键词
                        keyword_list.append(keyword)
                    if self.root.child.get(word) is not None: # 处理连续的关键词情况, 如"第九区流浪地球"
                        keyword = word
                        node_curr = self.root.child[word]
                    else: #
                        keyword = ''
                        node_curr = self.root  # 重新初始化
            else: # 有后缀就加到name里边
                keyword = keyword + word
                node_curr = node_curr.child[word]
                if count == index_last:  # 实体结尾的情况
                    if node_curr.child.get('[END]') is not None:
                        keyword_list.append(keyword)
        return keyword_list


def get_trie_tree_class(keywords):
    """
        根据list关键词,初始化trie树
    :param keywords: list, input
    :return: objext, 返回实例化的trie
    """
    trie = TrieTree()
    trie.add_keywords_from_list(keywords)
    return trie


if __name__ == "__main__":
    # 测试1, class实例
    trie = TrieTree()
    keywords = ['英雄', '人在囧途', '那些年,我们一起追过的女孩', '流浪地球', '华娱',
                '犬夜叉', '火影', '名侦探柯南', '约会大作战', '名作之壁', '动漫',
                '乃木坂46', 'akb48', '飘', '最后的武士', '约会', '英雄2', '日娱',
                '2012', '第九区', '星球大战', '侏罗纪公园', '泰坦尼克号', 'Speed']
    keywords = [list(keyword.strip()) for keyword in keywords]
    trie.add_keywords_from_list(keywords) # 创建树
    keyword = trie.find_keyword('第九区约会, 侏罗纪公园和泰坦尼克号泰坦尼克号')
    print(keyword)

    # 测试2, get树
    trie_tree = get_trie_tree_class(keywords) # 创建树并返回实例化class
    while True:
        print("sihui请你输入:")
        input_ques = input()
        keywords = trie_tree.find_keyword(input_ques)
        print(keywords)


希望对你有所帮助!
 

发布了96 篇原创文章 · 获赞 72 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/rensihui/article/details/94052412