数据结构——二叉树

目录

 

一:二叉树

1:二叉树定义:每个结点最多两棵树,即二叉树中不存在度大于2的结点。子树有左右之分。

2:二叉树的形态:

3:二叉树的重要特性:

4:树和二叉树的两个重要区别

5:满二叉树和完全二叉树

满二叉树:一个深度为k的二叉树,如果它包含2^k-1个节点,就把这棵树称为满二叉树。

完全二叉树:如果一颗二叉树除最后一层外,其余各层的所有节点都是满的,最后一层或者

二:二叉树的存储结构

1:顺序存储:

2:链式存储

详细介绍

2:二叉树顺序实现的代码

3:链式存储

二叉链式存储的代码

三叉链式存储方式

五:二叉树的基本操作

五:二叉树的遍历:


一:二叉树

1:二叉树定义:每个结点最多两棵树,即二叉树中不存在度大于2的结点。子树有左右之分。

左边的子树被称作“左子树”,右边的子树被称作右子树。

2:二叉树的形态:

3:二叉树的重要特性:

(1)二叉树,在第i层至多有2i-1个节点。

(2)深度为k的二叉树至多有2k-1个结点。

(3)深度(高度)为k的完全二叉树至少有2k-2个结点。

(4)非空二叉树的叶子结点数等于度为2的结点数加1.

(5)具有n个结点的完全二叉树,深度为log2n+1

4:树和二叉树的两个重要区别

(1)树中节点的最大度数没有限制,而二叉树节点的最大度数为2,也就是说二叉树是节点的最大度数为2的树。

(2)无序树的节点五左右之分,而二叉树的节点有左右之分,也就是说二叉树是有序树。

5:满二叉树和完全二叉树

满二叉树:一个深度为k的二叉树,如果它包含2^k-1个节点,就把这棵树称为满二叉树。

满二叉树的特点:每一层上的节点数都是最大节点数,即各层的节点数为1,2,4,8,16,……,2^(k-1)。

完全二叉树:如果一颗二叉树除最后一层外,其余各层的所有节点都是满的,最后一层或者

是满的,或者仅在右边缺少若干连续的节点,则此二叉树就是完全二叉树。

二:二叉树的存储结构

1:顺序存储:

用数组来存储数据元素,从存储的角度来看,这种顺序存储结构,仅适用于完全二叉树。

(因为在最坏的情况下,一个深度为k且只有k个结点的单枝树(树中不存在度为2的结点),需要2k-1的以为数组)

2:链式存储

二叉链表存储:每个节点保留一个left,right域,分别指向其左,右子节点。

三叉链表存储:每个节点保留一个left,right,parent域,分别指向其左,右子节点和父节点。

详细介绍

1:二叉树的顺序存储

顺序存储充分利用满二叉树的特性(深度为i的二叉树最多只能包含2^i-1个节点)。定义一份长度为2^i-1的数组就可以保存二叉树。

二叉树采用顺序存储的缺点:会造成空间浪费(对于普通二叉树,会有空出来的节点和对应的数组元素留空)

(如果该二叉树是完全二叉树,就不会有空间浪费,但如果该二叉树的所有节点都只有右子节点,那么就会

产生相当大的空间浪费。)

 

 

2:二叉树顺序实现的代码

import java.util.Arrays;

/**
 * Created by 20378 on 2018-11-05.
 */
public class ArrayBinTree<T> {
    private Object[] elements;//使用数组来记录该树的所有节点
    private int default_deep=8;//树的默认深度
    private int deep;//保存该树的深度
    private int arraySize;//数组的大小

    public ArrayBinTree()
    {//已默认的深度创建二叉树
        this.deep=default_deep;
        this.arraySize=(int)Math.pow(2,deep)-1;
        elements=new Object[arraySize];
    }
    public ArrayBinTree(int deep)
    {//以指定的深度创建二叉树
        this.deep=deep;
        this.arraySize=(int)Math.pow(2,deep)-1;
        elements=new Object[arraySize];
    }
    public ArrayBinTree(int deep,T ele)
    {//以指定深度,指定根节点创建二叉树
        this.deep=deep;
        this.arraySize=(int)Math.pow(2,deep)-1;
        elements=new Object[arraySize];
        elements[0]=ele;
    }


