树结构_二叉树基础,顺序存储二叉树,线索化二叉树

目录

【树结构】

【二叉树基础】

简易二叉树 

顺序存储二叉树

线索化二叉树


【树结构】

数组下标查找数据速度很快,但插入数据需要拷贝数组,数组扩容,数据全体向后移动,所以插入数据效率低下。

链表在插入方面表现优良,但查找数据仍要遍历这个链表,查找效率低下。

哈希表(散列表)hash表其实是结合了数组和链表的优点,进行的折中方案。平衡了数组和链表的优缺点。但其存在哈希关键值的冲突(两个以上数据经过哈希运算得到了同一个哈希值),此时要采取方案,如:数组+链表 的形式处理冲突。且哈希表数组长度固定,若改变数组长度将导致所有已加入的数据,重新进行哈希运算。

树的结构,可以提高数据的存储,读取效率,在保证数据的检索速度时,也能保证数据的插入,删除,修改的速度

树的分类:

二叉排序树(BST),平衡二叉树(AVL树),红黑树,B-树(B树),B+树等

 

【二叉树基础】

概念: 

如果二叉树的所有叶子结点都在最后一层,并且结点总数=2^n-1,n为层数,该二叉树称之为满二叉树

如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,称为完全二叉树

简易二叉树 

1,创建二叉树:

没什么难度,只不过是Node结点类里next变为了left,right。

2,二叉树遍历:

前序遍历:先输出父节点,再遍历左子树和右子树

中序遍历:先遍历左子树,再输出父节点,再遍历右子树

后序遍历:先遍历左子树,再遍历右子树,最后输出父节点

小结:看输出父节点的顺序,就确定是前序,中序还是后序

代码实现:

package cn.dataStructureAndAlgorithm.demo.tree;
class Node{
    private int id;
    private String data;
    private Node left;
    private Node right;

    public Node(int id, String data) {
        this.id = id;
        this.data = data;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node{" +
                "id=" + id +
                ", data='" + data + '\'' +
                '}';
    }
    //前序遍历
    public void preOrder(){
        System.out.println(this);
        if (this.left!=null){
            this.left.preOrder();
        }
        if (this.right!=null){
            this.right.preOrder();
        }
    }
    //中序遍历
    public void infixOrder(){
        if (this.left!=null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
            this.right.infixOrder();
        }
    }
    //后序遍历
    public void postOrder(){
        if (this.left!=null){
            this.left.postOrder();
        }
        if (this.right!=null){
            this.right.postOrder();
        }
        System.out.println(this);
    }

}
class BinaryTree{
    private Node root;//根节点
    public void setRoot(Node node){
        this.root=node;
    }

    public Node getRoot() {
        return root;
    }

