Day10——二叉树存储

为什么引用 (指针) 是无法存储到文件里面呢?
因为文件都是存储在磁盘上的,也就是外存,但我们在执行程序的时候,是需要将程序对应的进程加载到内存中,并且进程在内存中的地址不是固定的,这表明我们不能保证保存在文件中的指针在下次运行时所指向的内容还是我们之前所指向的内容。

所以我们该怎么保存二叉树呢?
保存结点之间的逻辑关系,就像数组一样,虽然每次运行时内存地址不一样,但它们的逻辑关系是确定的,我们就可以根据元素之间的逻辑关系和运行时的数组首地址来保存和访问各个元素。

介绍两种特殊的二叉树:
满二叉树:树中的每层都含有最多的结点,即叶子节点都在二叉树的最下一层并且除了叶子结点外的所有结点的度都为2。如果树高为h,则一共有个结点。如果按照层序编号:根结点开始从0为每个结点编号,自上而下,自左向右,则可以表示为:

完全二叉树:如果树高为h,除了第h层,其它各层(1~h-1)的结点数都达到最大个数,且第h层的结点都依次排列在该层的最左边的位置上。对完全二叉树进行层序编号可表示为:

如果用一个顺序表来记录这些结点,可表示为:

因此,我们可以把一棵普通的二叉树补充为一棵完全二叉树,用’\0’来表示补充的结点,然后用数组来保存各个结点的值及其父子关系。设父结点的层次序号为i ,则其左孩子的序号为 ,右孩子的序号为

但在实际运用中,这样的效果并不好,因为补充的完全二叉树有很多空结点,再采用这种方式会造成很多空间浪费,得不偿失了。

那我们就按照补充为完全二叉树的思路,但只记录实际存在的结点,用另一个数组来记录层次序号,两个数组通过数组索引实现层次序号和二叉树结点一一对应。

那么我们该怎么实现呢?
完全二叉树的层次序号的特点:自上而下,自左而右的编号。这也是二叉树的一次层次遍历。
层次遍历的特点:从根结点出发,获取根结点的值,然后获取根结点的左 孩 子的值,再是根结点的右 孩 子然后是根结点的左 孩 子,根结点左 孩 子 的右孩子;根结点右 孩 子的左孩子,根结点右 孩 子的右孩子……这刚好和队列的特点一致,获取出队元素,如果当前处理的结点存在孩子结点则让其孩子结点入队(满足从左到右的特点,先判断左孩子,再判断右孩子),直到队列为空。初始化队列:让根结点入队。

序号处理:当处理当前结点时,可以根据当前结点计算出其子结点的层次序号,然后将获得的值压入另一个队列中。

序号队列代码:

package day07;

public class CircleIntQueue {

    /**
     * The total space. One space can never be used.
     */
    public static final int TOTAL_SPACE = 10;

    /**
     * The data.
     */
    int[] data;

    /**
     * The index for calculation the head. The actual head is head % TOTAL_SPACE.
     */
    int head;

    /**
     * The index for calculating the tail.
     */
    int tail;

    public CircleIntQueue() {
        data = new int[TOTAL_SPACE];
        head = 0;
        tail = 0;
    }// Of the first constructor

    /**
     * Enqueue.
     *
     * @param paraValue The value of the new node.
     */
    public void enqueue(int paraValue) {
        if ((tail + 1) % TOTAL_SPACE == head) {
            System.out.println("Queue full.");
            return;
        } // Of if

        data[tail % TOTAL_SPACE] = paraValue;
        tail++;
    }// Of enqueue;

    /**
     * Dequeue.
     *
     * @return The value at the head.
     */
    public int dequeue() {
        if (head == tail) {
            System.out.println("No element in the queue");
            return -1;
        } // Of if

        int resultValue = data[head % TOTAL_SPACE];

        head++;

        return resultValue;
    }// Of dequeue

    /**
     * Overrides the method claimed in Object, the superclass of any class.
     */
    public String toString() {
        String resultString = "";

        if (head == tail) {
            return "empty";
        } // Of if

        for (int i = head; i < tail - 1; i++) {
            resultString += data[i % TOTAL_SPACE] + ", ";
        } // Of for i

        resultString += data[(tail - 1) % TOTAL_SPACE];
        return resultString;
    }// Of toString

