搜索是在一个项目集合中找到一个特定项目的算法过程. 搜索通常的答案是真的或假的, 因为该项目是否存在. 搜索的几种常见方法: 顺序查找. 二分法查找, 二叉树查找, 哈希查找.
二分查找又称折半查找, 有点事比较次数少, 查找速度快, 平均性能好; 其缺点是要求待查表为有序表, 且插入删除困难. 因此, 二分查找法适用于不经常变动而查找频繁的有序列表.
⾸先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字⽐较,如果两者相等,则查找成功;否则利⽤中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进⼀步查找前一子表,否则进⼀步查找后一子表。重复以上过程,直到找到满⾜条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
# –*– coding: utf-8 –*–
# @Time : 2019/4/6 22:21
# @Author : Damon_duanlei
# @FileName : binary_search.py
# @BlogsAddr : https://blog.csdn.net/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.csdn.net/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) 个有限节点组成一个具有层次关系的集合. 把它叫做 “树” 是因为他看起来像一颗倒挂的树, 也就是说它是根朝上, 叶朝下的. 它具有以下的特点:
顺序存储: 将数据结构存储在固定的数组中, 虽然在遍历速度上有一定的优势, 但因所占空间比较大. 二叉树通常以链式存储.
链式存储:
由于对节点的个数无法掌握, 常见树的存储表示都转换成二叉树进行处理, 子节点个数最多为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.csdn.net/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.csdn.net/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