    //index:需要添加的子节点的父节点的索引
    //ele:新节点的数据
    //left:是否为左节点
    public void add(int index,T ele,boolean left)
    {
        if(elements[index]==null)
        {
            throw new RuntimeException(index+"处节点为空,无法添加节点");
        }
        if(2*index+1>=arraySize)
        {
            throw new RuntimeException("树底层的数组已满,树越界异常");
        }
        //添加左子节点
        if(left)
            elements[2*index+1]=ele;
        else
            elements[2*index+2]=ele;
    }
    public boolean empty()
    {
        return elements[0]==null;
    }
    public T root()
    {
        return (T)elements[0];
    }
    public T parent(int index)
    {//返回指定节点(非根节点)的父节点
        return (T)elements[(index-1)/2];
    }
    public T left(int index)
    {//返回指定节点的左子节点,当左子节点不存在时返回null
        if(2*index+1>=arraySize)
        {
            throw new RuntimeException("该节点为叶子结点,无子节点");
        }
        return (T)elements[index*2+1];
    }
    public T right(int index)
    {//返回指定节点的右子节点,,当右子节点不存在时返回null
        if(2*index+1>=arraySize)
        {
            throw new RuntimeException("该节点为 叶子结点,无子节点");
        }
        return (T)elements[2*index+2];
    }

    public int deep(int index)
    {
        return deep;
    }
    public int pos(T ele)
    {//返回指定节点的位置
        for(int i=0;i<arraySize;i++)
        {
            if(elements[i].equals(ele))
                return i;
        }
        return -1;
    }
    public String toString()
    {
        return java.util.Arrays.toString(elements);
    }


    public static void main(String[] args)
    {
        ArrayBinTree<String> binTree=new ArrayBinTree<String>(4,"根");
        binTree.add(0,"第二层右子节点",false);
        binTree.add(2,"第三层右子节点",false);
        System.out.println(binTree);
    }


}

3:链式存储

二叉链式存储的代码

package kejian;

/**
 * Created by 20378 on 2018-11-06.
 */
public class TwoLinkBinTree<E> {
    public static class TreeNode
    {
        Object element;
        TreeNode left;
        TreeNode right;
        public TreeNode() {}
        public TreeNode(Object element)
        {
            this.element=element;
        }
        public TreeNode(Object element,TreeNode left,TreeNode right)
        {
            this.element=element;
            this.left=left;
            this.right=right;
        }
    }

    private TreeNode root;
    public TwoLinkBinTree()
    {//以默认的构造器创建二叉树
        this.root=new TreeNode();
    }
    public TwoLinkBinTree(E element)
    {
        this.root=new TreeNode(element);
    }

    public TreeNode addNode(TreeNode parent, E element,boolean isLeft)
    {
        if(parent==null)
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        if(isLeft && parent.left!=null)
            throw new RuntimeException(parent+"节点已有左子节点,无法添加左子节点");
        if(!isLeft && parent.right != null)
            throw new RuntimeException(parent+"节点已有右子节点,无法添加右子节点");

        TreeNode newNode=new TreeNode(element);
        if(isLeft)
        {//让父节点的left引用指向新节点
            parent.left=newNode;
        }
        else
            parent.right=newNode;
        return newNode;

    }
    public boolean empty()
    {
        return root.element==null;
    }
    public TreeNode root()
    {//返回根节点
        if(empty())
            throw new RuntimeException("树为空,无法访问根节点");
        return root;
    }
    public E parent(TreeNode node)
    {
        //对于二叉链表存储法,如果要访问指定节点的父节点必须要遍历二叉树
        return null;
    }
    public E leftChild(TreeNode parent)
    {//返回指定节点(非叶子)的左子节点,当左子节点不存在时返回null
        if(parent==null)
        {
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        }
        return parent.left==null?null:(E)parent.left.element;
    }
    public E rightChild(TreeNode parent)
    {
        if(parent == null)
        {
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        }
        return parent.right == null?null:(E)parent.right.element;
    }
    public int deep()
    {
        return deep(root);
    }
    private int deep(TreeNode node)
    {
        if(node==null)
            return 0;
        if(node.left==null && node.right==null)
            return 1;
        else{
            int leftDeep=deep(node.left);
            int rightDeep=deep(node.right);
            int max=leftDeep>rightDeep?leftDeep:rightDeep;
            return max+1;
        }

    }

    public static void main(String[] args)
    {
        TwoLinkBinTree<String> binTree=new TwoLinkBinTree<>("根节点");

        TwoLinkBinTree.TreeNode treenode1= binTree.addNode(binTree.root(),"第二层左节点",true);
        TwoLinkBinTree.TreeNode treenode2= binTree.addNode(binTree.root(),"第二层右节点",false);
        TwoLinkBinTree.TreeNode treenode3= binTree.addNode(treenode2,"第三层左节点",true);
        TwoLinkBinTree.TreeNode treenode4= binTree.addNode(treenode2,"第三层右节点",false);
        TwoLinkBinTree.TreeNode treenode5= binTree.addNode(treenode3,"第四层左节点",true);
        System.out.println("treenode2的左子节点:  "+binTree.leftChild(treenode2));
        System.out.println(binTree.deep());
    }


}
class Node<T>
{
    T element;
    Node left;
    Node reght;
}