    /**
     * The entrance of the program.
     *
     * @param args Not used now.
     */
    public static void main(String args[]) {
        CircleIntQueue tempQueue = new CircleIntQueue();
        System.out.println("Initialized, the list is: " + tempQueue.toString());

        for (int i = 0; i < 5; i++) {
            tempQueue.enqueue(i + 1);
        } // Of for i
        System.out.println("Enqueue, the queue is: " + tempQueue.toString());

        int tempValue = tempQueue.dequeue();
        System.out.println("Dequeue " + tempValue + ", the queue is: " + tempQueue.toString());

        for (int i = 0; i < 6; i++) {
            tempQueue.enqueue(i + 10);
            System.out.println("Enqueue, the queue is: " + tempQueue.toString());
        } // Of for i

        for (int i = 0; i < 3; i++) {
            tempValue = tempQueue.dequeue();
            System.out.println("Dequeue " + tempValue + ", the queue is: " + tempQueue.toString());
        } // Of for i

        for (int i = 0; i < 6; i++) {
            tempQueue.enqueue(i + 100);
            System.out.println("Enqueue, the queue is: " + tempQueue.toString());
        } // Of for i
    }// Of main
}// Of class CircleIntQueue


结点队列代码:

package day10;

/**
 * Circle Object queue.
 * 
 * @author Zhong Xiyan 976078956@qq.com
 */
public class CircleObjectQueue {

    /**
     * The total space. One space can never be used.
     */
    public static final int TOTAL_SPACE = 10;

    /**
     * The data.
     */
    Object[] data;

    /**
     * The index of the head.
     */
    int head;

    /**
     * The index of the tail.
     */
    int tail;

    /**
     * 
     *********************
     * The constructor
     *********************
     *
     */
    public CircleObjectQueue() {
        data = new Object[TOTAL_SPACE];
        head = 0;
        tail = 0;
    }// Of the first constructor

    /**
     * 
     *********************
     * @Title: enqueue
     * @Description: TODO(Enqueue)
     *
     * @param paraValue The value of the new node.
     *********************
     *
     */
    public void enqueue(Object paraValue) {
        if ((tail + 1) % TOTAL_SPACE == head) {
            System.out.println("Queue full.");
            return;
        } // Of if

        data[tail % TOTAL_SPACE] = paraValue;
        tail++;
    }// Of enqueue;

    /**
     * 
     *********************
     * @Title: dequeue
     * @Description: TODO(Dequeue)
     *
     * @return The value at the head.
     *********************
     *
     */
    public Object dequeue() {
        if (head == tail) {
            System.out.println("No element in the queue");
            return null;
        } // Of if

        Object resultValue = data[head % TOTAL_SPACE];

        head++;

        return resultValue;
    }// Of dequeue

    /**
     * Overrides the method claimed in Object, the superclass of any class.
     */
    public String toString() {
        String resultString = "";

        if (head == tail) {
            return "empty";
        } // Of if

        for (int i = head; i < tail - 1; i++) {
            resultString += data[i % TOTAL_SPACE] + ", ";
        } // Of for i

        resultString += data[(tail - 1) % TOTAL_SPACE];
        return resultString;
    }// Of toString

    /**
     * 
     *********************
     * @Title: main
     * @Description: TODO(The entrance of the program.)
     *
     * @param args Not used now.
     *********************
     *
     */
    public static void main(String args[]) {
        CircleObjectQueue tempQueue = new CircleObjectQueue();
    }// Of main
}// Of class CircleObjectQueue


二叉树存储:

package day10;

import java.util.Arrays;

import day07.CircleIntQueue;

public class BinaryCharTree {

    /**
     * The value in char.
     */
    char value;

    /**
     * The left child.
     */
    BinaryCharTree leftChild;

    /**
     * The right child.
     */
    BinaryCharTree rightChild;

    /**
     * 
     *********************
     * The first constructor.
     * 
     * @param paraName The value.
     *********************
     *
     */
    public BinaryCharTree(char paraName) {
        value = paraName;
        leftChild = null;
        rightChild = null;
    }// Of the constructor

    /**
     * 
     *********************
     * @Title: manualConstructTree
     * @Description: TODO(Manually construct a tree. Only for testing.)
     *
     * @return A binary tree.
     *********************
     */
    public static BinaryCharTree manualConstructTree() {
        // Step 1. Construct a tree with only one node.
        BinaryCharTree resultTree = new BinaryCharTree('a');

        // Step 2. Construct all nodes. The first node is the root.
        BinaryCharTree tempTreeB = new BinaryCharTree('b');
        BinaryCharTree tempTreeC = new BinaryCharTree('c');
        BinaryCharTree tempTreeD = new BinaryCharTree('d');
        BinaryCharTree tempTreeE = new BinaryCharTree('e');
        BinaryCharTree tempTreeF = new BinaryCharTree('f');
        BinaryCharTree tempTreeG = new BinaryCharTree('g');

        // Step 3. Link all nodes.
        resultTree.leftChild = tempTreeB;
        resultTree.rightChild = tempTreeC;
        tempTreeB.rightChild = tempTreeD;
        tempTreeC.leftChild = tempTreeE;
        tempTreeD.leftChild = tempTreeF;
        tempTreeD.rightChild = tempTreeG;

        return resultTree;
    }// Of manualConstructTree

