一、基本概念

1、什么是树?

树是一中类似于链表的数据结构,不过链表的结点是以线性方式简单地指向其后继节点,而树的一个结点可以指向许多结点;
树是一种典型的非线性结构,树结构是表达具有层次特性的图结构的一种方法;
对于树ADT(抽象数据类型),元素的顺序不是重点。如果需要用到用到元素的顺序信息,可以使用链表,栈,队列等线性数据结构。

2、树之术语

  • 根节点:没有双亲结点的结点,一个树最多只能有一个根节点;
  • 边:双亲结点到孩子结点的链接;
  • 叶子结点:没有孩子结点的结点;
  • 兄弟结点:拥有相同双亲结点的所有孩子结点;
  • 祖先结点:如果存在一条从根结点到孩子结点Q的路径,且结点P出现在这条路径上,则结点P就是结点Q的祖先结点,同时Q也是结点P的子孙结点;
  • 结点大小:结点大小是指其子孙的个数(包括其自身);
  • 树的层:位于相同深度的所有结点集合叫做树的层,从开始0层,则根节点位于0层;
  • 结点的深度:从根节点到该结点的路径长度;和结点位于的层数一样;
  • 结点的高度:从该结点到最深结点的路径长度,结点的高度和深度不一定相等;
  • 树的高度:树中所有结点高度的最大值;
  • 树的深度:树中所有结点深度的最大值,且树的高度等于树的深度;
  • 斜树:树中除了叶子结点外,每一个结点都只有一个孩子结点;
    • 只有左孩子结点的叫左斜树;
    • 只有右孩子结点的叫右斜树;
    • 其余的叫斜树;
    • 结点的度数:一个结点含有的孩子结点的个数;

二、二叉树

1、什么是二叉树?

如果一颗树中的每个结点有0,1或者2个孩子结点,那么这棵树就是二叉树;
空树也是一颗二叉树;
一颗二叉树可以看做由根结点和两颗不相交的子树组成;

2、二叉树类型

严格二叉树:二叉树中的每个结点要么有两个孩子结点,要么没有孩子结点;
满二叉树:二叉树中的每个结点恰好都有两个孩子结点,且所有的叶子结点都位于同一层;
完全二叉树:如果一颗二叉树,扣除其最大层次后是一颗满二叉树,且最后一层的所有节点均向左靠齐,则称该树为完全二叉树;

3、二叉树的性质

假设输的高度为h,根结点的深度为0,
满二叉树的结点个数为2^(h+1) - 1;
完全二叉树结点个数为2^h 到 2^(h+1) - 1;
满二叉树的叶子结点个数为2^h;
对于n的结点的完全二叉树,空指针个数为n+1???;

4、二叉树的结构
表示一个结点的方法为:定义两个指针,一个指向左孩子结点,一个指向右孩子结点,中间为数据字段;

public class BinaryTreeNode{
    private int data;
    private BinaryTreeNode left;
    private BinaryTreeNode right;

    public int getData(){
        return data;
    }

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

    public BinaryTreeNode getLeft(){
        return left;
    }

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

    public BinaryTreeNode getRight(){
        return right;
    }

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

5、二叉树的遍历
访问树所有结点的过程称为遍历;

遍历的分类:
前序遍历:DLR
中序遍历:LDR
后序遍历:LRD
层次遍历:

前序遍历:
//时间复杂度O(n),空间复杂度O(n)
void preOrder(BinaryTreeNode root){
    if(root!=null){
        System.out.println(root.getData());
        preOrder(root.getLeft());
        preOrder(root.getRight());
    }
}


非递归前序遍历:
//时间复杂度O(n),空间复杂度O(n)
void preOrderNonRecursive(BinaryTreeNode root){
    if(root==null) return null;
    LStack s = new LStack();
    while(true){
        while(root != null){
            System.out.println(root.getData());
            s.push(root);
            root = root.getLeft();
        }
        if(s.isEmpty()) break;
        root = (BinaryTreeNode) s.pop();
        root = root.getRight();
    }
    return;
}

//中序遍历
//时间复杂度O(n),空间复杂度O(n)
void InOrder(BinaryTreeNode root){
    if(root != null){
        InOrder(root.getLeft());
        System.out.println(root.getData());
        InOrder(root.getRight())
    }
}

//非递归中序遍历
//时间复杂度O(n),空间复杂度O(n)
void InOrderNonRecursive(BinaryTreeNode root){
    if(root == null) return null;
    LStack s = new LStack();
    while(true){
        while(root != null){
            s.push(root);
            root = root.getLeft();
        }
        if(s.isEmpty()) break;
        root = (BinaryTreeNode) s.pop();
        System.out.println(root.getData());
        root = root.getRight();
    } 
    return;
}

//后续遍历
//时间复杂度O(n),空间复杂度O(n)
void PostOrder(BinaryTreeNode root){
    if(root != null){
        PostOrder(root.getLeft());
        PostOrder(root.getRight());
        System.out.println(root.getData());
    }
}

//非递归后序遍历
//时间复杂度O(n),空间复杂度O(n)
//在前序和中序遍历中,当元素出栈后就不需要再次访问该结点啦。但是后续遍历时,
//每个结点需要访问两次。这就意味着,在遍历完左子树后,需要访问该结点,
//之后遍历完右子树时,还需要访问该结点,此时也才能输出该结点。
//所以需要进行判断,是第几次返回该结点,即让最新出栈的元素和栈顶元素的
//右孩子结点比较,如果相同,则是第二次访问,可以输出;反之,不行。
void PostOrderNonRecursive(BinaryTreeNode root){
    if(root == null) return null;
    LStack s = new LStack();
    while(true){
        if(root != null){
            s.push(root);
            root = root.getLeft();
        }else{
            if(s.isEmpty()){
                System.out.println("Stack is Empty");
                return; 
            }else{
                if(s.top().getRight() == null){
                    root = s.top();
                    System.out.println(root.getData());
                    if(root == s.top().getRight()){
                        System.out.println(s.top().getData());
                        s.pop();
                    }
                }
                if(!s.isEmpty()){
                    root = s.top().getRight();
                }else 
                    root = null;
            }
        }
    } 
    s.deleteStack();//清空栈
}

//层次遍历
//利用队列,在访问第l层的时,将其相关的l+1层按顺序保存在队列中。
//时间复杂度O(n),空间复杂度O(n)
void LevelOrder(BinaryTreeNode root){
    BinaryTreeNode temp;
    LQueue q = new LQueue();
    if(root == null){
        return;
    }
    q.enQueue(root);
    while(!q.isEmpty()){
        temp = q.deQueue();
        System.out.println(temp.getData());
        //输出该结点后,将其下一层的左右孩子结点存入队列中
        if(temp.getLeft())
            q.enQueue((temp.getLeft()));
        if(temp.getRight())
            q.enQueue((temp.getRight()));
    }
    q.deleteQueue();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值