三叉链式存储方式

package kejian;

/**
 * Created by Administrator on 2018/11/8 0008.
 */
public class ThreeLinkBinTree<E> {
    public static class TreeNode
    {
        Object element;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
        public TreeNode()
        {
        }
        public TreeNode(Object element)
        {
            this.element=element;
        }
        public TreeNode(Object element,TreeNode left,
                        TreeNode right,TreeNode parent)
        {
            this.element=element;
            this.left=left;
            this.right=right;
            this.parent=parent;
        }
    }
    private TreeNode root;
    public ThreeLinkBinTree()
    {
        this.root=new TreeNode();
    }
    public ThreeLinkBinTree(E element)
    {
        this.root=new TreeNode(element);
    }
    public TreeNode addNode(TreeNode parent, E element,boolean isLeft)
    {
        if(parent==null)
        {
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        }
        if(!isLeft && parent.left!=null)
        {
            throw new RuntimeException(parent+"节点已有左子节点,无法添加左子节点");
        }
        if(!isLeft && parent.right!=null)
        {
            throw new RuntimeException(parent+" 节点已有右子节点,无法添加右子节点");
        }

        TreeNode newNode=new TreeNode(element);
        if(isLeft)
        {
            parent.left=newNode;
        }
        else
        {
            parent.right=newNode;
        }
        newNode.parent=parent;
        return newNode;
    }
    public boolean empty()
    {
        return root.element==null;
    }
    public TreeNode root()
    {
        if(empty())
            throw new RuntimeException("树为空,无法访问根节点");
        return root;
    }
    public E parent(TreeNode node)
    {
        if(node==null)
            throw new RuntimeException(node+"节点为null,无法访问其父节点");
        return (E)node.parent.element;
    }
    public E leftChild(TreeNode parent)
    {
        if(parent==null)
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        return parent.left==null?null:(E)parent.left.element;
    }
    public int deep()
    {
        return deep(root);
    }
    private int deep(TreeNode node)
    {
        if(node==null)
            return 0;
        if(node.left==null && node.right==null)
            return 1;
        else
        {
            int leftdeep=deep(node.left);
            int rightdeep=deep(node.right);
            int max=leftdeep>rightdeep?leftdeep:rightdeep;
            return max+1;
        }
    }



}

三叉链式存储是对二叉链表的一种改进,通过为树节点增加一个parent引用,可以让每个

节点都能非常方便地访问其父节点。三叉链表存储的二叉树既能向上访问节点,也可向下访问节点。

 

 

五:二叉树的基本操作

1:初始化:通常是一个构造器,用于创建一个空的树,或者以指定节点为根来创建二叉树。

2:为指定节点添加子节点。

3:判断二叉树是否为空

4:返回根节点

5:返回指定节点(非根节点)的父节点

6:返回指定节点(非叶子结点)的左子节点

7:返回指定节点(非叶子节点)的右子节点

8:返回该二叉树的深度

9:返回指定节点的位置

 

五:二叉树的遍历:

遍历二叉树:指的是按照某种规律依次访问二叉树的每个节点,对二叉树的

               遍历过程就是将非线性结构的二叉树中的节点排列在一个线性序列上

                过程。

采用链表来保存二叉树的节点,则有以下两类遍历方式。

——深度优先遍历:这种遍历算法将先访问到树中最深层次的节点。

————先序遍历:DLR:先处理根节点。

————中序遍历:LDR:其次处理根节点。

————后序遍历:LRD:最后处理根节点。

如果用L,D,R,表示左子树,根,右子树。这三种遍历都是针对根节点

——广度优先遍历:这种遍历算法将逐层访问每层的节点,先访问根节点,然后访问第二层的节点……

依次类推。因此,广度优先遍历方法又被称为按层遍历。

package kejian;

import sun.reflect.generics.tree.Tree;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;

/**
 * Created by Administrator on 2018/11/8 0008.
 */
