树(tree)是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具备树状结构性质的数据集合
它具备如下的特色:
①每一个节点有零个或多个子节点;
②没有父节点的节点称为根节点;
③每个非根节点有且只有一个父节点;
④除了根节点外,每一个子节点能够分为多个不相交的子树;
node
二叉树:每一个节点最多含有两个子树的树称为二叉树。
python
二叉树中一些专业术语:算法
节点的高度
:节点到叶子结点的最长路径,好比C节点的高度是2(L->F是1,F->C是2)节点的深度
:节点到根节点的所经历的边的个数好比C节点的高度是1(A->C,只有一条边,因此深度=1)节点的层
:节点的高度树的高度
:根节点的高度基于二叉树衍生的多种树型结构:数据库
满二叉树
:除最后一层无任何子节点外,每一层上的全部结点都有两个子结点。也能够这样理解,除叶子结点外的全部结点均有两个子结点。节点数达到最大值,全部叶子结点必须在同一层上
数据结构
彻底二叉树
:设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h 层全部的结点都连续集中在最左边,这就是彻底二叉树
app
满二叉树和彻底二叉树对比:
post
二叉查找树
: 也称二叉搜索树,或二叉排序树。其定义也比较简单,要么是一颗空树,要么就是具备以下性质的二叉树:
(1)若任意节点的左子树不空,则左子树上全部结点的值均小于它的根结点的值;
(2) 若任意节点的右子树不空,则右子树上全部结点的值均大于它的根结点的值;
(3) 任意节点的左、右子树也分别为二叉查找树;
(4) 没有键值相等的节点。性能
定义
: 平衡二叉搜索树,又被称为AVL树,且具备如下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,而且左右两个子树都是一棵平衡二叉树设计
平衡二叉树出现缘由
:
因为普通的二叉查找树会容易失去”平衡“,极端状况下,二叉查找树会退化成线性的链表,致使插入和查找的复杂度降低到 O(n) ,因此,这也是平衡二叉树设计的初衷。那么平衡二叉树如何保持”平衡“呢?根据定义,有两个重点,一是左右两子树的高度差的绝对值不能超过1,二是左右两子树也是一颗平衡二叉树。
指针
平衡二叉树的建立
:
平衡二叉树是一棵高度平衡的二叉查找树。因此,要构建跟维系一棵平衡二叉树就比普通的二叉树要复杂的多。在构建一棵平衡二叉树的过程当中,当有新的节点要插入时,检查是否因插入后而破坏了树的平衡,若是是,则须要作旋转去改变树的结构
avl树每次插入删除会进行大量的平衡度计算致使IO数量巨大而影响性能。因此出现了红黑树。一种二叉查找树,但在每一个节点增长一个存储位表示节点的颜色,能够是红或黑(非红即黑)
定义
:
红黑树有两个重要性质
:
一、红节点的孩子节点不能是红节点;
二、从根到叶子节点的任意一条路径上的黑节点数目同样多。
这两条性质确保该树的高度为logN,因此是平衡树。
优点
:
红黑树的查询性能略微逊色于AVL树,由于他比avl树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的avl树最多多一次比较,可是,红黑树在插入和删除上完爆avl树,avl树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所作的红黑变换和旋转的开销,相较于avl树为了维持平衡的开销要小得多
使用场景
:
定义
:
B树是为实现高效的磁盘存取而设计的多叉
平衡搜索树。(B树和B-tree这两个是同一种树)
产生缘由
:
B树是一种查找树,咱们知道,这一类树(好比二叉查找树,红黑树等等)最初生成的目的都是为了解决某种系统中,查找效率低的问题。
B树也是如此,它最初启发于二叉查找树,二叉查找树的特色是每一个非叶节点都只有两个孩子节点。然而这种作法会致使当数据量很是大时,二叉查找树的深度过深
,搜索算法自根节点向下搜索时,须要访问的节点也就变的至关多。
若是这些节点存储在外存储器中,每访问一个节点,至关于就是进行了一次I/O操做,随着树高度的增长,频繁的I/O操做必定会下降查询的效率。
定义
:
B树是一种平衡的多分树,一般咱们说m阶的B树,它必须知足以下条件:
全部叶子都出如今同一水平
,没有任何信息(高度一致)。特色
:
B+树是应文件系统所需而产生的B树的变形树
B+树有两种类型的节点:内部结点(也称索引结点)和叶子结点。内部节点就是非叶子节点,内部节点不存储数据,只存储索引,数据都存储在叶子节点。
内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的全部key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
每一个叶子结点都存有相邻叶子结点的指针,叶子结点自己依关键字的大小自小而大顺序连接
父节点存有右孩子的第一个元素的索引。
最核心的特色以下:
(1)多路非二叉
(2)只有叶子节点保存数据
(3)搜索时至关于二分查找
(4)增长了相邻接点的指向指针
B+树为何时候作数据库索引
:因为B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只须要扫一遍叶子结点便可,可是B树由于其分支结点一样存储着数据,咱们要找到具体的数据,须要进行一次中序遍历按序来扫。简单来讲就是:B+树查询某一个数据时扫描叶子节点便可;而B树须要中序遍历整个树,因此B+树更快。
为何说B+树比B树更适合数据库索引?
1)B+树的磁盘读写代价更低
B+树的内部结点并无指向关键字具体信息的指针。所以其内部结点相对B 树更小。若是把全部同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的须要查找的关键字也就越多。相对来讲IO读写次数也就下降了;
2)B+树查询效率更加稳定
因为非终结点并非最终指向文件内容的结点,而只是叶子结点中关键字的索引。因此任何关键字的查找必须走一条从根结点到叶子结点的路。全部关键字查询的路径长度相同,致使每个数据的查询效率至关;
3)B+树便于范围查询(最重要的缘由,范围查找是数据库的常态)
B树在提升了IO性能的同时并无解决元素遍历效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只须要去遍历叶子节点就能够实现整棵树的遍历。并且在数据库中基于范围的查询是很是频繁的,而B树不支持这样的操做或者说效率过低;
B树的范围查找用的是中序遍历,而B+树用的是在链表上遍历;
树的建立有不少种方式,分为迭代建立和递归建立。下面分别介绍这两种建立数的方式。
建立的树:
该建立方法是按照层次建立,第一层建立好以后第二层,第二层完成后建立第三层。
class Node(object): def __init__(self,value=-1,left=None,right=None): self.value = value self.left = left self.right = right class Tree(object): def __init__(self, root=None): self.root = root def insert(self,element): node = Node(element) if self.root == None: self.root = node else: queue = [] queue.append(self.root) while queue: cur = queue.pop(0) if cur.left == None: cur.left = node return elif cur.right == None: cur.right = node return else: queue.append(cur.left) queue.append(cur.right) def output(self, root): if root == None: return print(root.value) self.output(root.left) self.output(root.right) one = Tree() for i in range(10): one.insert(i) one.output(one.root)
该建立方式是递归建立,前提是将树的数据组织成一个彻底二叉树的形式
class Node(object): def __init__(self,value=None): self.value = value self.left = None self.right = None def create_two(index, length, arr): if index > length: return None node = Node(arr[index]) node.left = create_two(index*2+1, length, arr) node.right = create_two(index*2+2, length, arr) return node def BFS(root): queue = [root] while queue: cur = queue.pop(0) print(cur.value) if cur.left: queue.append(cur.left) if cur.right: queue.append(cur.right) arr = [1,2,3,4,None,None,None,None,None] length = len(arr) -1 head = create_two(0,length, arr) print(head.value) print(head.left) print(head.right) BFS(head)
树的遍历方式有不少种,能够分为五类:
实现遍历的方式中有能够分为递归和迭代
class TreeNode(object): def __init__(self,value=None): self.value = value self.left = None self.right = None class Tree(object): def __init__(self): self.root = TreeNode(None) self.arr = [] def create(self,value): if self.root.value is None: self.root = TreeNode(value) else: queue = [self.root] while queue: node = queue.pop(0) if node.left: queue.append(node.left) else: node.left = TreeNode(value) return if node.right: queue.append(node.right) else: node.right = TreeNode(value) return # 递归、前序遍历 def preorder(self,root): if root is None: return self.arr.append(root.value) self.preorder(root.left) self.preorder(root.right) # 递归、中序遍历 def inorder(self,root): if root is None: return self.inorder(root.left) self.arr.append(root.value) self.inorder(root.right) # 递归、后序遍历 def postorder(self,root): if root is None: return self.postorder(root.left) self.postorder(root.right) self.arr.append(root.value) # 迭代、前序遍历 def preorder_two(self,root): stack = [root] arr = [] while stack: cur = stack.pop() arr.append(cur.value) if cur.right: stack.append(cur.right) if cur.left: stack.append(cur.left) print(arr) # 迭代、后序遍历 def postorder_two(self,root): stack = [root] arr = [] while stack: cur = stack.pop() arr.append(cur.value) if cur.left: stack.append(cur.left) if cur.right: stack.append(cur.right) print(arr[::-1]) # 迭代、中序遍历 def inorder_two(self,root): cur = root stack = [] arr = [] while cur or stack: while cur: stack.append(cur) cur = cur.left node = stack.pop() arr.append(node.value) cur = node.right print(arr) # 层次遍历 def levelorder(self,root): queue = [root] arr = [] while queue: cur = queue.pop(0) arr.append(cur.value) if cur.left: queue.append(cur.left) if cur.right: queue.append(cur.right) print(arr) # 子数遍历,返回从根节点到每个叶子节点的一条路径 # 子数遍历,返回从根节点到每个叶子节点的一条路径 def zishu(self,root,arr): if not root.left and not root.right: print(arr) return if root.left: self.zishu(root.left, arr + [root.left.value]) if root.right: self.zishu(root.right, arr + [root.right.value]) tree = Tree() for i in range(10): tree.create(i) print('--------------------递归--------------------------') tree.preorder(tree.root) print(tree.arr) tree.arr = [] tree.inorder(tree.root) print(tree.arr) tree.arr = [] tree.postorder(tree.root) print(tree.arr) print('--------------------迭代--------------------------') tree.preorder_two(tree.root) tree.inorder_two(tree.root) tree.postorder_two(tree.root) print('--------------------层次--------------------------') tree.levelorder(tree.root) print('--------------------子数--------------------------') tree.arr = [] tree.zishu(tree.root, [tree.root.value]) print(tree.arr)