    //前序遍历
    public void preOrder(){
        if (this.root!=null){
            this.root.preOrder();
        }else {
            System.out.println("二叉树为空");
        }
    }
    //中序遍历
    public void infixOrder(){
        if (this.root!=null){
            this.root.infixOrder();
        }else {
            System.out.println("二叉树为空");
        }
    }
    //后序遍历
    public void postOrder(){
        if (this.root!=null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空");
        }
    }
}
public class 二叉树_BinaryTree {
    public static void main(String[] args) {
        //创建二叉树
        BinaryTree binaryTree=new BinaryTree();
        Node node1=new Node(1,"one");
        Node node2=new Node(2,"two");
        Node node3=new Node(3,"three");
        Node node4=new Node(4,"four");
        Node node5=new Node(5,"five");
        binaryTree.setRoot(node1);
        binaryTree.getRoot().setLeft(node2);
        binaryTree.getRoot().setRight(node3);
        node3.setLeft(node4);
        node3.setRight(node5);

        System.out.println("前序遍历");
        binaryTree.preOrder();//1,2,3,4,5

        System.out.println("中序遍历");
        binaryTree.infixOrder();//2,1,3,4,5

        System.out.println("后序遍历");
        binaryTree.postOrder();//2,3,4,5,1
    }
}
前序遍历
Node{id=1, data='one'}
Node{id=2, data='two'}
Node{id=3, data='three'}
Node{id=5, data='five'}
Node{id=4, data='four'}
中序遍历
Node{id=2, data='two'}
Node{id=1, data='one'}
Node{id=5, data='five'}
Node{id=3, data='three'}
Node{id=4, data='four'}
后序遍历
Node{id=2, data='two'}
Node{id=5, data='five'}
Node{id=4, data='four'}
Node{id=3, data='three'}
Node{id=1, data='one'}

3,二叉树遍历查找:

就是在原有前中后遍历中,加入对id相等的判定即可。当判定相等时返回该节点。

代码实现:

Node类中新增

//前序查找
    public Node preOrderSearch(int id){
        if (this.getId()==id){
            return this;
        }
        Node resNode=null;
        if (this.left!=null){
            resNode=this.left.preOrderSearch(id);
        }
        if (resNode!=null){
            return resNode;
        }
        if (this.right!=null){
            resNode=this.right.preOrderSearch(id);
        }
        return resNode;
    }
    //中序查找
    public Node infixOrderSearch(int id){
        Node resNode=null;
        if (this.left!=null){
            resNode=this.left.infixOrderSearch(id);
        }
        if (resNode!=null){
            return resNode;
        }
        if (this.getId()==id){
            return this;
        }
        if (this.right!=null){
            resNode=this.right.infixOrderSearch(id);
        }
        return resNode;
    }
    //后序查找
    public Node postOrderSearch(int id){
        Node resNode=null;
        if (this.left!=null){
            resNode=this.left.postOrderSearch(id);
        }
        if (resNode!=null){
            return resNode;
        }
        if (this.right!=null){
            resNode=this.right.postOrderSearch(id);
        }
        if (resNode!=null){
            return resNode;
        }
        if (this.getId()==id){
            return this;
        }
        return resNode;
    }

binaryTree类中新增:

public void preOrderSearch(int id){
        if (this.root!=null){
            System.out.println("查询结果:"+this.root.preOrderSearch(id));
        }else {
            System.out.println("二叉树为空");
        }
    }
    //中序查找
    public void infixOrderSearch(int id){
        if (this.root!=null){
            System.out.println("查询结果:"+this.root.infixOrderSearch(id));
        }else {
            System.out.println("二叉树为空");
        }
    }
    //前序查找
    public void postOrderSearch(int id){
        if (this.root!=null){
            System.out.println("查询结果:"+this.root.postOrderSearch(id));
        }else {
            System.out.println("二叉树为空");
        }
    }

测试类中新增:

        System.out.println("前序查找");
        binaryTree.preOrderSearch(5);

        System.out.println("中序查找");
        binaryTree.infixOrderSearch(1);

        System.out.println("后序查找");
        binaryTree.postOrderSearch(0);
前序查找
查询结果:Node{id=5, data='five'}
中序查找
查询结果:Node{id=1, data='one'}
后序查找
查询结果:null

4,二叉树简单结点删除:

这里实现的简单删除是指:删除根节点,相当于重置二叉树;删除叶子结点,即将叶子结点置空;删除非叶子结点,即将该结点之下的所有结点都一并删除

代码实现:

Node类新增:

//简单删除
    public void delete(int id){
        if (this.left!=null && this.left.getId()==id){//删除左子节点
            this.left=null;
            return;
        }
        if (this.right!=null && this.right.getId()==id){//删除右子节点
            this.right=null;
            return;
        }
        if (this.left!=null){//向左子树递归
            this.left.delete(id);
        }
        if (this.right!=null){//向右子树递归
            this.right.delete(id);
        }
    }

binaryTree类新增:

//简单删除
    public void delete(int id){
        if (this.root!=null){
            if (this.root.getId()==id){//删除根节点
                this.root=null;
            }else{
                this.root.delete(id);
            }
        }else {
            System.out.println("二叉树为空");
        }
    }

测试类新增:

System.out.println("简单删除");
        binaryTree.delete(3);
        binaryTree.preOrder();
简单删除
Node{id=1, data='one'}
Node{id=2, data='two'}

 

顺序存储二叉树

