Operation example of binary tree in c language/c++ (data structure) (5/7)

Purpose:

1. Master the storage structure of the binary linked list and master the design of various basic operation algorithms in the binary tree.

2. Master the various traversal processes of binary trees and the design of traversal algorithms.

Experimental content

Programming implementation:

(1) Create the corresponding binary chain storage structure b from the binary tree shown in Figure 1 , the brackets of the binary tree represent the string "A(B(D,E(H(J,K(L,M(,N) )))),C(F,G(,I)))”;

(2) output binary tree b;

(3) Output the left and right child node values ​​of the 'H' node;

(4) Output the height of the binary tree b ;

(5) Write the pre-order, in-order and post-order traversal programs, and output the pre-order, in-order and post-order traversal results of the binary tree;

(6) Write a program to count the number of nodes in the binary tree;

(7) Write a program to count the number of leaf nodes of the binary tree.

Figure 1 A binary tree

 code:

#include <iostream>

#include <queue>

using namespace std;

const int N = 10010;

const int INF = -1; // We use a constant to indicate that the current binary tree node is empty

struct Node {

    int w; // the value of the current tree node

    int p; // the subscript of the array where the parents of the current tree node are located

    int l; // The subscript of the array where the left child node of the current tree node is located

    int r; // 当前树节点的右子节点所在数组下标

};

Node node[N];

// 按照前序遍历二叉树的顺序输入树节点

void input(int n) {

    cin >> node[n].w;

    if(node[n].w == INF) { // 输入 -1 代表当前节点所在子二叉树停止输入

        return ;

    }

    node[n].p = n / 2;

    node[n].l = n * 2;

    node[n].r = n * 2 + 1;

    input(n*2);

    input(n*2+1);

}

// 前序遍历二叉树

void preOrderParse(int n) {

    if(node[n].w == INF) {

        return ;

    }

    cout << node[n].w << " ";

    preOrderParse(node[n].l);

    preOrderParse(node[n].r);

}

// 中序遍历二叉树

void inOrderParse(int n) {

    if(node[n].w == INF) {

        return ;

    }

    inOrderParse(n*2);

    cout << node[n].w << " ";

    inOrderParse(n*2+1);

}

// 后续遍历二叉树

void postOrderParse(int n) {

    if(node[n].w == INF) {

        return ;

    }

    postOrderParse(n*2);

    postOrderParse(n*2+1);

    cout << node[n].w << " ";

}

void sequenceParse() {

    queue<int> que;

    int n = 1;

    que.push(1); // 插入根节点所在数组下标

    while(!que.empty()) {

        n = que.front();

        que.pop(); // 得到队头元素并且将队头元素出队列

        // 如果当前节点不为空,那么输出该节点,并且将该节点的左右子节点插入队尾

        if(node[n].w != INF) {

            cout << node[n].w << " ";

            que.push(node[n].l);

            que.push(node[n].r);

        }

    }

}

int main() {

    cout << "请以前序遍历的顺序输入二叉树,空节点输入 -1 :" << endl;

    input(1); // 从下标为 1 开始前序输入二叉树

    cout << "前序遍历:" << endl;

    preOrderParse(1);

    cout << endl << "中序遍历:" << endl;

    inOrderParse(1);

    cout << endl << "后序遍历:" << endl;

    postOrderParse(1);

    cout << endl << "层序遍历:" << endl;

    sequenceParse();

    return 0;

}

 程序测试及运行结果:

分析与讨论:

1.先序遍历的递归实现:

先序遍历的遍历顺序为根节点–>左孩子–>右孩子,所以往栈中放数时先放右孩子,在放左孩子(左右孩子都不为空的情况下)。根据栈的先进后出的性质,所以先打印左孩子再打印右孩子,但是有的人可能会有疑问,那根节点呢。根节点在往栈中放左右孩子的时候就已经打印完了,所以根节点肯定在左右孩子打印之前就打印完了。

这样打印顺序就为:根节点–>左孩子–>右孩子。

2.中序遍历:

如果当前节点不为空,就往栈中放入该节点,然后让指向该节点的指针指向该节点的左孩子。如果当前节点为空,就弹出栈顶的节点,打印。然后让指向该节点的指针指向该节点的右孩子

3.后序遍历

后序遍历的遍历顺序为左孩子–>右孩子–>根节点,将这个顺序倒过来:根节点–>右孩子–>左孩子。和前序遍历是不是很像呢。对:前序遍历的顺序:根节点–>左孩子–>右孩子。那么,实现后序遍历只需要先往栈中放根节点的左孩子,然后再放右孩子。那么打印的顺序就成了根节点–>右孩子–>左孩子。但是,我们先不着急打印,利用栈结构能够使一段序列逆序的性质,将第一个栈中的节点全部放到第2个栈中去,那么打印顺序就发生了逆序:左孩子–>右孩子–>根节点,正好是后序遍历的顺序。

Guess you like

Origin blog.csdn.net/qq_59819866/article/details/131451208