二分搜索 树结构 python描述
搜索是在一个项目集合中找到一个特定项目的算法过程. 搜索通常的答案是真的或假的, 因为该项目是否存在. 搜索的几种常见方法: 顺序查找. 二分法查找, 二叉树查找, 哈希查找.
二分法查找
二分查找又称折半查找, 有点事比较次数少, 查找速度快, 平均性能好; 其缺点是要求待查表为有序表, 且插入删除困难. 因此, 二分查找法适用于不经常变动而查找频繁的有序列表.
二分法查找步骤
⾸先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字⽐较,如果两者相等,则查找成功;否则利⽤中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进⼀步查找前一子表,否则进⼀步查找后一子表。重复以上过程,直到找到满⾜条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
代码实现
非递归实现
# –*– coding: utf-8 –*–
# @Time : 2019/4/6 22:21
# @Author : Damon_duanlei
# @FileName : binary_search.py
# @BlogsAddr : https://blog.youkuaiyun.com/Damon_duanlei
def binary_search(find_list, search_info):
"""
二分法非递归实现
:param find_list: 查询列表
:param search_info: 查找元素
:return: Ture or False
"""
low = 0
high = len(find_list) - 1
while low <= high:
mid = (low + high) // 2
if search_info == find_list[mid]:
return mid
if search_info > find_list[mid]:
low = mid + 1
else:
high = mid - 1
return None
递归实现
# –*– coding: utf-8 –*–
# @Time : 2019/4/6 22:21
# @Author : Damon_duanlei
# @FileName : binary_search.py
# @BlogsAddr : https://blog.youkuaiyun.com/Damon_duanlei
def binary_search2(find_list, search_info):
"""
二分法递归实现
:param find_list: 查询列表
:param search_info: 查找元素
:return: Ture or False
"""
n = len(find_list) // 2
if find_list[n] == search_info:
return True
elif search_info > find_list[n]:
return binary_search2(find_list[n:], search_info)
else:
return binary_search2(find_list[:n], search_info)
注意:经过一定次数的测试, 非递归方式的二分法查找效率明显高于递归方式的二分法查找.
树与树算法
树的概念
树 (英语 tree ) 是一种抽象数据类型 (ADT) 或是视作这种抽象数据类型的数据结构, 用来模拟就有树状结构性质的数据集合. 它是由 n (n >= 1) 个有限节点组成一个具有层次关系的集合. 把它叫做 “树” 是因为他看起来像一颗倒挂的树, 也就是说它是根朝上, 叶朝下的. 它具有以下的特点:
- 每个节点有零个或多个子节点;
- 没有父节点的节点称为根节点;
- 每一个非跟节点有且只有一个父节点;
- 除了根节点外, 每个子节点可以分为多个不想交的子树;
树的术语
- 节点的度: 一个节点含有的子树的个数称为该节点的度;
- 树的度: 一棵树中, 最大的节点的度称为树的度;
- 叶节点或终端节点: 度为零的节点;
- 父亲节点或父节点: 若一个节点含有子节点, 则这个节点成为其子节点的父节点;
- 孩子节点或子节点: 一个节点含有的子树的跟节点称该节点的子节点;
- 兄弟节点: 具有相同父节点的节点互称为兄弟节点;
- 节点的层次: 从根开始定义起, 根为第一层,根的子节点为第二次, 以此类推;
- 树的高度或深度: 树中节点的最大层次;
- 堂兄弟节点: 父节点在同一层的节点互为堂兄弟;
- 节点的祖先: 从根到该节点所经分支上的所有节点;
- 子孙: 以某节点为根的子树中任一节点都称为该节点的子孙
- 森林: 由 m (m >=0 )棵互不相交的树的集合称为森林
树的种类
- 无序树: 树中任意节点的子节点之间没有顺序关系, 这种树称为无序树, 也称自由树;
- 有序树: 树中任一节点的子节点之间的顺序关系, 这种树称为有序树;
- 二叉树: 每个节点最多含有两个子树的树称为二叉树;
- 完全二叉树: 对于一颗二叉树, 假设其深度为 d (d > 1). 除了第 d 层外, 其他各层的节点数目均已达到最大值, 且第 d 层所有节点从左向右连续地紧密排列, 这样的二叉树被称为完全二叉树, 其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
- 平衡二叉树(AVL树): 当且仅当人和街店的两个子树的高度差不大于 1 的二叉树;
- 排序二叉树 (二叉查找树, 也称 二叉搜索树,有序二叉树)
- 霍夫曼树 (用于信息编码): 带权路径最短的二叉树称为霍夫曼树或最优二叉树
- B树: 一种对读写操作进行优化的平衡的二叉查找树, 能够保持数据有序, 拥有多与两个子树.
- 二叉树: 每个节点最多含有两个子树的树称为二叉树;
树的存储与表示
顺序存储: 将数据结构存储在固定的数组中, 虽然在遍历速度上有一定的优势, 但因所占空间比较大. 二叉树通常以链式存储.
链式存储:
由于对节点的个数无法掌握, 常见树的存储表示都转换成二叉树进行处理, 子节点个数最多为2
二叉树
二叉树的基本概念
二叉树是每个节点最多有两个子树的树结构. 通常子树被称作 “左子树” 和 “右子树”
二叉树的性质(特性)
性质1: 在二叉树的第 i 层上, 至多有2^(i-1) 个结点 ( i > 0 )
性质2: 深度为 K 的二叉树至多有 2^k -1个结点 ( K>0 )
性质3: 对于任意一颗二叉树, 如果其叶节点树为 N0, 而度数为2的结点总数为 N2, 则 N0 = N2 + 1
性质4: 具有 n 个结点的完全二叉树的深度必为 log2(n + 1)
性质5: 对完全二叉树, 若从上至下, 从左至右编号, 则编号为 i 的结点, 其左孩子编号必为 2i, 其右孩子编号必为 2i+1; 其双亲的编号必为 i/2 (根节点除外)
完全二叉树
若二叉树的高度为h ,除第 h 层外, 其它各层的结点数都达到最大个数, 第 h 层有叶子节点, 且叶子结点都是从左到右依次排布, 这就是完全二叉树
满二叉树
除了叶结点外, 每个结点都有左右子叶, 且左右叶结点都处在最底层的二叉树
二叉树的节点表示以及树的创建
# –*– coding: utf-8 –*–
# @Time : 2019/4/23 16:48
# @Author : Damon_duanlei
# @FileName : binary_tree_01.py
# @BlogsAddr : https://blog.youkuaiyun.com/Damon_duanlei
class Node(object):
"""节点类
通过使用Node类中定义三个属性,分别为elem本身的值,还有lchild左孩子
和rchild右孩子"""
def __init__(self, elem, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class BinaryTree(object):
"""树的创建,创建一个树的类,并给一个root根节点,一开始为空,随后添加节点"""
def __init__(self, root=None):
self.root = root
def add(self, item):
if self.root is None:
self.root = Node(item)
else:
# 使用广度优先遍历的方式查找到有子节点为None的节点
# 定义一个空列表 使用队列的方式添加节点对象
queue = list()
# 首先将root 节点添加到queue
queue.append(self.root)
while queue: # 通过循环的方式必然可以通过下方return方式退出循环,此处为了方便理解未使用 while True
node = queue.pop(0)
if node.lchild is None:
node.lchild = Node(item)
return
elif node.rchild is None:
node.rchild = Node(item)
return
# 将当前节点的左右两个子节点添加到队列
else:
queue.append(node.lchild)
queue.append(node.rchild)
二叉树的遍历
树的遍历是树的一种重要的运算. 所谓遍历是指对树中所有结点的信息的访问, 即依次对树中每个结点访问一次且金访问一次, 这种对多有节点的访问称为遍历 (traversal) . 树的两种重要的遍历模式是深度优先遍历和广度优先遍历, 深度优先一般使用递归, 广度优先一般使用队列. 一般情况下能用递归实现的算法发部分也能用堆栈来实现.
深度优先遍历
对于一颗二叉树, 深度优先搜索 (Depth First Search) 是沿着树的深度遍历树的节点, 尽可能深的搜索树的分支.
深度遍历有三种重要的方法. 这三种方式常被用于访问树的节点, 他们之间的不同在于访问节点的次序不同. 这三种遍历分别叫做 先序遍历(preorder), 中序遍历 (inorder), 后续遍历(postorder).
- 先序遍历: 在先序遍历中,先访问跟节点, 然后递归使用先序遍历访问左子树, 再递归使用先序遍历访问右子树
- 中序遍历: 在中序遍历中, 递归使用中序遍历访问左子树, 然后访问根结点, 最后再递归使用中序遍历访问右子树
- 后续遍历: 在后续遍历中, 首先使用后续遍历访问左子树和右子树, 最后访问根结点.
广度优先遍历(层次遍历)
从树的 root 开始, 从上到下, 从左到右遍历整个树的节点.
实现代码:
# –*– coding: utf-8 –*–
# @Time : 2019/4/23 16:48
# @Author : Damon_duanlei
# @FileName : binary_tree_01.py
# @BlogsAddr : https://blog.youkuaiyun.com/Damon_duanlei
class Node(object):
"""节点类
通过使用Node类中定义三个属性,分别为elem本身的值,还有lchild左孩子
和rchild右孩子"""
def __init__(self, elem, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class BinaryTree(object):
"""树的创建,创建一个树的类,并给一个root根节点,一开始为空,随后添加节点"""
def __init__(self, root=None):
self.root = root
def add(self, item):
if self.root is None:
self.root = Node(item)
else:
# 使用广度优先遍历的方式查找到有子节点为None的节点
# 定义一个空列表 使用队列的方式添加节点对象
queue = list()
# 首先将root 节点添加到queue
queue.append(self.root)
while queue: # 通过循环的方式必然可以通过下方return方式退出循环,此处为了方便理解未使用 while True
node = queue.pop(0)
if node.lchild is None:
node.lchild = Node(item)
return
elif node.rchild is None:
node.rchild = Node(item)
return
# 将当前节点的左右两个子节点添加到队列
else:
queue.append(node.lchild)
queue.append(node.rchild)
def breadth_travel(self):
"""广度优先遍历"""
if self.root is None:
return None
queue = list()
queue.append(self.root)
while queue:
cur_node = queue.pop(0)
print(cur_node.elem, end=",")
if cur_node.lchild:
queue.append(cur_node.lchild)
if cur_node.rchild:
queue.append(cur_node.rchild)
def preorder(self, root):
"""先序遍历"""
if root is None:
return
print(root.elem, end=",")
self.preorder(root.lchild)
self.preorder(root.rchild)
def inorder(self, root):
"""中序遍历"""
if root:
self.inorder(root.lchild)
print(root.elem, end=",")
self.inorder(root.rchild)
def postorder(self, root):
"""行后续遍历"""
if root:
self.postorder(root.lchild)
self.postorder(root.rchild)
print(root.elem, end=",")
if __name__ == '__main__':
tree = BinaryTree()
tree.add(0)
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.add(5)
tree.add(6)
tree.add(7)
tree.add(8)
tree.add(9)
print("广度遍历:")
tree.breadth_travel()
print("")
print("先序遍历:")
tree.preorder(tree.root)
print("")
print("中序遍历:")
tree.inorder(tree.root)
print("")
print("后续遍历:")
tree.postorder(tree.root)
运行结果:
广度遍历:
0,1,2,3,4,5,6,7,8,9,
先序遍历:
0,1,3,7,8,4,9,2,5,6,
中序遍历:
7,3,8,1,9,4,0,5,2,6,
后续遍历:
7,8,3,9,4,1,5,6,2