二叉树那点儿事(使用Java)

什么是二叉树?

二叉树又称knuth树,是一个由有限节点所组成的集合,此集合可以为空集合,或由一个树根及左右两个子树所组成。

为什么使用二叉树?

树状结构在计算机内存中的存储方式往往以链表为主,对于n元树来说,因为每个节点的分支度都不同,所以为了方便起见,一般就取n为链接个数的最大固定长度,那么每个节点中就包含了一份数据与n个链接。

假设n元树有m个节点,那么这颗树应共享了m*n个链接字段。除了树根之外,每个非空链接都指向一个节点,所以对于m个节点的树,应有m-1个实际链接,则树的空链接个数为mn-(m-1)=m(n-1)+1,则n元树的链接浪费率为 m ( n − 1 ) + 1 m n \frac{m(n-1)+1}{mn} mnm(n1)+1。根据这个式子我们可以发现,当n=2时,树的链接浪费率最低为 1 2 \frac{1}{2} 21,因此我们常常使用二叉树结构。

二叉树的节点数

如果一颗二叉树的深度为k,那么它的总结点数应为:

∑ i = 1 k 2 i − 1 = 2 0 + 2 1 + . . . + 2 k − 1 = 2 k − 1 2 − 1 = 2 k − 1 \sum_{i=1}^k 2^{i-1}=2^0+2^1+...+2^{k-1}=\frac{2^k-1}{2-1}=2^k-1 i=1k2i1=20+21+...+2k1=212k1=2k1

二叉树的存储方式

树状结构在计算机中的存储方式往往以链表为主,这是因为链表的指针用来处理数比较方便,只需改变指针即可。不过也可以使用数组来存储二叉树,两种方法其实都各有利弊。

数组表示法

如果要使用数组来存储二叉树,首先要将二叉树想象成一个满二叉树,,然后依序存放在一维数组中。

比如下面这颗树:

A
B
空1
空2
C
空3
D

树中的A,B,C,D代表节点的值,其余的为空节点,它们的位置如图所示,那么如果让它们放进数组,应该是这样的:

索引1234567
ABCD

建立二叉树需要遵守小于父节点的值放在左子节点,大于父节点的值放在右子节点的规则,因此左子树的值一定全部小于树根,右子树的值一定大于树根。

下面是一个使用数组存储二叉树的示例:

public class arrayBinaryTree {
    public static void main(String[] args) {
        int i,level;
        int[] data = {6,3,5,9,7,8,4,2}; //  原始数据
        int btree[] = new int[16];      //  二叉树数组大小,长度16是因为使用后15个空间,让索引从1开始,比较好理解
        for (i=0; i<16; i++)
            btree[i] = 0;
        System.out.println("原始数组内容:");
        for (i=0;i<8;i++)
            System.out.print("["+data[i]+"]");
        System.out.println();
        //  将原始数据添加入二叉树
        for (i=0;i<8;i++) {
            for (level=1;btree[level]!=0;) {
                if (data[i]>btree[level])
                    level = level*2+1;
                else
                    level = level*2;
            }
            btree[level] = data[i];
        }
        System.out.println("二叉树:");
        for (i=1;i<16;i++)
            System.out.print("["+btree[i]+"]");
    }
}
OUT:
原始数组内容:
[6][3][5][9][7][8][4][2]
二叉树:
[6][3][9][2][5][7][0][0][0][4][0][0][8][0][0]
链表表示法

使用链表表示法来存储二叉树可以定义TreeNodeBinaryTree类,其中前者代表二叉树中的一个节点,后者就代表一棵二叉树。

节点中应包括存放的数据、左子节点的指针和右子节点的指针。二叉树中应该实现一个方法即将数据插入到二叉树中。

示例如下:

/*
    TreeNode    二叉树节点
 */
class TreeNode {
    int value;
    TreeNode leftNode,rightNode;
    public TreeNode(int value) {
        this.value = value;
        leftNode = null;
        rightNode = null;
    }
}
/*
    BinaryTree  二叉树
 */