    /**
     * 
     *********************
     * @Title: getNumNodes
     * @Description: TODO(Get the number of nodes)
     *
     * @return The number of nodes.
     *********************
     *
     */
    public int getNumNodes() {
        // It is a leaf.
        if (leftChild == null && rightChild == null) {
            return 1;
        } // Of if

        int leftChildNodes = 0, rightChildNodes = 0;

        // Get the number of nodes of the left child.
        if (leftChild != null) {
            leftChildNodes = leftChild.getNumNodes();
        } // Of if

        // Get the number of nodes of the right child.
        if (rightChild != null) {
            rightChildNodes = rightChild.getNumNodes();
        } // Of if

        // The total number of nodes.
        return leftChildNodes + rightChildNodes + 1;
    }// Of getNumNodes

    /**
     * The values of nodes according to breadth first traversal.
     */
    char[] valuesArray;

    /**
     * The indices in the complete binary tree.
     */
    int[] indicesArray;

    /**
     * 
     *********************
     * @Title: toDataArrays
     * @Description: TODO(Convert the tree to data arrays, including a char array
     *               and an int array. The results are stored in two member
     *               variables)
     * 
     * @see #valuesArray
     * @see #indicesArray
     *
     *********************
     *
     */
    public void toDataArrays() {
        // Initialize arrays.
        int tempLength = getNumNodes();

        valuesArray = new char[tempLength];
        indicesArray = new int[tempLength];
        int i = 0;

        // Traverse and convert at the same time.
        CircleObjectQueue tempQueue = new CircleObjectQueue();
        tempQueue.enqueue(this);
        CircleIntQueue tempIntQueue = new CircleIntQueue();
        tempIntQueue.enqueue(0);

        BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue();
        int tempIndex = tempIntQueue.dequeue();
        while (tempTree != null) {
            valuesArray[i] = tempTree.value;
            indicesArray[i] = tempIndex;
            i++;

            if (tempTree.leftChild != null) {
                tempQueue.enqueue(tempTree.leftChild);
                tempIntQueue.enqueue(tempIndex * 2 + 1);
            } // Of if

            if (tempTree.rightChild != null) {
                tempQueue.enqueue(tempTree.rightChild);
                tempIntQueue.enqueue(tempIndex * 2 + 2);
            } // Of if

            tempTree = (BinaryCharTree) tempQueue.dequeue();
            tempIndex = tempIntQueue.dequeue();
        } // Of while
    }// Of toDataArrays

    /**
     * 
     *********************
     * @Title: main
     * @Description: TODO(The entrance of the program.)
     *
     * @param args Not used now.
     *********************
     *
     */
    public static void main(String args[]) {
        BinaryCharTree tempTree = manualConstructTree();
        tempTree.toDataArrays();
        System.out.println("The values are: " + Arrays.toString(tempTree.valuesArray));
        System.out.println("The indices are: " + Arrays.toString(tempTree.indicesArray));
    }// Of main

}// Of class BinaryCharTree


运行结果:

————————————————
版权声明:本文为CSDN博主「颜妮儿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Z__XY_/article/details/124062703

### 二叉树的数组存储实现 #### 使用数组存储二叉树的概念 二叉树可以通过数组来进行存储,这种方式特别适用于完全二叉树或接近完全二叉树的情况。对于一般的二叉树,如果存在缺失节点,则这些位置会在数组中用特定值(通常是`0`或其他特殊标记)填充[^1]。 #### 存储规则 当使用数组来表示一棵二叉树时,根节点位于索引为`1`的位置(假设基于1的索引)。对于任意给定的父节点i: - 左子节点存放在索引 `2*i` 处; - 右子节点存放在索引 `2*i + 1` 处; 这种映射关系使得通过简单的计算即可访问任何节点的孩子节点,从而简化了某些类型的遍历算法的设计和实现。 #### 示例代码展示如何构建并打印先序遍历的结果 下面是一个Python函数的例子,它接受一个列表作为输入参数,该列表代表按照上述规则编码后的二叉树,并返回其先序遍历序列: ```python def preorder_traversal(tree_array, index=1): result = [] if index >= len(tree_array) or tree_array[index] == 0: return [] # 访问当前节点 (Pre-order visit) result.append(tree_array[index]) # 遍历左子树 result.extend(preorder_traversal(tree_array, 2 * index)) # 遍历右子树 result.extend(preorder_traversal(tree_array, 2 * index + 1)) return result # 测试例子 test_cases = [ [None, 3, 1, 2], [None, 5, 1, 2, None, 4], [None, 13, 1, 2, 3, 4, None, 5, 6, 7, 8, None, None, 9, 10] ] for case in test_cases: print(' '.join(map(str, preorder_traversal(case)))) ``` 这段程序会输出与提供的测试案例相匹配的结果字符串,其中每个数字由单个空格分隔开来[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值