Java数据结构:树基础及二叉树

1. 树的简介

1.1 树的定义

树是一种特殊的数据结构,它可以用来描述有分支的结构,是由一个或一个以上的节点所组成的有限集合。

1.2 树的特点

a. 存在一个特殊的节点,称为根节点;

b. 没有父节点的节点称为根节点;

c. 每一个非根节点有且只有一个父节点;

d. 除了根节点外,每个子节点可以分为多个不相交的子树;

图中A节点就称为根节点,B、C、D 均为A的子节点。

1.3 树的专有名词

a. 树根或者根节点(root):没有父节点的节点称为根节点,如下图中树的根节点为 A。

b. 父节点(parent):每一个节点的上层节点为父节点,如下图中 B 的父节点为 A,E 的父节点为 C。

c. 子节点(children):每一个节点的下层节点为子节点,如 A的子节点有 B、C、D,而 C 的子节点有 E、F、G。

d. 兄弟节点(siblings):有共同父节点的节点为兄弟节点,如 B、C、D 的父节点均为 A,所以彼此称为兄弟节点。

e. 度(degree):子树的个数称为该节点的度,如 A的度为3,D 的度为1。

f. 叶子节点或终端节点(terminal node):没有子节点的节点,即度为 0 的节点,如图中的 B、E、F、G、I。

g. 阶层或级(level):根节点的阶层为 1,图中 A的阶层为 1,B、C、D的阶层为 2,E、F、G、H的阶层为3,I 的阶层为4。

h. 高度(height):树的最大阶层,例如此树的高度为 4。

i. 森林(forest):森林是由 n 个不相交的树组成,移去树根即为森林。如图中,移除节点 A,则包含三个树的森林。

2. 二叉树

2.1 二叉树的定义

二叉树是由有限个节点所组成的集合,此集合可以为空集合,或由一个树根及其左右两个子树所组成。简单的说,二叉树最多只能有两个子节点,就是度小于或者等于2。二叉树的数据结构如下:

2.2 二叉树的性质

1. 二叉树第 i 层上的节点数目最多为 2^{i-1},其中 i ≥ 1。

2. 深度为 k 的二叉树至多有 2^{k}-1个节点,其中 k ≥ 1。

2.3. 满二叉树

定义

高度为 h,并且由 2^h - 1 个节点的二叉树,被称为满二叉树。如下图所示,这是一棵满二叉树

2.4. 完全二叉树

定义

一棵二叉树中,只有最下层节点的度可以小于2,并且最下层的叶节点集中在靠左的若干位置上,这样的二叉树称为完全二叉树。

特点

叶子节点只能出现在最下层和次下层,且最下层的叶子节点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。如下图,这是一棵完全二叉树:

而下图不是一棵完全二叉树:

2.5 二叉查找树

定义

二叉查找树,又称为二叉搜索树。假设 x 为二叉查找树中的一个节点,x 节点包含关键字 key,节点的 key 值记为 key[x]。如果 y 是 x 的左子树中的一个节点,则 key[y] ≤ key[x] ;如果 y 是 x 的右子树的一个节点,则 key[y] ≥ key[x]。如下图所示:

特点

a. 每个树根的值需大于左子树的值。

b. 每一个树根的值需小于右子树的值。

c. 左右子树也是二叉搜索树。

d. 没有键值相等的节点。

3. 二叉树存储

3.1 数组表示法

如果要使用一维数组来存储二叉树,首先将二叉树想象成一个满二叉树,而且第 k 个阶层具有 2^{k-1} 个节点,并且按照顺序存放在此一维数组之中。这里从数组下标为1的位置开始存放。

如下图所示的二叉树,用数组表示为:{0,13,65,5,97,25,0,37,22,0,4,28,0,0,32,0}。

3.2 链表表示法

树的节点:

package zzw.cn.tree;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/24 21:37
 */
public class TreeNode
{
    int value;//节点值
    TreeNode leftNode;//指向左节点
    TreeNode rightNode;//指向右节点

    public TreeNode(int value)
    {
        this.value = value;
    }
}

二叉树:

package zzw.cn.tree;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/25 0:40
 */
