二叉树顺序存储和遍历

1 二叉树的存储

1.1 顺序存储

使用数组自上而下,自左至右存储完全二叉树上的结点元素,即将完全二叉树上编号为i的结点元素存储在某个数组下标为i-1的分量中,然后通过一些方法确定结点在逻辑上的父子和兄弟关系。

根据二叉树的性质,完全二叉树和满二叉树树采用顺序存储比较合适,树中结点的序号可以唯一地反映出结点之间的逻辑关系,既能节省存储空间,又能利用数组元素下标值确定结点在二叉树中的位置,以及结点之间的关系。

而对于一般的二叉树也必须按照完全二叉树的形式存储,也就是必须添加一些并不存在的虚拟结点,造成空间的浪费。

图1 二叉树顺序存储

顺序存储的关键是数组下标确定结点的位置,如结点从1开始编号,那么结点i的左孩子为2*i,右孩子为2*i+1。不存在结点用0表示。

先回忆一下二叉树的一些性质:

(1) 非空二叉树k最多有2k-1个结点

(2) 高度为h的二叉树至多有2h-1个结点

首先按照树的高度height初始化一颗树,以及一些有用的方法,代码如下:

public class ArrayBiTree<T> {
    private Object[] data;
    private int height = 3; // 树的高度 默认为3
    private int n; // 结点个数
    public ArrayBiTree() {
        data = new Object[(int) Math.pow(2, height)];
        init();
    }
    /**
     * 指定深度初始化一个树
     * @param height 树的深度
     */
    public ArrayBiTree(int height) {
        this.height = height;
        data = new Object[(int) Math.pow(2, height) - 1];
    }
    private void init(){
        System.out.println("默认生成一颗完全二叉树,高度为3:");
        for(int i=0; i<(int) Math.pow(2, height) - 1; i++){
            data[i] = i+1;
            n++;
        }
        print();
    }
    /**
     * 判断结点是否存在
     * @param index 根节点从 1 开始
     * @return
     */
    public boolean isExist(int index){
        if(index > n) return false;
        return Integer.valueOf(data[index-1].toString()) != 0;
    }
}

1.2 层次遍历

/**
 * 层次遍历,利用队列是实现
 */
public void levelOrder(){
    RingBuffer<Integer> queue = new RingBuffer<Integer>(n+1);
    queue.put(1); // 根节点先进队列

    while(queue.size()>0){
        int tmp = queue.get();
        System.out.print(data[tmp-1]+" ");

        if (isExist(2 * tmp)) { // 如果左子树存在,把左子树编号入栈
            queue.put(2 * tmp);
        }

        if (isExist(2 * tmp + 1)) {  // 如果右子树存在,把右子树编号入栈,
            queue.put(2 * tmp + 1);
        }
    }
}

1.3 先序遍历

1.3.1 递归实现

/**
 * 先序遍历,递归实现Recursion
 * @param index 根节点从 1 开始
 */
public void preOrderRecur(int index){
    if(isExist(index)){ //判断结点是否存在
        System.out.print(data[index-1]+" "); // 访问根节点
        preOrderRecur(2*index); // 递归遍历左子树
        preOrderRecur(2*index + 1); // 递归遍历右子树
    }
}

1.3.2 非递归实现

实现方法1:

/**
 * 先序遍历,非递归实现,借助栈来实现<p>
 * 根节点先入栈,访问栈顶结点,若栈顶元素的右孩子存在则入栈,若栈顶元素的左孩子存在则入栈,如此循环直到栈空
 */
public void preOrder(){
    ArrayStack<Integer> stack = new ArrayStack<Integer>(n);
    stack.push(1); // 根节点入栈
    while(!stack.isEmpty()){
        int tmp = stack.pop(); // 取根结点,把每个结点都看作根节点
        System.out.print(data[tmp-1]+" "); // 访问根结点

        if (isExist(2 * tmp + 1)) {  // 如果根节点的右子树存在,把右子树编号入栈
            stack.push(2 * tmp + 1);
        }

        if (isExist(2 * tmp)) { // 如果根节点的左子树存在,把左子树编号入栈
            stack.push(2 * tmp);
        }
    }
}

实现方法2:

/**
 * 先序遍历1,非递归实现,借助栈来实现<p>
 * @param index 根节点从 1 开始
 */
