树结构(一)——二叉树、字典树的操作

一、二叉树的遍历

        树结构分为广度优先遍历和深度优先遍历。广度遍历是一层一层的遍历树中的元素,这种遍历方式需要借助队列的方式,左右子树分别入队列,利用队列先进先出的特性,按层对树进行遍历。深度遍历有又分为前序遍历、中序遍历、后序遍历,针对这三种遍历方式,又有利用递归的形式进行遍历,利用堆栈结构进行遍历。

        尽管树的遍历看起来有很多种方式,但是我们平常更多用到的是基于递归方式的深度遍历,故而读者看到这些introduction不用紧张,小编已经对代码做了注释便于大家理解。

class Node(object):
    """节点类"""
    def __init__(self, elem=-1, lchild=None, rchild=None):
        self.elem = elem
        self.lchild = lchild
        self.rchild = rchild

class Tree(object):
    """
        普通二叉树
    """
    def __init__(self):
        self.root = Node()  # 根节点
        self.myQueue = []

    def add(self, elem):
        """为树添加节点"""
        node = Node(elem)
        if self.root.elem == -1:    # 如果树是空的,则对根节点赋值
            self.root = node
            self.myQueue.append(self.root)
        else:
            treeNode = self.myQueue[0]  # 此结点的子树还没有齐。
            # print("~~", treeNode.elem, len(self.myQueue))
            # 因为是二叉树,故而添加左子树的时候不会移除队首元素(因为此时其右子树还可以再添加),添加右子树的时候会移除队首元素(此时左右子树都有,当前节点就可以移除了不需要再插入新的树了)。
            if treeNode.lchild == None:
                treeNode.lchild = node
                self.myQueue.append(treeNode.lchild)
            else:
                treeNode.rchild = node
                self.myQueue.append(treeNode.rchild)
                self.myQueue.pop(0)  # 如果该结点存在右子树,将此结点丢弃。

    def front_digui(self, root):
        """利用递归实现树的先序遍历,深度遍历"""
        if root == None:
            return
        print(root.elem)
        self.front_digui(root.lchild)
        self.front_digui(root.rchild)

    def middle_digui(self, root):
        """利用递归实现树的中序遍历,深度遍历"""
        if root == None:
            return
        self.middle_digui(root.lchild)
        print(root.elem)
        self.middle_digui(root.rchild)

    def later_digui(self, root):
        """利用递归实现树的后序遍历,深度遍历"""
        if root == None:
            return
        self.later_digui(root.lchild)
        self.later_digui(root.rchild)
        print(root.elem)

    def front_stack(self, root):
        """利用堆栈实现树的先序遍历,深度遍历"""
        if root == None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:  # 从根节点开始,一直找它的左子树
                print(node.elem)
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()  # while结束表示当前节点node为空,即前一个节点没有左子树了
            node = node.rchild  # 开始查看它的右子树

    def middle_stack(self, root):
        """利用堆栈实现树的中序遍历,深度遍历"""
        if root == None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:  # 从根节点开始,一直找它的左子树
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()  # while结束表示当前节点node为空,即前一个节点没有左子树了
            print(node.elem)
            node = node.rchild  # 开始查看它的右子树

    def later_stack(self, root):
        """利用堆栈实现树的后序遍历,深度遍历"""
        if root == None:
            return
        myStack1 = []
        myStack2 = []
        node = root
        myStack1.append(node)
        while myStack1:  # 这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
            node = myStack1.pop()
            if node.lchild:
                myStack1.append(node.lchild)
            if node.rchild:
                myStack1.append(node.rchild)
            myStack2.append(node)
        while myStack2:  # 将myStack2中的元素出栈,即为后序遍历次序
            print(myStack2.pop().elem)

    def level_queue(self, root):
        """利用队列实现树的层次遍历,广度遍历"""
        if root == None:
            return
        myQueue = []
        node = root
        myQueue.append(node)
        while myQueue:
            node = myQueue.pop(0)
            print(node.elem)
            if node.lchild != None:
                myQueue.append(node.lchild)
            if node.rchild != None:
                myQueue.append(node.rchild)

