樹結構(一)——二叉樹、字典樹的操作

一、二叉樹的遍歷

        樹結構分爲廣度優先遍歷和深度優先遍歷。廣度遍歷是一層一層的遍歷樹中的元素,這種遍歷方式需要藉助隊列的方式,左右子樹分別入隊列,利用隊列先進先出的特性,按層對樹進行遍歷。深度遍歷有又分爲前序遍歷、中序遍歷、後序遍歷,針對這三種遍歷方式,又有利用遞歸的形式進行遍歷,利用堆棧結構進行遍歷。

        儘管樹的遍歷看起來有很多種方式,但是我們平常更多用到的是基於遞歸方式的深度遍歷,故而讀者看到這些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----------------------------')

 

發佈了46 篇原創文章 · 獲贊 50 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章