public void preOrderOne(int index){
    ArrayStack<Integer> stack = new ArrayStack<Integer>(n);
    while (isExist(index) || !stack.isEmpty()) {
        // (1) 首先访问根节点,一直往左下方走,直到一个左孩子不存在的结点。
        while (isExist(index)) {
            System.out.print(data[index - 1] + " ");
            stack.push(index); // 根节点入栈,把每个结点都看作一个根节点,检查其左右孩子是否存在
            index = 2 * index;
        }
        // 此时,栈内是从根节点左孩子开始的左孩子,最后一个结点是不存在左孩子的结点
        // (2) 拿栈顶元素,看其右孩子是否存在,把当前结点置为其右孩子,继续循环判断(1)
        if (!stack.isEmpty()) {
            int tmp = stack.pop(); // 弹出的左子树结点
            index = 2 * tmp + 1; // 看它的右孩子是否存在
        }
    }
}

1.4 中序遍历

1.4.1 递归实现

/**
 * 中序遍历,递归实现Recursion
 * @param index 根节点从 1 开始
 */
public void inOrderRecur(int index){
    if(isExist(index)){
        inOrderRecur(2*index); // 递归遍历左子树
        System.out.print(data[index-1]+" "); // 访问根节点
        inOrderRecur(2*index + 1); // 递归遍历右子树
    }
}

1.4.2 非递归实现

/**
 * 中序遍历,非递归实现,更改访问时机即可
 * @param index
 */
public void inOrder(int index){
    ArrayStack<Integer> stack = new ArrayStack<Integer>(n);
    while(isExist(index) || !stack.isEmpty()){
        while(isExist(index)){
            stack.push(index); // 根节点入栈
            index = 2 * index; // 是否存在左孩子
        }
        if(!stack.isEmpty()){
            int tmp = stack.pop(); // 弹出左孩子
            System.out.print(data[tmp-1]+" "); // 访问结点
            index = 2 * tmp + 1; // 看左孩子的右孩子是否存在
        }
    }
}

1.5 后序遍历

1.5.1 递归实现

/**
 * 后序遍历,递归实现Recursion
 * @param index 根节点从 1 开始
 */
public void postOrderRecur(int index){
    if(isExist(index)){
        postOrderRecur(2*index); // 递归遍历左子树
        postOrderRecur(2*index + 1); // 递归遍历右子树
        System.out.print(data[index-1]+" "); // 访问根节点
    }
}

1.5.2 非递归实现

/**
 * 后序遍历,非递归实现<p>
 * 与前中序相比实现比较麻烦,先访问左子树再访问右子树
 */
public void postOrder(int index){
    ArrayStack<Integer> stack = new ArrayStack<Integer>(n);
    int visited = 0; // 标记前一个已被访问的结点
    while(isExist(index) || !stack.isEmpty()){
        while(isExist(index)){
            stack.push(index); // 根节点入栈
            index = 2 * index;
        }// 先把 index 的左孩子全部找到 

        int top = stack.peek(); // 查看栈顶元素,没有弹出,访问完右孩子之后在弹出访问根节点

        // 如果当前结点不存在右孩子或者右孩子已经访问过,则访问当前结点
        if(!isExist(2*top+1) || (2*top+1) == visited){
            int tmp = stack.pop();
            System.out.print(data[tmp-1]+" ");
            visited = tmp;
        } else { // 否则访问右孩子
            index = 2 * top + 1;
        }
    }
}

2 测试

public static void main(String[] args) {
    ArrayBiTree<Integer> biTree = new ArrayBiTree<Integer>();
    System.out.print("先序遍历(递归):");
    biTree.preOrderRecur(1);
    System.out.print("\n中序遍历(递归):");
    biTree.inOrderRecur(1);
    System.out.print("\n后序遍历(递归):");
    biTree.postOrderRecur(1);
    System.out.print("\n层次遍历:");
    biTree.levelOrder();

    System.out.print("\n先序遍历(非递归):");
    biTree.preOrder();
//        biTree.preOrderOne(1);
    System.out.print("\n中序遍历(非递归):");
    biTree.inOrder(1);
    System.out.print("\n后序遍历(非递归):");
    biTree.postOrder(1);
    System.out.println();
    biTree.stdIn();
}

2.1 输出结果

