Data structure - binary tree front, middle and back order traversal (sequential storage/chain storage & recursive/non-recursive)

1 Storage and establishment of binary tree

1.1 Sequential storage structure

1.1.1 What is a sequential storage structure

The sequential storage structure of the binary tree is to store all the nodes in the binary tree in a one-dimensional array   according to the sequential traversal method , so as to realize the storage and traversal of the binary tree. If the left child node or right child node of a node is empty, then a special value is stored in the corresponding array position, such as null or -1, indicating that the node does not exist. For example, a schematic diagram of a binary tree and its sequential storage structure is as follows:

insert image description here

1.1.2 Code example

insert image description here
For example, the above binary tree is stored in a sequential storage structure :

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 11

int tree[MAX_SIZE] = {
    
    0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 6}; //一般从下标1开始存储,更利于后续计算

1.2 Binary linked list storage

1.2.1 What is a chained storage structure

insert image description here

  The linked storage structure of the binary tree is a commonly used way to store the binary tree, that is, a binary linked list is used for storage. In this storage structure, each node contains a data field and two pointer fields, pointing to its left child node and right child node respectively . The entire binary tree is linked by pointers to form a chain structure.

1.2.2 Code example

insert image description here
For example, the above binary tree is stored in a chained storage structure :

#include <stdio.h>
#include <stdlib.h>

typedef struct node {
    
    
    int data;
    struct node *left;
    struct node *right;
} Node;

Node *createNode(int data) {
    
    
    if (data == 0) return NULL;
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = data;
    newNode->left = NULL;
    newNode->right = NULL;
    return newNode;
}

int main() {
    
    
    int tree[] = {
    
    0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 6, 0};
    int n = sizeof(tree) / sizeof(tree[0]);

    Node *nodes[n]; //创建了一个大小为n的指针数组nodes
    for (int i = 0; i < n; i++) {
    
    
        nodes[i] = createNode(tree[i]);
    }

    for (int i = 0; i < n; i++) {
    
    
        if (nodes[i] != NULL) {
    
    
        //假设一个节点在数组中的下标为 i (根结点编号为1),那么它的左孩子节点和右孩子节点的下标分别为 2 * i 和 2 * i + 1
            nodes[i]->left = nodes[2 * i];
            nodes[i]->right = nodes[2 * i + 1];
        }
    }

    return 0;
}

  This code defines a Nodestructure representing a node in a binary tree. createNodeFunctions are used to create new nodes. In mainthe function , an array is created tree, and then the array is traversed, creating a node for each non-zero element. Then, traverse the array again, and link the nodes according to their positions in the array to form a binary tree.
  In summary, the storage of the binary tree is realized by using the binary linked list.

1.3 Comparison between sequential storage structure and chained storage structure

Advantages of binary tree sequential storage structure :
Fast access to nodes : You can directly access any node through the array subscript.
Continuous storage space : All nodes are stored in a continuous memory space for easy management.

Disadvantages of binary tree sequential storage structure:
Waste of space : For an incomplete binary tree, there will be a lot of vacancies in the array, resulting in waste of space.
Insertion and deletion operations are difficult : a large number of elements need to be moved to maintain the tree structure.

Advantages of binary tree chain storage structure:
High space utilization : each node only occupies the space actually needed, and will not cause space waste.
Simple insertion and deletion operations : only need to modify the pointer to complete the insertion and deletion operations.

Disadvantages of binary tree chain storage structure:
Slow node access : You need to traverse the linked list to access the specified node.

  In short, the binary tree sequential storage structure is suitable for the complete binary tree and the full binary tree or the situation with a small number of nodes, while the binary tree chain storage structure is suitable for the situation of the incomplete binary tree or the large number of nodes.

1.4 Supplementary knowledge

nIn the binary linked list containing nodes, there are n+1empty chain domains
② Assume that the subscript of a node in the array is i(the root node number is 1), then the subscripts of its left child node and right child node are respectively for 2 * i and2 * i + 1

2 Traversal of a binary tree

2.1 Recursive algorithm

The following figure is an example
insert image description here

2.1.1 Sequential storage structure

#include <stdio.h>
#define MAXSIZE 100