 二叉树中的内容可以通过顺序存储或链式存储进行储存。

顺序存储指的是使用顺序表(数组)存储二叉树。需要注意的是,顺序存储只适用于完全二叉树。换句话说,只有完全二叉树才可以使用顺序表存储。因此,如果我们想顺序存储普通二叉树,需要提前将普通二叉树转化为完全二叉树。

顺序(数组)存储二叉树需要遵循一定规律:

树对应数组索引从root结点开始从左向右由0到n;n索引结点左节点对应数组索引为2*n+1;右结点对应数组索引为2*n+2;对应父节点为 (n-1)/2

 遵循以上规律,我们只需要在前中后序遍历中按照规律,就可以实现数组的前中后序遍历。

代码实现:

package cn.dataStructureAndAlgorithm.demo.tree;
class ArrBinaryTree{
    private int arr[];
    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }
    //前序遍历
    public void preOrder(){
        preOrder(0);
    }
    public void preOrder(int index){
        if (arr.length==0 || arr==null){//空数组不遍历
            System.out.println("空数组");
            return;
        }
        System.out.print(arr[index]+" ");
        //左遍历
        if ((index*2+1)<arr.length){//判断是否有对应的左节点
            preOrder(index*2+1);
        }
        //右遍历
        if ((index*2+2)<arr.length){//判断是否有对应的右节点
            preOrder(index*2+2);
        }
    }
    //中序遍历
    public void infixOrder(){
        infixOrder(0);
    }
    public void infixOrder(int index){
        if (arr.length==0 || arr==null){//空数组不遍历
            System.out.println("空数组");
            return;
        }
        //左遍历
        if ((index*2+1)<arr.length){//判断是否有对应的左节点
            infixOrder(index*2+1);
        }
        System.out.print(arr[index]+" ");
        //右遍历
        if ((index*2+2)<arr.length){//判断是否有对应的右节点
            infixOrder(index*2+2);
        }
    }
    //后序遍历
    public void postOrder(){
        postOrder(0);
    }
    public void postOrder(int index){
        if (arr.length==0 || arr==null){//空数组不遍历
            System.out.println("空数组");
            return;
        }
        //左遍历
        if ((index*2+1)<arr.length){//判断是否有对应的左节点
            postOrder(index*2+1);
        }
        //右遍历
        if ((index*2+2)<arr.length){//判断是否有对应的右节点
            postOrder(index*2+2);
        }
        System.out.print(arr[index]+" ");
    }
}
public class 顺序存储二叉树_OrderBinaryTree {
    public static void main(String[] args) {
        int data[]=new int[]{1,2,3,4,5,6,7};
        ArrBinaryTree arrBinaryTree=new ArrBinaryTree(data);
        System.out.println("前序遍历");
        arrBinaryTree.preOrder();
        System.out.println("\n中序遍历");
        arrBinaryTree.infixOrder();
        System.out.println("\n后序遍历");
        arrBinaryTree.postOrder();
    }
}
前序遍历
1 2 4 5 3 6 7 
中序遍历
4 2 5 1 6 3 7 
后序遍历
4 5 2 6 7 3 1 

 

线索化二叉树

 所谓线索化二叉树就是将二叉树中存在空指针域的结点,将其左指针指向其前一个结点(前驱结点:即遍历后其排序之前的结点),将其右指针指向其后一个结点(后继结点:即遍历后其排序之后的结点)

 上图中中序遍历后为{8,3,10,1,14,6},所以8的左指针指向null,右指针指向3。以此类推

算法分析:(以中序遍历线索化为例)

采用递归的形式进行线索化,需要一个pre用于保存前驱结点,而node保存当前结点。

遍历过程中,当发现结点的左子节点为空时就进行前驱结点的链接。当发现右子节点为空时,这个不好进行线索化。因为没有指向后继结点的指针。所以要先让其递归一次,使得原先的pre指向node,原先的node指向后继结点。这样后再对pre的右节点进行线索化,连接至node即可完成(开始理解起来有点难!)

代码实现:

