二分搜索 树结构 python描述

二分搜索 树结构 python描述

搜索是在一个项目集合中找到一个特定项目的算法过程. 搜索通常的答案是真的或假的, 因为该项目是否存在. 搜索的几种常见方法: 顺序查找. 二分法查找, 二叉树查找, 哈希查找.

二分法查找

二分查找又称折半查找, 有点事比较次数少, 查找速度快, 平均性能好; 其缺点是要求待查表为有序表, 且插入删除困难. 因此, 二分查找法适用于不经常变动而查找频繁的有序列表.

二分法查找步骤

⾸先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字⽐较,如果两者相等,则查找成功;否则利⽤中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进⼀步查找前一子表,否则进⼀步查找后一子表。重复以上过程,直到找到满⾜条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

1554560329989

代码实现

非递归实现

# –*– 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) 个有限节点组成一个具有层次关系的集合. 把它叫做 “树” 是因为他看起来像一颗倒挂的树, 也就是说它是根朝上, 叶朝下的. 它具有以下的特点:

  • 每个节点有零个或多个子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非跟节点有且只有一个父节点;
  • 除了根节点外, 每个子节点可以分为多个不想交的子树;

树的术语

  • 节点的度: 一个节点含有的子树的个数称为该节点的度;
  • 树的度: 一棵树中, 最大的节点的度称为树的度;
  • 叶节点或终端节点: 度为零的节点;
  • 父亲节点或父节点: 若一个节点含有子节点, 则这个节点成为其子节点的父节点;
  • 孩子节点或子节点: 一个节点含有的子树的跟节点称该节点的子节点;
  • 兄弟节点: 具有相同父节点的节点互称为兄弟节点;
  • 节点的层次: 从根开始定义起, 根为第一层,根的子节点为第二次, 以此类推;
  • 树的高度或深度: 树中节点的最大层次;
  • 堂兄弟节点: 父节点在同一层的节点互为堂兄弟;
  • 节点的祖先: 从根到该节点所经分支上的所有节点;
  • 子孙: 以某节点为根的子树中任一节点都称为该节点的子孙
  • 森林: 由 m (m >=0 )棵互不相交的树的集合称为森林

树的种类

  • 无序树: 树中任意节点的子节点之间没有顺序关系, 这种树称为无序树, 也称自由树;
  • 有序树: 树中任一节点的子节点之间的顺序关系, 这种树称为有序树;
    • 二叉树: 每个节点最多含有两个子树的树称为二叉树;
      • 完全二叉树: 对于一颗二叉树, 假设其深度为 d (d > 1). 除了第 d 层外, 其他各层的节点数目均已达到最大值, 且第 d 层所有节点从左向右连续地紧密排列, 这样的二叉树被称为完全二叉树, 其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
      • 平衡二叉树(AVL树): 当且仅当人和街店的两个子树的高度差不大于 1 的二叉树;
      • 排序二叉树 (二叉查找树, 也称 二叉搜索树,有序二叉树)
    • 霍夫曼树 (用于信息编码): 带权路径最短的二叉树称为霍夫曼树或最优二叉树
    • B树: 一种对读写操作进行优化的平衡的二叉查找树, 能够保持数据有序, 拥有多与两个子树.

树的存储与表示

顺序存储: 将数据结构存储在固定的数组中, 虽然在遍历速度上有一定的优势, 但因所占空间比较大. 二叉树通常以链式存储.

1554622187248

链式存储:

1554622289730

由于对节点的个数无法掌握, 常见树的存储表示都转换成二叉树进行处理, 子节点个数最多为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 层有叶子节点, 且叶子结点都是从左到右依次排布, 这就是完全二叉树

1554623116192

满二叉树

除了叶结点外, 每个结点都有左右子叶, 且左右叶结点都处在最底层的二叉树

二叉树的节点表示以及树的创建

# –*– 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

你可能感兴趣的:(自学总结,算法,树,二分法)