`

二叉树基础-定义、创建、遍历、属性计算(深度,结点数)、查找算法

阅读更多
一、概念和定义   
    在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

    二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。

    一棵深度为k,且有2^k-1个节点称之为满二叉树;深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。

二、基本术语
    1、结点的度和树的度
       树中每个结点具有的非空子树数或者说后继结点数被定义为该结点的度。树中所有结点的度的最大值被定义为该树的度
    2、分支节点和叶子结点
       度为0的为叶子结点或终端结点,度大于0的为分支节点。
    3、孩子结点、双亲结点、兄弟结点。
       在一棵树中,每个结点的子树的根或者说每个结点的后继,称为该结点的孩子,该结点称为孩子结点的双亲结点,具有同一个双亲结点的结点互称兄弟。结点的所有子树中的结点称为该结点的子孙。
    4、结点的层数和深度
三、二叉树的存储结构
1、顺序存储结构  顾名思义,以各结点编号为下标,把结点值对应存储到一维数组中。
   这种方式的好处,查找速度快,但是只适用于存放完全二叉树,对于存放单支结点比较多的二叉树会有很多存储空间空闲,浪费存储空间。这种结构适合存放完全二叉树或者单支结点较少的二叉树。
2、链式存储结构
   链表中的每个结点设置4个域:值域,左指针域,右指针域,双亲指针域。这样的结构即便于向上查找双亲结点,也方便查找该结点的孩子结点,缺点就是存储空间要比顺序存储结构大。
   结构定义代码实现:
package com.data.tree.po;

/**
 * 二叉树结点:数组元素结点
 * Created by xiaoyun on 2016/7/18.
 */
public class ArrayBTreeNode {
    private Object element;
    int left, right;
    public ArrayBTreeNode(Object obj) {
        this.element = obj;
    }
    public ArrayBTreeNode(Object obj, int lt, int rt) {
        this.element = obj;
        left = lt;
        right = rt;
    }
}

package com.data.tree.po;

/**
 * 二叉树节点:链式存储结构
 * Created by xiaoyun on 2016/7/18.
 */
public class BTreeNode {
    public Object element;
    public BTreeNode left,right;

    public BTreeNode(Object obj) {
        this.element = obj;
        left = right = null;
    }

    public BTreeNode(Object obj, BTreeNode lt, BTreeNode rt) {
        this.element = obj;
        this.left = lt;
        this.right = rt;
    }
}




四、常用算法及实现
1、根据二叉树的抽象数据类型,在Java中对应的接口定义为
package com.data.tree.po;

/**
 * 二叉树接口(标准行为规范定义)
 * Created by xiaoyun on 2016/7/19.
 */
public interface BinaryTree {
    // 由mode数组提供遍历二叉树的4中不同的访问次序(前序,中序,后续,层级)
    final String[] mode = {"preOrder", "inOrder", "postOrder", "levelOrder"};
    // 根据二叉树的广义表表示在计算机中建立对应的二叉树
    boolean createBTree(String gt);
    // 判断二叉树是否为空 为空,返回true,不为空,返回false
    boolean isEmpty();
    // 按照字符串s所给的次序遍历一棵二叉树,每个结点均被访问一次
    void traverseBTree(String s);
    // 从二叉树中查找值为obj的结点,若存在则返回完整值,否则返回空
    Object findBTree(Object obj);
    // 求出一棵二叉树的深度
    int depthBTree();
    // 求出一棵二叉树的结点数
    int countBTree();
    // 按照树的一种表示方法输出一棵二叉树
    void printBTree();
    // 清除二叉树的所有结点,使之变为一棵空树
    void clearBTree();
}

2、链接存储的二叉树定义
package com.data.tree.po;

import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringStack;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Stack;

/**
 * 链接二叉树(链接存储)
 * Created by admin on 2016/7/19.
 */
public class LinkBinaryTree implements BinaryTree {
    // 定义根节点
    protected BTreeNode root;

    // 无参数构造器
    public LinkBinaryTree() {
        this.root = null; // 二叉树的结点为空,即把空值null献给root
    }

    /**
     * 将一个二叉树的跟的引用赋值给root
     * @param root
     */
    public LinkBinaryTree(BTreeNode root) {
        this.root = root;
    }

    /**
     * 先序遍历
     * @param root
     */
    private void preOrder(BTreeNode root) {
        if (root != null) {
            System.out.print(root.element + " "); // 访问根节点
            preOrder(root.left); // 先序遍历左子树
            preOrder(root.right); // 先序遍历右子树
        }
    }

    /**
     * 中序遍历
     * @param root
     */
    private void inOrder(BTreeNode root) {
        if(root != null) {
            inOrder(root.left);
            System.out.print(root.element + " ");
            inOrder(root.right);
        }
    }

    /**
     * 后序遍历
     * @param root
     */
    private void postOrder(BTreeNode root) {
        if(root != null) {
            postOrder(root.left);
            postOrder(root.right);
            System.out.print(root.element + " ");
        }
    }

    /**
     * 层次遍历
     * @param root
     * 访问每个结点时,都需要经过入队和出队以及输出节点值的操作,这些操作的时间复杂度均为O(1),所以整个算法的时间复杂度为O(n)
     */
    private void levelOrder(BTreeNode root) {
        Queue queue = new ArrayDeque();
        BTreeNode p = null;
        queue.offer(root);// 将该树的根节点入队
        while (!queue.isEmpty()) {
            p = (BTreeNode) queue.poll();// 获取并移除该队列的头
            System.out.print(p.element + " ");
            if(p.left != null) {
                queue.offer(p.left);
            }
            if(p.right != null) {
                queue.offer(p.right);
            }
        }
    }

    /**
     * 查找的递归算法
     * @param root
     * @param x
     * @return
     * 思路:类似先序遍历
     *       1、如果树为空那么直接返回空
     *       2、比较根结点,如果相等直接返回根结点
     *       3、递归左子树,如果查找成功则返回结点的值
     *       4、递归查找右子树,如果查找成功则返回结点的值
     *       5、左右子树均为找到则返回null,查找失败
     */
    private Object findBTree(BTreeNode root, Object x) {
        if(root == null) {
            return null;
        }else {
            if(root.element.equals(x)) {
                return root.element;
            }else {
                Object target;
                target = findBTree(root.left, x);
                if(target != null) {
                    return target;
                }
                target = findBTree(root.right, x);
                if(target != null) {
                    return target;
                }
                return null;
            }
        }
    }

    /**
     * 求出二叉树深度的递归算法
     * @param root
     * @return
     * 思路:若一一棵二叉树为空,那么它的深度为0,否则它的深度等于左子树和右子树中的最大深度+1
     */
    private int depthBTree(BTreeNode root) {
        if(root == null) {
            return 0;
        }else {
            int dep1 = depthBTree(root.left);
            int dep2 = depthBTree(root.right);
            if(dep1 > dep2) {
                return dep1 + 1;
            }else {
                return dep2 + 1;
            }
        }
    }

    /**
     * 计算二叉树节点总数
     * @param root
     * @return
     * 思路:如果一棵二叉树为空,那么结点数为0,否则它等于左子树中的结点数与右子树中的结点数之和再加1
     */
    private int countBTree(BTreeNode root) {
        if(root == null){
            return 0;
        }else {
            int countLt = countBTree(root.left);
            int countGt = countBTree(root.right);
            return countLt + countGt + 1;
        }
    }

    /**
     * 输出二叉树广义表的递归算法
     * 说明:以广义表的形式输出一棵二叉树,应该首先输出根结点,然后再依次输出它的左子树和右子树,
     *       在输出左子树和之前答应左括号,输出右子树后打印右括号,具体广义表的输出规则,请查看数据结构之二叉树广义表的表示
     * @param root
     */
    private void printBTree(BTreeNode root) {
        if(root != null) {
            System.out.print(root.element);
            if (root.left != null || root.right != null) {
                System.out.print("(");
                printBTree(root.left);
                if(root.right != null) {
                    System.out.print(",");
                }
                printBTree(root.right);
                System.out.print(")");
            }
        }
    }

    /**
     * 建立二叉树的存储结构的方法
     * @param str
     * @return
     * 该算法的时间复杂度为O(n),n表示二叉树广义表中字符的个数,由于平均每两至三个字符之中就要一个元素字符,
     * 所以n也可以看做二叉树中元素结点的个数
     */
    public boolean createBTree(String str) {
        Stack stack = new Stack(); // 定义和创建一个保存结点指针的栈
        this.root = null; // 把树根指针置为空,即从空树开始
        BTreeNode p = null; // 定义p为指向二叉树结点的指针
        int k = 1;  // 用k作为处理结点的左子树和右子树的标记
        char[] a = str.toCharArray(); // 将字符串转换为字符数组
        for(int i = 0; i < a.length; i++) {
            switch (a[i]) {
                case ' ':
                    break;
                case '(':               // 处理左括号
                    stack.push(p);
                    k = 1;
                    break;
                case ')':              // 处理右括号
                    if(stack.isEmpty()){
                        System.out.println("二叉树广义表字符串错,返回假");
                        return false;
                    }
                    stack.pop();
                    break;
                case ',':
                    k = 2;
                    break;
                default:              // 扫描到的为字母,其他非法字符
                    if(a[i] >= 'a' && a[i] <= 'z' || a[i] >= 'A' && a[i] <= 'Z') {
                        p = new BTreeNode(a[i]);
                        if(root == null) {
                            root = p; // 将p结点定义为树根
                        }else { // 链接到双亲结点
                            if(k == 1) {
                                ((BTreeNode)stack.peek()).left = p;
                            }else {
                                ((BTreeNode)stack.peek()).right = p;
                            }
                        }
                    }else {
                        System.out.println("二叉树广义表中存在非法字符,返回假!");
                    }
            }
        }
        if(stack.isEmpty()) {
            return true;
        }else {
            return false;
        }
    }

    public boolean isEmpty() {
        return this.root == null;
    }

    /**
     * 按照一定的次序访问二叉树,使得每个结点均被访问一次
     * @param s
     */
    public void traverseBTree(String s) {
        if(s.equals(mode[0])){
            preOrder(root);
        }else if(s.equals(mode[1])) {
            inOrder(root);
        }else if(s.equals(mode[2])) {
            postOrder(root);
        }else if(s.equals(mode[3])) {
            levelOrder(root);
        }
        System.out.println(); // 输出换行
    }

    public Object findBTree(Object obj) {
        return findBTree(root, obj);
    }

    public int depthBTree() {
        return depthBTree(root);
    }

    public int countBTree() {
        return countBTree(root);
    }

    public void printBTree() {
        printBTree(root);
    }

    public void clearBTree() {
        root = null;
    }
}

3、测试类及运行结果
import com.data.tree.po.BinaryTree;
import com.data.tree.po.LinkBinaryTree;

/**
 * Created by admin on 2016/7/23.
 * 二叉树测试算法
 */
public class Example6_1 {
    public static void main(String[] args) {
        // 1、定义并创建链接存储结构的一棵空二叉树bt
        BinaryTree bt = new LinkBinaryTree();
        // 2、初始化二叉树广义表字符串
        String s = "A(B(C,D),E(,F(G)))";
        //String s = "a(b(c),d(e(f,g),h(,i)))";
        // 3、根据字符串建立链式存储结构的二叉树
        boolean yn = bt.createBTree(s);
        if (!yn) {
            System.out.println("表示二叉树的广义表字符串有误,请修正后在执行,错误的字符串s:" + s);
            System.exit(0);
        }
        // 4、广义表输出该二叉树
        System.out.println("二叉树的广义表表示形式:");
        bt.printBTree();
        // 5、前序遍历
        System.out.println("前序遍历:");
        bt.traverseBTree(BinaryTree.mode[0]);
        // 6、中序遍历
        System.out.println("中序遍历:");
        bt.traverseBTree(BinaryTree.mode[1]);
        // 7、后序遍历
        System.out.println("后序遍历:");
        bt.traverseBTree(BinaryTree.mode[2]);
        // 8、层级遍历
        System.out.println("层级遍历:");
        bt.traverseBTree(BinaryTree.mode[3]);
        // 9、求二叉树的深度
        System.out.println("计算该二叉树的深度:" + bt.depthBTree());
        // 10、计算该二叉树的结点数
        System.out.println("该二叉树的结点数:" + bt.countBTree());
        // 11、查找并输出二叉树中值为'C'和'F'的结点
        System.out.println("查找结点:" + bt.findBTree('c') + " " + bt.findBTree('F'));
        // 12、清空该二叉树
        bt.clearBTree();
    }
}

运行结果为:

二叉树的广义表表示形式:
A(B(C,D),E(,F(G)))前序遍历:
A B C D E F G
中序遍历:
C B D A E G F
后序遍历:
C D B G F E A
层级遍历:
A B E C D F G
计算该二叉树的深度:4
该二叉树的结点数:7
查找结点:null F

总结:本周学习和温习了二叉树的性质,定义,属性,基本术语,存储结构,常用算法。

忙碌的一周,导致今天才把二叉树的基本算法写完和理解。
二叉树的应用场景:搜索、索引、查询、排序等。
明天将学习二叉搜索树和堆。
下周计划:哈夫曼树、平衡二叉树,B树,红黑树了解,以及二叉树在软件、互联网和数据库中的应用。
分享到:
评论

相关推荐

    树和二叉树-层序遍历二叉树 

    树和二叉树-层序遍历二叉树 在计算机科学中,树和二叉树...层序遍历二叉树是树和二叉树的重要概念,通过这个例子,我们可以学习到如何使用链表存储二叉树,并使用队列完成层序遍历算法,最后将结果打印在终端屏幕上。

    c语言 二叉树应用:创建、递归非递归遍历、计算结点、分支、交换子树

    非递归中序遍历二叉树(算法2): 层次遍历二叉树: 递归计算单分支结点: 递归计算双分支结点: 递归计算叶子数: 二叉数的深度: 交换二叉树的左右子树: 二叉树已左右交换。 递归先序遍历二叉树: 递归...

    二叉树的遍历与求深度以及结点数

    在探讨二叉树的遍历方法及其深度与结点数的计算之前,我们首先简要回顾一下二叉树的基本定义。二叉树是一种特殊的树结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树具有以下特点: 1. **...

    数据结构课程设计报告-二叉树的遍历.docx

    本报告基于二叉树的遍历方法,旨在通过递归和非递归两种方法创建一棵二叉树,并对其进行先序遍历、中序遍历、后序遍历及层次遍历,并求出该二叉树的深度和叶子结点数。同时,报告还实现了查找功能,能够输入一个结点...

    先序创建二叉树并遍历计算节点数

    先序创建二叉树并遍历计算节点数 先序创建二叉树并遍历计算节点数

    二叉树的建立和遍历算法

    二叉树的建立和遍历算法 数据结构课程设计用

    定义二叉树的结点结构 实现先序序列构造二叉树的算法 实现先序遍历这棵二叉树,输出每个结点的值的算法 利用先序遍历,统计叶子结点的

    定义二叉树的结点结构 实现先序序列构造二叉树的算法 实现先序遍历这棵二叉树,输出每个结点的值的算法 利用先序遍历,统计叶子结点的个数 利用后序遍历,求二叉树的深度

    二叉树的创建以及遍历

    根据给定的信息,本文将详细介绍二叉树的基本概念、链表结构表示以及四种常见的遍历方式:中序遍历、先序遍历、后序遍历和层序遍历。 ### 二叉树的基本概念 二叉树是一种非常重要的非线性数据结构,它在计算机科学...

    二叉树遍历-前序中序后序层次遍历

    本文将深入探讨四种常见的二叉树遍历方法:前序遍历、中序遍历、后序遍历以及层次遍历,并以C++编程语言为例,阐述如何实现这些遍历算法。 1. **前序遍历**(Preorder Traversal) 前序遍历的顺序是根节点 -&gt; 左...

    二叉树的建立及遍历

    - 分别使用前序遍历和中序遍历算法遍历新构建的二叉树,将其结果与给定的序列比较。如果两者匹配,则证明构造正确。 3. **后序遍历**: - 后序遍历的顺序是左子树 -&gt; 右子树 -&gt; 根节点。同样可以使用递归方法实现...

    二叉树遍历实验报告

    ### 二叉树遍历实验报告知识点概览 #### 一、实验背景及目标 **实验背景:** 本次实验属于《数据结构》课程的一部分,旨在通过实际编程加深学生对二叉树这一数据结构的理解和应用能力。二叉树作为一种基本且重要的...

    构造二叉树与遍历二叉树

    ### 构造二叉树与遍历二叉树 ...总之,通过上述分析我们可以了解到,二叉树不仅是一种重要的数据结构,也是许多高级数据结构的基础,掌握好二叉树的构造与遍历方法对于进一步学习更复杂的算法和数据结构至关重要。

    二叉树三种遍历算法算法实现

    本文将详细介绍二叉树的三种基本遍历算法:前序遍历、中序遍历和后序遍历,并通过给定的代码示例进行深入解析。 ### 1. 二叉树的基本概念 二叉树是一种每个节点最多有两个子节点的树结构,这两个子节点分别被称为...

    二叉树建树、遍历、秋叶子结点数等功能的实现

    在IT领域,二叉树是一种基础且重要的数据结构,它由根节点、左右子树构成,每个节点最多有两个子节点。本资源针对二叉树的建树与遍历等相关功能进行了详细实现,主要使用C++编程语言。下面将对这些功能进行深入探讨...

    数据结构课程设计--二叉树的遍历.docx

    在算法分析中,二叉树的数学理论对于某些最有启发性的应用是与给出用于计算各种类型中不同树的数目的公式有关的。因此,二叉树在计算机科学中扮演着非常重要的角色。 本文对二叉树以及二叉树的各种功能做介绍,以及...

    /*建立二叉树后写出各种遍历算法,统计各类结点个数并求树的深度

    /*建立二叉树后写出各种遍历算法,统计各类结点个数并求树的深度 (1)仅输出中序遍历第K个元素算法(奖励1分) (2)编写求某个结点在树中层数算法(奖励2分) (3)已知中序和后序建立树结构(奖励3分)*/

    题目整理(二叉树).pdf

    在本文件中,整理了与二叉树相关的多个考点,涵盖了建立二叉树、遍历二叉树、二叉树的深度和高度计算、二叉树的平衡判断、二叉树的修改操作等多个方面的算法知识。以下是整理的知识点: 1. 建立二叉树的二叉链表: ...

    按层次遍历二叉树的算法

    按层次遍历二叉树的算法是二叉树遍历的一种重要方法。该算法的时间复杂度为O(n),空间复杂度为O(m),非常适合实际应用。在树形结构中,按层次遍历二叉树的算法可以快速地遍历、搜索、插入、删除等操作。

    二叉树的基本操作,遍历 查找 数据结构

    在这个实验中,我们关注的是二叉树的基本操作,包括建立、遍历、查找节点数量以及计算树的深度。 首先,我们来详细解释二叉树的结构。二叉树的节点包含数据和两个指向子节点的指针。在C++中,我们可以用结构体来...

Global site tag (gtag.js) - Google Analytics