package cn.dataStructureAndAlgorithm.demo.tree;
class Node_TB {
    private int id;
    private String data;
    private Node_TB left;
    private Node_TB right;
    //Type=0说明保存的为正常结点,1为前驱或后继结点
    private int leftType;
    private int rightType;

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public Node_TB(int id, String data) {
        this.id = id;
        this.data = data;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public Node_TB getLeft() {
        return left;
    }

    public void setLeft(Node_TB left) {
        this.left = left;
    }

    public Node_TB getRight() {
        return right;
    }

    public void setRight(Node_TB right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node{" +
                "id=" + id +
                ", data='" + data + '\'' +
                '}';
    }
}
class ThreadBinaryTree{
    private Node_TB root;//根节点
    private Node_TB pre=null;//上一个结点
    public void setRoot(Node_TB node){
        this.root=node;
    }
    public Node_TB getRoot() {
        return root;
    }
    //递归方法:中序线索化二叉树
    public void threadTree(Node_TB node){
        if (node==null){//递归结束条件:空节点不能线索化
            return;
        }
        //线索化左子树
        threadTree(node.getLeft());
        //线索化当前结点
        //1,线索化左结点 pre保存前上一个结点,node为当前结点,当node的left空时将left连接到pre上即可
        if (node.getLeft()==null){
            node.setLeft(pre);//链接到前驱结点
            node.setLeftType(1);
        }
        //2,线索化右节点 【有点难】 判断完左子树后,由于pre的right一定不为空,所以会逃过下面的判断进行一次递归后又来到这
        //此时pre保存上一次的node,而node保存的是上一次node的后继结点,此时pre的right为空,满足条件进行线索化
        if (pre!=null && pre.getRight()==null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        pre=node;//pre指向后移
        //线索化右子树
        threadTree(node.getRight());
    }
}
public class 线索化二叉树_ThreadBinaryTree {
    public static void main(String[] args) {
        Node_TB node1=new Node_TB(1,"one");
        Node_TB node2=new Node_TB(2,"two");
        Node_TB node3=new Node_TB(3,"three");
        Node_TB node4=new Node_TB(4,"four");
        Node_TB node5=new Node_TB(5,"five");
        Node_TB node6=new Node_TB(6,"six");
        Node_TB node7=new Node_TB(7,"seven");
        ThreadBinaryTree threadBinaryTree=new ThreadBinaryTree();
        threadBinaryTree.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        node3.setRight(node7);
        threadBinaryTree.threadTree(node1);
        System.out.println(node4.getRight());
    }
}
Node{id=2, data='two'}

线索化二叉树的遍历:

二叉树被线索化后,其叶子节点,没有了空指针域。用传统遍历时,会导致找不到结束递归的条件。造成死龟,引发栈溢出。所以要重新结合线索化二叉树的特点,重新设计。

代码实现:

ThreadBinaryTree类新增方法:

//中序遍历的线索化二叉树遍历(非递归)
    public void ThreadTreeList() {
        Node_TB node = this.root;
        //循环遍历到结尾
        while (node != null) {
            //1,遍历左子树阶段
            //循环直到找到left类型为1时,它一定是中序遍历的左结点数据,将其输出
            while (node.getLeftType() == 0) {
                node = node.getLeft();
            }
            System.out.println(node);
            //2,遍历中间阶段(由于树已被中序线索化,所以遍历right,就是在找中间的那个值阶段)
            //循环看right类型是否为1,是1声明它一定是后继结点,可以输出
            while (node.getRightType() == 1) {
                node = node.getRight();
                System.out.println(node);
            }
            //3,遍历右子树阶段
            node = node.getRight();
        }
    }

这个方法原理需要deBug一下才能明白。

测试类新增:

threadBinaryTree.ThreadTreeList();

结果输出:

Node{id=2, data='two'}
Node{id=4, data='four'}
Node{id=2, data='two'}
Node{id=5, data='five'}
Node{id=1, data='one'}
Node{id=6, data='six'}
Node{id=3, data='three'}
Node{id=7, data='seven'}

以上遍历是基于中序线索化二叉树实现的。其实还可以实现前序线索化,后序线索化。在这里先挖个坑~

 

 


有关树结构的其他内容,见下各链接

【树结构_堆排序】

【树结构_赫夫曼树,赫夫曼编码,文件解压缩】

【树结构_BST树(二叉排序树)】

【树结构_AVL树(平衡二叉树),红黑树与B系列树简介】

 

【数据结构与算法整理总结目录 :>】<-- 宝藏在此(doge)  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值