树是一种数据结构,好比:目录结构。html
树是一种能够递归定义的数据结构。node
定义:树是由n个节点组成的集合:python
若是n=0,那这是一棵空树;数据库
若是n>0,那存在1个节点做为树的根节点,其余节点能够分为m个集合,每一个集合自己又是一棵树。数据结构
根节点: 根节点(root)是树的一个组成部分,也叫树根。它是同一棵树中除自己外全部节点的祖先,没有父节点。app
叶子节点(终端节点):一棵树当中没有子节点(即度为0)的结点称为叶子结点,简称“叶子”。 叶子是指度为0的结点,又称为终端结点。dom
树的深度(高度):树中节点的最大层次。post
节点的度:一个节点含有的子树的个数称为该节点的度。spa
树的度:一棵树中,最大的节点的度称为树的度。3d
父节点(双亲节点):若一个节点含有子节点,则这个节点称为其子节点的父节点;
子树:设T是有根树,a是T中的一个顶点,由a以及a的全部后裔(后代)导出的子图称为有向树T的子树。
class Node: def __init__(self, name, type='dir'): self.name = name self.type = type # 类型能够是"dir"或"file" self.children = [] self.parent = None
"""链式存储""" def __repr__(self): return self.name class FileSystemTree: def __init__(self): self.root = Node("/") # 根目录 self.now = self.root # 当前目录 def mkdir(self, name): """建立目录""" if name[-1] != "/": name += "/" # 判断当不是以"/"结尾,添加"/" node = Node(name) # 建立文件夹 self.now.children.append(node) node.parent = self.now def ls(self): """展现当前目录下的全部目录""" return self.now.children def cd(self, name): """切换路径""" if name[-1] != "/": name += "/" # 判断当不是以"/"结尾,添加"/" if name == "../": self.now = self.now.parent return for child in self.now.children: if child.name == name: self.now = child return raise ValueError("invalid dir") tree = FileSystemTree() tree.mkdir("var/") tree.mkdir("bin/") tree.mkdir("usr/") print(tree.root.children) # [var/, bin/, usr/] print(tree.ls()) # [var/, bin/, usr/] tree.cd("bin/") tree.mkdir("python/") print(tree.ls()) # [python/] tree.cd("../") print(tree.ls()) # [var/, bin/, usr/]
树绝大多数的存储都是和链表同样链式存储。日后指child;往前指parent。经过节点和节点间相互链接的关系来组成这么一个数据结构。
二叉树:度不超过2的树。以下所示:
每一个节点最多有两个孩子节点,两个孩子节点被区分为左孩子节点和右孩子节点。
一个二叉树若是每一层的节点数都达到最大值,则这个二叉树就是满二叉树。
叶节点只能出如今最下层和次下层,而且最下面一层的节点都集中在该层最左边的若干位置的二叉树。
满二叉树必定是彻底二叉树,但彻底二叉树不必定是满二叉树。堆是一个特殊的彻底二叉树。
二叉树这种数据结构在计算机中的存储方法。
二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间经过相似链表的连接方式来链接。
class BiTreeNode: def __init__(self, data): # data就是传进去的节点值 self.data = data self.lchild = None self.rchild = None
代码以下:
class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子 # 建立二叉树节点 a = BiTreeNode("A") b = BiTreeNode("B") c = BiTreeNode("C") d = BiTreeNode("D") e = BiTreeNode("E") f = BiTreeNode("F") g = BiTreeNode("G") # 节点链接 e.lchild = a e.rchild = g a.rchild = c c.lchild = b c.rchild = d g.rchild = f # 指定根节点 root = e print(root.lchild.rchild.data) # C
所谓顺序存储方式就是二叉树用列表来存储。以下图所示就是用列表来存储二叉树。
如上图二叉树标出了元素所对应的索引,则能够有如下结论:
父与左子下标关系:0-1 1-3 2-5 3-7 4-9
i (父)——>2i+1 (子)
若是已知父亲节点为i,那么他的左孩子节点为2i+1
父与右子下标关系:0-2 1-4 2-6 3-8 4-10
i (父)——>2i+2 (子)
若是知道父亲节点为i,那么他的右孩子节点为2i+2
知道左孩子求父节点:(n-1)/2=i
知道右孩子求父节点:(n-2)/2=i
访问根节点操做发生在遍历其左右子树以前。
def pre_order(root): """前序遍历""" if root: # 若是不为空(递归条件) print(root.data, end=',') # 访问本身 pre_order(root.lchild) # 递归左子树 pre_order(root.rchild) # 递归右子树 pre_order(root) # E,A,C,B,D,G,F,
访问根节点的操做发生在遍历其左右子树之间。
def in_order(root): """中序遍历""" if root: in_order(root.lchild) # 递归左子树 print(root.data, end=',') # 访问本身 in_order(root.rchild) # 递归右子树 in_order(root) # A,B,C,D,E,G,F,
访问根节点的操做发生在遍历其左右子树以后。
def post_order(root): """后序遍历""" if root: post_order(root.lchild) # 递归左子树 post_order(root.rchild) # 递归右子树 print(root.data, end=",") # 访问本身 post_order(root) # B,D,C,A,F,G,E,
层次遍历很好理解,须要利用到队列。不只适用二叉树也适用多叉树。
用一个队列保存被访问的当前节点的左右孩子以实现层序遍历。
from collections import deque def level_order(root): """层次遍历""" queue = deque() queue.append(root) while len(queue) > 0: # 只要队不空 node = queue.popleft() # 出队 print(node.data, end=',') if node.lchild: queue.append(node.lchild) if node.rchild: queue.append(node.rchild) level_order(root) # E,A,G,C,F,B,D,
例如:前序遍历——EACBDGF;中序遍历——ABCDEGF。
由此可知E是根节点,E的左边包含ABCD,右边包含GF。且A是根节点的左节点、G是根节点的右节点。
BCD是A的子节点,因为中序遍历ABCD可知A的左节点是空的,右节点包含BCD,由前序ACBD可知C是A的右子节点。再由中序遍历BCD可知B是C的左节点,D是C的右节点。
GF是根节点右边节点,G是右节点,F是G的子节点。由中序GF可知F是G节点的右节点。至此推导出整个树。
二叉搜索树是一颗二叉树且知足性质:设x是二叉树的一个节点。若是y是x左子树的一个节点,那么y.key <= x.key;若是y是x右子树的一个节点,那么y.key >= x.key。
总结来讲:二叉搜索树的左子树不空,则左子树上全部节点的值均小于它的根节点的值;若它的右子树不空,则右子树上全部节点的值均大于它根节点的值;它的左右子树也都是二叉搜索树。
class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子 self.parent = None # 加了parent就是双链表 class BST: def __init__(self, li=None): self.root = None if li: for val in li: self.insert_no_rec(val) def insert(self, node, val): """ 递归插入 :param node: 节点 :param val: 要插入的值 :return: """ if not node: node = BiTreeNode(val) elif val < node.data: node.lchild = self.insert(node.lchild, val) node.lchild.parent = node elif val > node.data: node.rchild = self.insert(node.lchild,val) node.rchild.parent = node # else: # "=" else不用写了 return node def insert_no_rec(self, val): """非递归插入""" p = self.root if not p: # 空树的状况处理 self.root = BiTreeNode(val) return while True: if val < p.data: # 小于根节点往左边走 if p.lchild: # 若是左孩子存在 p = p.lchild else: # 左子树不存在 p.lchild = BiTreeNode(val) p.lchild.parent = p return elif val > p.data: # 大于根节点往右边走 if p.rchild: # 若是右孩子存在 p = p.rchild else: # 右子树不存在 p.rchild = BiTreeNode(val) p.rchild.parent = p return else: # 等于的时候,什么都不干(相似集合) return def pre_order(self, root): """前序遍历""" if root: # 若是不为空(递归条件) print(root.data, end=',') # 访问本身 self.pre_order(root.lchild) # 递归左子树 self.pre_order(root.rchild) # 递归右子树 def in_order(self, root): """中序遍历""" if root: self.in_order(root.lchild) # 递归左子树 print(root.data, end=',') # 访问本身 self.in_order(root.rchild) # 递归右子树 def post_order(self, root): """后序遍历""" if root: self.post_order(root.lchild) # 递归左子树 self.post_order(root.rchild) # 递归右子树 print(root.data, end=",") # 访问本身 tree = BST([4,6,7,9,2,1,3,5,8]) tree.pre_order(tree.root) print("") tree.in_order(tree.root) print("") tree.post_order(tree.root) """ 4,2,1,3,6,5,7,9,8, 1,2,3,4,5,6,7,8,9, # 注意中序是有序的 1,3,2,5,8,9,7,6,4, """
能够注意到中序遍历输出的是有序的,作以下验证:
import random li = list(range(500)) random.shuffle(li) tree = BST(li) tree.in_order(tree.root) # 0,1,2,3,4,5,...,496,497,498,499
这是由于二叉搜索树的性质致使二叉搜索树的左孩子必定是最小的,所以它的中序序列必定是升序的。
class BST: """代码省略""" def query(self, node, val): """ 递归查询 :param node: 要递归的节点 :param val: 要查询的值 :return: """ if not node: # 若是node是空,则找不到 return None # 递归终止条件 if val > node.data: # 大于node的值往右边找 return self.query(node.rchild, val) elif val < node.data: # 小于node的值往左边找 return self.query(node.lchild, val) else: return node # 值相同返回当前节点 def query_no_rec(self, val): """非递归查询""" p = self.root while p: # 若是树不为空 if p.data < val: # 大于p的值往右边找 p = p.rchild elif p.data > val: # 小于p的值往左边找 p = p.lchild else: return p return None # 树为空,递归终止条件 import random li = list(range(0, 500, 2)) random.shuffle(li) tree = BST(li) print(tree.query_no_rec(3)) # None print(tree.query_no_rec(6)) # <__main__.BiTreeNode object at 0x103d01cc0> print(tree.query_no_rec(6).data) # 6
操做方法是:直接删除
操做方法是:将此节点的父亲与孩子链接,而后删除该节点。
操做方法:将其右子树的最小节点(该节点最多有一个右孩子)删除,并替换当前节点。
class BiTreeNode: def __init__(self, data): self.data = data self.lchild = None # 左孩子 self.rchild = None # 右孩子 self.parent = None # 加了parent就是双链表 class BST: """代码省略""" def __remove_node_1(self, node): """状况1:node是叶子节点""" if not node.parent: # 此叶子节点没有父节点,说明树中就这一个节点 self.root = None # 将这惟一的节点删除 if node == node.parent.lchild: # node是父亲的左孩子 node.parent.lchild = None # 父亲与node断联系 node.parent = None # node与父亲断联系(这句可写可不写) else: # node是父亲的右孩子 node.parent.rchild = None # # 父亲与node断联系 def __remove_node_21(self, node): """状况2-1:node只有一个左孩子""" if not node.parent: # 若是node是根节点 self.root = node.lchild # 将node的左孩子置为根节点 node.lchild.parent = None # 将新根节点的父亲设为空 elif node == node.parent.lchild: # 若是node是它父亲的左孩子 node.parent.lchild = node.lchild # node父节点的左孩子设为node的左孩子 node.lchild.parent = node.parent # node左孩子的父节点设为node的父节点 else: # 若是node是它父亲的右孩子 node.parent.rchild = node.lchild # node父节点的右孩子指向node的左孩子 node.lchild.parent = node.parent # node左孩子的父亲指向node的父节点 def __remove_node_22(self, node): """状况2-2:node只有一个右孩子""" if not node.parent: # 若是node是根节点 self.root = node.rchild # 将node的右孩子置为根节点 node.rchild.parent = None # 将新根节点的父亲设为空 elif node == node.parent.lchild: # 若是node是父亲的左孩子 node.parent.lchild = node.rchild # 将node父节点的左孩子指向node的右孩子 node.rchild.parent = node.parent else: # 若是node是父亲的右孩子 node.parent.rchild = node.rchild # 将node父节点的右孩子指向node的右孩子 node.rchild.parent = node.parent def delete(self, val): if self.root: # 若是不是空树 node = self.query_no_rec(val) if not node: # 若是node不存在 return False if not node.lchild and not node.rchild: # 若是node是叶子节点 self.__remove_node_1(node) elif not node.rchild: # 若是没有右孩子(只有一个左孩子) self.__remove_node_21(node) elif not node.lchild: # 若是没有左孩子(只有一个右孩子) self.__remove_node_22(node) else: # 若是两个孩子都有 min_node = node.rchild while min_node.lchild: # 一直查找node右孩子的左子树的左孩子,直到没有为止 min_node = min_node.lchild node.data = min_node.data # 将min_node.data的值赋给node.data # 删除min_node if min_node.rchild: # 若是min_node只有右孩子 self.__remove_node_22(min_node) else: # 若是min_node没有孩子 self.__remove_node_1(min_node) tree = BST([1,4,2,5,3,8,6,9,7]) tree.in_order(tree.root) # 1,2,3,4,5,6,7,8,9, print("") tree.delete(4) tree.in_order(tree.root) # 1,2,3,5,6,7,8,9, print("") tree.delete(1) tree.delete(8) tree.in_order(tree.root) # 2,3,5,6,7,9,
平均状况下,二叉搜索树进行搜索的时间复杂度为O(logn)。
最坏状况下,二叉搜索树可能很是偏斜,时间复杂度退化到O(n)。以下所示:
解决方案:
(1)随机化的二叉搜索树(打乱顺序插入),有时是是否是插入的那打乱插入就很差用。
(2)AVL树
B树(B-Tree):B树是一棵自平衡的多路搜索树。经常使用于数据库的索引,最经常使用数据库的索引就是哈希表、B树。
以下所示,一个节点存了两个值,分红了三路。