class BinaryTree {
    public TreeNode rootNode;
    public BinaryTree(int[] data) {
        for (int i=0;i<data.length;i++)
            Add_Node_To_BinaryTree(data[i]);
    }
    /*
        将节点加入二叉树中
     */
    void Add_Node_To_BinaryTree(int value) {
        TreeNode currentNode = rootNode;
        //  如果根节点为空,先创建根节点
        if (rootNode == null) {
            rootNode = new TreeNode(value);
            return;
        }
        //  循环寻找适合当前节点的位置
        while (true) {
            if (value<currentNode.value) {
                if (currentNode.leftNode == null) {
                    currentNode.leftNode = new TreeNode(value);
                    return;
                }
                else currentNode = currentNode.leftNode;
            }else {
                if (currentNode.rightNode == null) {
                    currentNode.rightNode = new TreeNode(value);
                    return;
                }else currentNode = currentNode.rightNode;
            }
        }
    }
    //	打印各个节点值(中序遍历,即结果从小到大排序)
    void printTree(TreeNode node) {
        if (node != null) {
            printTree(node.leftNode);
            System.out.print("["+node.value+"]");
            printTree(node.rightNode);
        }
    }
}
public class LinkBinaryTree {
    public static void main(String[] args) {
        int[] content = {11, 13, 15, 17, 19, 28, 26, 24, 22, 20};
        BinaryTree binarytree = new BinaryTree(content);
        binarytree.printTree(binarytree.rootNode);
    }
}
OUT:
[11][13][15][17][19][20][22][24][26][28]

如上使用链表来建立了二叉树,并根据中序遍历的方法打印出了二叉树存储的数据,可以看到数据是从小到大排列的。

二叉树遍历

二叉树的遍历就是将树中的每一个节点各访问一次,然后在遍历完毕后将树中的数据转化为线性关系。

如下一个二叉树节点:

A
B
C

如果想将它遍历,则共有六种遍历方法:ABC,ACB,BAC,BCA,CAB,CBA,不过既然二叉树满足左子节点值小于父节点而右子节点值大于父节点关系,那么从左向右遍历和从右向左遍历得到的结果是类似的,只要将结果反转一下就一样了,因此我们可以规定遍历规则为一律从左向右,那么就剩下三种遍历方式了:BAC,ABC,BCA

这三种遍历方式就是

  • 中序遍历(BAC):左子树->树根->右子树
  • 前序遍历(ABC):树根->左子树->右子树
  • 后序遍历(BCA):左子树->右子树->树根

其实这三种遍历方式,你只要记住树根的遍历顺序就行了,树根在前就是前序遍历,树根在后就是后序遍历,而左子树一定在右子树之前遍历的。

如下一颗子树:

A
B
C
D
E
F

它的前中后序遍历结果应该是:

ABDECF

DBEACF

DEBFCA

这很简单,大家应该一看就知道如何遍历了。

那么如果我们想要将一棵二叉树的各个节点值遍历出来,在程序中应该怎么实现呢?在链表表示法中我们已经给出了中序遍历的方法,方法中巧妙的运用了递归的方式进行遍历。下面给出前中后序遍历的代码:

    //  前序遍历
    void PreprintTree(TreeNode node) {
        if (node != null) {
            System.out.print("["+node.value+"]");
            PreprintTree(node.leftNode);
            PreprintTree(node.rightNode);
        }
    }
	//  中序遍历
    void MidprintTree(TreeNode node) {
        if (node != null) {
            MidprintTree(node.leftNode);
            System.out.print("["+node.value+"]");
            MidprintTree(node.rightNode);
        }
    }
    //  后序遍历
    void PosprintTree(TreeNode node) {
        if (node != null) {
            PosprintTree(node.leftNode);
            PosprintTree(node.rightNode);
            System.out.print("["+node.value+"]");
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳寜

“请作者吃颗糖!”

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值