public class BinaryTree
{
    TreeNode root;//根节点

    public BinaryTree(int[] array)
    {
        root = transformArrayToBinaryTree(array, 1);
    }

    //采用递归方式创建二叉树
    public TreeNode transformArrayToBinaryTree(int[] array, int index)
    {
        if (index < array.length)
        {
            int value = array[index];
            if (value != 0)
            {
                TreeNode node = new TreeNode(value);
                array[index] = 0;
                node.leftNode = transformArrayToBinaryTree(array, index * 2);
                node.rightNode = transformArrayToBinaryTree(array, index * 2 + 1);
                return node;
            }
        }
        return null;
    }
}

4. 二叉树的遍历

二叉树的遍历,就是访问树的所有节点各一次,并且在遍历后,将树中的数据转化为线性关系。树的遍历存在三种方式:

a. 中序遍历

b. 前序遍历

c. 后续遍历

4.1 中序遍历

若二叉树非空,则执行以下操作:左子树 → 树根 → 右子树。就是沿着树的左子树一直往下,直到无法前进后退回返回父节点,再往右子树一直往下。如果右子树也走完了就退回上层的左节点,再重复左、中、右的顺序遍历。

上图树的中序遍历如下所示: 

package zzw.cn.tree;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/25 1:18
 */
public class BinaryTreeTest
{
    public static void main(String[] args)
    {
        int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
        BinaryTree tree = new BinaryTree(arr);
        inOrder(tree);//中序遍历
    }

    //中序遍历
    private static void inOrder(TreeNode node)
    {
        if (node != null)
        {
            inOrder(node.leftNode);
            System.out.print(node.value + " ");
            inOrder(node.rightNode);
        }
    }

    private static void inOrder(BinaryTree tree)
    {
        System.out.print("中序遍历:");
        inOrder(tree.root);
    }
}

结果:

中序遍历:22 97 65 4 25 28 13 5 32 37 

4.2 前序遍历

前序遍历的顺序为:树根 → 左子树 → 右子树。前序遍历就是从根节点开始处理,根节点处理完往左子树,直到无法前进再处理右子树。

上图树的前序遍历如下所示: 

package zzw.cn.tree;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/25 1:18
 */
public class BinaryTreeTest
{
    public static void main(String[] args)
    {
        int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
        BinaryTree tree = new BinaryTree(arr);
        preOrder(tree);//前序遍历
    }

    //前序遍历
    private static void preOrder(TreeNode node)
    {
        if (node != null)
        {
            System.out.print(node.value + " ");
            preOrder(node.leftNode);
            preOrder(node.rightNode);
        }
    }

    private static void preOrder(BinaryTree tree)
    {
        System.out.print("前序遍历:");
        preOrder(tree.root);
    }
}

结果:

前序遍历:13 65 97 22 25 4 28 5 37 32 

4.3 后序遍历

后序遍历的顺序为:左子树 → 右子树 → 树根。后序遍历和前序遍历的方法相反,它是把左子树的节点和右节点都处理完了才处理树根。

上图树的后序遍历如下所示: 

package zzw.cn.tree;

/**
 * @author 鹭岛猥琐男
 * @create 2019/8/25 1:18
 */
public class BinaryTreeTest
{
    public static void main(String[] args)
    {
        int[] arr = {0, 13, 65, 5, 97, 25, 0, 37, 22, 0, 4, 28, 0, 0, 32, 0};
        BinaryTree tree = new BinaryTree(arr);
        postOrder(tree);//后序遍历
    }

    //后序遍历
    private static void postOrder(TreeNode node)
    {
        if (node != null)
        {
            postOrder(node.leftNode);
            postOrder(node.rightNode);
            System.out.print(node.value + " ");
        }
    }

    private static void postOrder(BinaryTree tree)
    {
        System.out.print("后序遍历:");
        postOrder(tree.root);
    }
}

结果:

后序遍历:22 97 4 28 25 65 32 37 5 13 

参考:

https://github.com/LRH1993/android_interview/blob/master/data-structure/tree/tree-introduction.md

《图解数据结构-使用Java》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章