int tree[MAXSIZE] = {
    
    0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 6};

void preOrder(int index) {
    
    
    if (tree[index] == 0 || index >= MAXSIZE) return;
    printf("%d ", tree[index]);  //根
    preOrder(index * 2);	//左
    preOrder(index * 2 + 1);  //右
}

void inOrder(int index) {
    
    
    if (tree[index] == 0 || index >= MAXSIZE) return;
    inOrder(index * 2);  //左
    printf("%d ", tree[index]); //根
    inOrder(index * 2 + 1); //右
}

void postOrder(int index) {
    
    
    if (tree[index] == 0 || index >= MAXSIZE) return; 
    postOrder(index * 2); //左
    postOrder(index * 2 + 1);  //右
    printf("%d ", tree[index]);  //根
}

void levelOrder() {
    
    
    for (int i = 1; i < MAXSIZE; i++) {
    
    
        if (tree[i] != 0) printf("%d ", tree[i]);  //层序遍历直接依次输出数组各个元素即可
    }
}

int main() {
    
    
    printf("前序遍历: ");
    preOrder(1);
    printf("\n中序遍历: ");
    inOrder(1);
    printf("\n后序遍历: ");
    postOrder(1);
    printf("\n层序遍历: ");
    levelOrder();
}

The output of this code is:

前序遍历: 1 2 4 6 3 5 
中序遍历: 2 6 4 1 3 5
后序遍历: 6 4 2 5 3 1
层序遍历: 1 2 3 4 5 6

2.1.2 Chain storage structure

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100

typedef struct BiTNode {
    
    
    int data;
    struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

//递归创建二叉树,注意是使用二级指针创建二叉树
void CreateBiTree(BiTree *T, int *a, int i) {
    
    
    if (a[i] == 0 || i > MAXSIZE) {
    
    
        *T = NULL;
    } else {
    
    
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T)->data = a[i];
        CreateBiTree(&(*T)->lchild, a, 2 * i);
        CreateBiTree(&(*T)->rchild, a, 2 * i + 1);
    }
}

void PreOrderTraverse(BiTree T) {
    
    
    if (T == NULL) {
    
    
        return;
    }
    printf("%d ", T->data);  //根
    PreOrderTraverse(T->lchild);  //左
    PreOrderTraverse(T->rchild); //右
}

void InOrderTraverse(BiTree T) {
    
    
    if (T == NULL) {
    
    
        return;
    }
    InOrderTraverse(T->lchild); //左
    printf("%d ", T->data); //根
    InOrderTraverse(T->rchild); //右
}

void PostOrderTraverse(BiTree T) {
    
    
    if (T == NULL) {
    
    
        return;
    }
    PostOrderTraverse(T->lchild); //左
    PostOrderTraverse(T->rchild); //右
    printf("%d ", T->data); //根
}

//层序一般使用队列进行辅助
void LevelOrderTraverse(BiTree T) {
    
    
    BiTree queue[MAXSIZE];
    int front = 0, rear = 0;
    BiTree p;
    if (T != NULL) {
    
    
        queue[rear++] = T;
        while (front != rear) {
    
    
            p = queue[front++];
            printf("%d ", p->data);
            if (p->lchild != NULL) {
    
    
                queue[rear++] = p->lchild;
            }
            if (p->rchild != NULL) {
    
    
                queue[rear++] = p->rchild;
            }
        }
    }
}

int main() {
    
    
    int a[MAXSIZE] = {
    
    0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 6};
    BiTree T;
    CreateBiTree(&T, a, 1);

    printf("前序遍历: ");
    PreOrderTraverse(T);
    printf("\n");

    printf("中序遍历: ");
    InOrderTraverse(T);
    printf("\n");

    printf("后序遍历: ");
    PostOrderTraverse(T);
    printf("\n");

    printf("层序遍历: ");
    LevelOrderTraverse(T);
    printf("\n");

    return 0;
}

The output of this code is:

前序遍历: 1 2 4 6 3 5 
中序遍历: 2 6 4 1 3 5
后序遍历: 6 4 2 5 3 1
层序遍历: 1 2 3 4 5 6