if __name__ == '__main__':
    """主函数"""
    elems = range(10)   # 生成十个数据作为树节点
    tree = Tree()       # 新建一个树对象
    for elem in elems:
        tree.add(elem)  # 逐个添加树的节点

    print('队列实现层次遍历:', tree.level_queue(tree.root))

    print()
    print('递归实现先序遍历:', tree.front_digui(tree.root))

    print()
    print('递归实现中序遍历:', tree.middle_digui(tree.root))

    print()
    print('递归实现后序遍历:', tree.later_digui(tree.root))

    print()
    print('堆栈实现先序遍历:', tree.front_stack(tree.root))

    print()
    print('堆栈实现中序遍历:', tree.middle_stack(tree.root))

    print()
    print('堆栈实现后序遍历:', tree.later_stack(tree.root))

二、字典树的操作

       字典树又称前缀树,字典树常用于搜索提示,如当输入一个网址,可以自动搜索出可能的选择。小编在工作中用到最多的树就是字典树了,因为小编会将字典树固化到磁盘上,每天更新一次固化的文件,第二天会用这个字典树支撑一些搜索业务,当然这是前期数据量不大的情况,如果数据量很大,则可能要根据时间序列构建多棵字典树,并发的对这些树结构进行检索。

       字典树并不是二叉树,一个父节点可能会有多个子节点,故而在定义树的node类时,记录子节点的数据结构是dict。

       python的代码如下,仅供读者参考,另外小编在固化字典树的时候用到的是python的pickle包,大家可自行查阅相关信息。

import _pickle as pickle


class TrieNode:
    def __init__(self):
        '''
        定义节点的数据结构,并初始化,设置标志位判断是否单词是否是完整的存在于字典树中
        '''
        self.data = {}
        self.flag = False
        self.value = {}


class TrieTree(object):
    def __init__(self):
        '''
        定义字典树的数据结构并初始化根节点
        '''
        self.root = TrieNode()

    def insert_func(self, data):
        '''
        字典树的插入函数,data为待插入的数据,类型为字符串
        '''
        top_node = self.root
        for i in range(len(data["word"])):
            one_char = data["word"][i]
            child_node = top_node.data.get(one_char)   # 当前节点的子节点,因为一个字母可能是很多字母的前缀,故而用dict存储
            if not child_node:
                top_node.data[one_char] = TrieNode()
            top_node = top_node.data[one_char]
        top_node.flag = True
        top_node.value["age"] = data["age"]

    def search_func(self, data):
        '''
        字典树的查找函数,data类型为字符串
        '''
        top_node = self.root
        for i in range(len(data)):
            one_char = data[i]
            top_node = top_node.data.get(one_char)
            if not top_node:
                return False
        return top_node.flag, top_node.value


if __name__ == '__main__':
    str_list = ['html', 'head', 'body', 'meta', 'div', 'link', 'a', 'comment', 'iframe', 'ul', 'li']
    str_list_new = []
    for i in range(len(str_list)):
        str_list_new.append({
            "word": str_list[i],
            "age": i+1
        })
    TestTrieTree = TrieTree()
    for one_str in str_list_new:
        TestTrieTree.insert_func(one_str)
    print('***********************************结果如下,True表示存在,False表示不存在******************************************')

    search_list = ['html', 'table', 'body', 'tr', 'h1', 'script', 'javascript', 'iframe']
    for one_str in search_list:
        print(one_str, TestTrieTree.search_func(one_str))
    print()

    print('----------------------------------持久化存储TestTrieTree模型---------------------------------')
    trie_file = open('trie.pkl', 'wb')
    pickle.dump(TestTrieTree, trie_file)
    trie_file = open('trie.pkl', "rb")
    trie_model = pickle.load(trie_file)
    print()

    # 二次插入
    trie_model.insert_func({"word":"yi", "age":100})
    trie_model.insert_func({"word":"shui", "age":200})
    trie_model.insert_func({"word":"han", "age":300})
    trie_model.insert_func({"word":"cheng", "age":400})
    print('----------------------------------insert over----------------------------------')

    print(trie_model.search_func('yi'))
    print(trie_model.search_func('shui'))
    print(trie_model.search_func('han'))
    print(trie_model.search_func('cheng'))
    print('-------------------search over----------------------------')

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值