时间: 2024-10-28 17:12:26

二叉树顺序存储和遍历的相关文章

[数据结构]二叉树创建与遍历

实验报告:二叉树创建与遍历 一.问题描述 二叉树是一种实用范围很广的非线性结构,一棵非空二叉树有也只有一个根结点,每个结点最多有两个子树,我们称为左子树与右子树,当一个结点的左.右子树都是空的时,沃恩称此结点为叶子结点. 二叉树有一些很好的性质,这里不再赘述.考虑如何存储一棵树,本实验选择使用链式存储结构——二叉链表:如果事先知道需要存储的二叉树是满二叉树或者完全二叉树,则可以考虑使用顺序存储,否则将浪费大量的存储空间. 对于一棵既成的二叉树,有三种遍历方式——先序.中序与后序.可以证明,一棵形

二叉树三种遍历递归及非递归实现(Java)

import java.util.Stack; //二叉树三种遍历递归及非递归实现(Java) public class Traverse { /******************定义二叉树**************************/ private final int MAX_SIZE = 10; //链式存储 public static class BinaryTreeNode { int mValue; BinaryTreeNode mLeft; BinaryTreeNode

LeetCode 145 Binary Tree Postorder Traversal(二叉树的后续遍历)+(二叉树、迭代)

翻译 给定一个二叉树,返回其后续遍历的节点的值. 例如: 给定二叉树为 {1, #, 2, 3} 1 2 / 3 返回 [3, 2, 1] 备注:用递归是微不足道的,你可以用迭代来完成它吗? 原文 Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary tree {1,#,2,3}, 1 2 / 3 return [3,2,1]. Note: Recur

3143 二叉树的序遍历——http://codevs.cn/problem/3143/

第一部分:题目 题目描述 Description 求一棵二叉树的前序遍历,中序遍历和后序遍历 输入描述 Input Description 第一行一个整数n,表示这棵树的节点个数. 接下来n行每行2个整数L和R.第i行的两个整数Li和Ri代表编号为i的节点的左儿子编号和右儿子编号. 输出描述 Output Description 输出一共三行,分别为前序遍历,中序遍历和后序遍历.编号之间用空格隔开. 样例输入 Sample Input 5 2 3 4 5 0 0 0 0 0 0 样例输出 Sam

根据二叉树的前序遍历和中序遍历重建二叉树

题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回. 1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(

二叉树建立和遍历

二叉树创建遍历规则: 1.先序:根-左-右 2.中序:左-根-右 3.后序:左-右-根 二叉树定义和辅助函数如下: struct node { int data; struct node* left; struct node* right; }; void visit(int data) { printf("%d ", data); } int indata() { int data; scanf("%d",&data); return data; } 先序

二叉树三种遍历(递归以及非递归实现)

package com.shiyeqiang.tree; import java.util.Stack; public class BiTree { public static void main(String[] args) { // 首先构造叶子节点 BiTree leafA1 = new BiTree(4); BiTree leafA2 = new BiTree(5); BiTree leafB1 = new BiTree(6); BiTree leafB2 = new BiTree(7)

二叉树的递归遍历 Tree UVa548

题意:给一棵点带权的二叉树的中序和后序遍历,找一个叶子使得他到根的路径上的权值的和最小,如果多解,那该叶子本身的权值应该最小 解题思路:1.用getline()输入整行字符,然后用stringstream获得字符串中的数字 2.用数组in_oder[]和post_order[]分别表示中序遍历和后序遍历的顺序,用数组rch[]和lch[]分别表示结点的左子树和右子树 3.用递归的办法根据遍历的结果还原二叉树(后序遍历的最后一个树表示的是根节点,中序遍历的中间一个表示根节点) 4.二叉树构造完成后

重建二叉树与二叉树的层次遍历

数据结构实验之求二叉树后序遍历和层次遍历 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描写叙述 已知一棵二叉树的前序遍历和中序遍历,求二叉树的后序遍历. 输入 输入数据有多组,第一行是一个整数t (t<1000).代表有t组測试数据.每组包含两个长度小于50 的字符串,第一个字符串表示二叉树的先序遍历序列,第二个字符串表示二叉树的中序遍历序列. 输出 每组第一行输出二叉树的后序遍历序列,第二行输出二叉树的层次遍历序列 演示样例输