2.2 Non-recursive algorithm

insert image description here
We implement the code on the basis of the chain storage structure

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100

typedef struct BiTNode {
    
    
	int data;
	struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

//递归建立二叉树
void CreateBiTree(BiTree *T, int *a, int i) {
    
    
	if (a[i] == 0 || i > MAXSIZE) {
    
    
		*T = NULL;
	} else {
    
    
		*T = (BiTree)malloc(sizeof(BiTNode));
		(*T)->data = a[i];
		CreateBiTree(&(*T)->lchild, a, 2 * i);
		CreateBiTree(&(*T)->rchild, a, 2 * i + 1);
	}
}

void PreOrderTraverse(BiTree T) {
    
    
	BiTree stack[MAXSIZE];
	int top = -1;
	BiTree p = T;
	while (p || top != -1) {
    
    
		while (p) {
    
    
			printf("%d ", p->data);
			stack[++top] = p;
			p = p->lchild;
		}
		if (top != -1) {
    
    
			p = stack[top--];
			p = p->rchild;
		}
	}
}

void InOrderTraverse(BiTree T) {
    
    
	BiTree stack[MAXSIZE];
	int top = -1;
	BiTree p = T;
	while (p || top != -1) {
    
    
		while (p) {
    
    
			stack[++top] = p;
			p = p->lchild;
		}
		if (top != -1) {
    
    
			p = stack[top--];
			printf("%d ", p->data);
			p = p->rchild;
		}
	}
}

void PostOrderTraverse(BiTree T) {
    
    
	BiTree stack[MAXSIZE];
	int top = -1;
	BiTree p = T;
	BiTree r = NULL;
	while (p || top != -1) {
    
    
		if (p) {
    
    
			stack[++top] = p;
			p = p->lchild;
		} else {
    
    
			p = stack[top];
			if (p->rchild && p->rchild != r) {
    
    
				p = p->rchild;
				stack[++top] = p;
				p = p->lchild;
			} else {
    
    
				p = stack[top--];
				printf("%d ", p->data);
				r = p;
				p = NULL;
			}
		}
	}
}


int main() {
    
    
	int a[MAXSIZE] = {
    
    0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 6};
	BiTree T;
	CreateBiTree(&T, a, 1);
	
	printf("前序遍历: ");
	PreOrderTraverse(T);
	printf("\n");
	
	printf("中序遍历: ");
	InOrderTraverse(T);
	printf("\n");
	
	printf("后序遍历: ");
	PostOrderTraverse(T);
	printf("\n");
	
	return 0;
}

2.2.1 Possible Difficulties

1. Why CreateBiTree(BiTree *T, int *a, int i)should be defined as instead BiTree *Tof BiTree T?

  Same thing with the following code:

void swap(int *a, int *b) {
    
    
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    
    
    int x = 1, y = 2;
    swap(&x, &y);
    printf("x = %d, y = %d\n", x, y);
    return 0;
}

  For the above example, simply speaking, if you want to modify the tree inside the function, you must modify the pointer type so that the modified result can be brought out of the function.
  Now we go back to the original code, in CreateBiTree(&T, a, 1);which Tis a pointer to the structure BiNode, we CreateBiTreecreate a binary tree in the method, in order to bring out the modified results, so we need to define BiTree *Tthis secondary pointer to modify the pointer to the structure T. Then we can PreOrderTraverse(T);InOrderTraverse(T);···pass in T in a series of methods to traverse.
  If it is defined BiTree T, it is equivalent to only operating on the formal parameters, and the modification and creation of the binary tree cannot be realized. In simple terms, the operations in the function cannot be passed out, and cannot be passed to the user. If it is not modified, the following cannot be Tcontinued T. The traversal works now.

3 Examples of real questions for the postgraduate entrance examination

1、
insert image description here



analyze
insert image description here
2、insert image description here



analyze
insert image description here

3、insert image description here



analyze
insert image description here

4、insert image description here



analyzeinsert image description here
insert image description here

Comes with a wallpaper ヽ( ^ー^)人(^ー^ )ノ
insert image description here

Guess you like

Origin blog.csdn.net/m0_56494923/article/details/129798075