public class ThreeLinkBinTree<E> {
    public static class TreeNode
    {
        Object element;
        TreeNode left;
        TreeNode right;
        TreeNode parent;
        public TreeNode()
        {
        }
        public TreeNode(Object element)
        {
            this.element=element;
        }
        public TreeNode(Object element,TreeNode left,
                        TreeNode right,TreeNode parent)
        {
            this.element=element;
            this.left=left;
            this.right=right;
            this.parent=parent;
        }
    }
    private TreeNode root;
    public ThreeLinkBinTree()
    {
        this.root=new TreeNode();
    }
    public ThreeLinkBinTree(E element)
    {
        this.root=new TreeNode(element);
    }
    public TreeNode addNode(TreeNode parent, E element,boolean isLeft)
    {
        if(parent==null)
        {
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        }
        if(!isLeft && parent.left!=null)
        {
            throw new RuntimeException(parent+"节点已有左子节点,无法添加左子节点");
        }
        if(!isLeft && parent.right!=null)
        {
            throw new RuntimeException(parent+" 节点已有右子节点,无法添加右子节点");
        }

        TreeNode newNode=new TreeNode(element);
        if(isLeft)
        {
            parent.left=newNode;
        }
        else
        {
            parent.right=newNode;
        }
        newNode.parent=parent;
        return newNode;
    }
    public boolean empty()
    {
        return root.element==null;
    }
    public TreeNode root()
    {
        if(empty())
            throw new RuntimeException("树为空,无法访问根节点");
        return root;
    }
    public E parent(TreeNode node)
    {
        if(node==null)
            throw new RuntimeException(node+"节点为null,无法访问其父节点");
        return (E)node.parent.element;
    }
    public E leftChild(TreeNode parent)
    {
        if(parent==null)
            throw new RuntimeException(parent+"节点为null,无法添加子节点");
        return parent.left==null?null:(E)parent.left.element;
    }
    public int deep()
    {
        return deep(root);
    }
    private int deep(TreeNode node)
    {
        if(node==null)
            return 0;
        if(node.left==null && node.right==null)
            return 1;
        else
        {
            int leftdeep=deep(node.left);
            int rightdeep=deep(node.right);
            int max=leftdeep>rightdeep?leftdeep:rightdeep;
            return max+1;
        }
    }


    //实现先序遍历
    public List<TreeNode> preIterator()
    {
        return preIterator(root);
    }
    private List<TreeNode> preIterator(TreeNode node)
    {
        List<TreeNode> list=new ArrayList<TreeNode>();
        list.add(node);
        if(node.left!=null)
            list.addAll(preIterator(node.left));
        if(node.right!=null)
            list.addAll(preIterator(node.right));
        return list;
    }

    //实现中序遍历
    public List<TreeNode> inIterator()
    {
        return inIterator(root);
    }
    private List<TreeNode> inIterator(TreeNode node)
    {
        List<TreeNode> list=new ArrayList<TreeNode>();
        if(node.left!=null){
            list.addAll(inIterator(node.left));
        }
        list.add(node);
        if(node.right!=null)
            list.addAll(inIterator(node.right));
        return list;
    }

    //后序遍历
    public List<TreeNode> postIterator()
    {
        return postIterator(root);
    }
    private List<TreeNode> postIterator(TreeNode node)
    {
        List<TreeNode> list=new ArrayList<TreeNode>();
        if(node.left!=null)
            list.addAll(postIterator(node.left));
        if(node.right!=null)
            list.addAll(postIterator(node.right));
        list.add(node);
        return list;
    }

    //广度优先遍历
    public List<TreeNode> breadthFirst()
    {
        Queue<TreeNode> queue=new ArrayDeque<TreeNode>();
        List<TreeNode> list = new ArrayList<TreeNode>();
        if(root!=null)
            queue.offer(root);
        while (!queue.isEmpty())
        {
            list.add(queue.peek());
            TreeNode p=queue.poll();
            if(p.left!=null)
                queue.offer(p.left);
            if(p.right!=null)
                queue.offer(p.right);
        }
        return list;
    }
}

 

1:由遍历序列确定二叉树

由先序和中序,可以确定。

由后序和中序,可以确定(但是注意后序最后一个为根,下一个是右子树根)

由层序和中序,可以确定

 

2:根据遍历序列估计二叉树

前序遍历序列和后序遍历序列相同的数:只有根节点。

前序遍历和中序遍历相同的二叉树:所有节点没有左子树

中序遍历和后序遍历相同的二叉树:所有结点没有右子树。

前序遍历和后序遍历相反的二叉树:没有左子树或者没有左子树。

前序遍历和中序遍历相反的二叉树,所有结点没有右子树

中序遍历和后序遍历相反的二叉树:所有结点没有左子树。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值