Experiment 2: Implementation and Application of Tree Data Structure

  • Students of Dongguan Institute of Technology can learn from it, please do not plagiarize

1. Purpose of the experiment

Achieved through experiments:

  1. Understand and master the basic concepts of trees and binary trees;

  2. Understand and master the sequential storage structure and chain storage structure of the binary tree;

  3. Understand and master the ideas and applications of various traversal operations of binary trees under the binary chain storage structure;

  4. Deepen the understanding of the concepts of stacks and queues and their typical operating ideas;

  5. Master the algorithm analysis of typical binary tree operation algorithms.

2. Experiment topic: establishment, traversal and application of binary tree

Set the element type of the tree node as ElemType (which can be char or int), and store it in a binary chain (or triple chain, that is, parents and children), and realize the following programs for various basic operations of the binary tree:

① Write a function to create a binary tree, and create a binary tree T with no less than 10 nodes through file reading (it is recommended to create it recursively);

② Given an element x, search for the element x in the binary tree, and return the pointer of the node if found;

③ Print the binary tree in concave notation (it can be in the form of Figure 8-2 or 90° counterclockwise in 8.4.3, one preorder and one inorder RDL);

④ Use non-recursive pre-order traversal to output the nodes of the tree T; (using the stack)

⑤ Use in-order or post-order traversal to output the nodes of the tree T;

⑥ Output the nodes of the tree T by hierarchical traversal; (queue is used)

⑦ The depth of the output tree T;

⑧ Leaf nodes or non-leaf nodes of the output tree T;

⑨ The main function design menu, select the corresponding function call through the menu to realize the above operations. (Please draw the binary tree of the test in the lab report.) Additional questions: (5 extra points for each one completed, the upper limit is 10 points)

① 8-32 Determine whether the binary tree is a complete binary tree (hierarchical traversal);

② 8-34 Build a binary tree stored in a binary chain based on sequential storage (similar to the first operation);

③ Design and implementation of Huffman tree coding problem (parent-child notation + flag).

An example of a binary tree of expressions:insert image description here

2.1. Data structure design

typedef char ElemType;

typedef struct Node {
    
    
	ElemType value;
	struct Node* left;
	struct Node* right;
}Node;

2.2. Design and analysis of main operation algorithms

2.2.1. Read file build tree function

Node* createTreeByFile();

Node* createTreeByOrder(char string[]);

Node* createNode();

return type: Node*;

Whether there are parameters: no

step:

  1. fopen opens the file to read the string, this string is in this format: "-+a##*b##-c##d##/e##f##"
    • (Teacher please explain clearly, I don’t even know if you want me to traverse the sequence structure in hierarchical order, or construct it in pre-inorder or post-inorder sequence, or in this way...)
  2. Define a global variable j to record the subscript to which the string is read
  3. After entering the function, judge whether the j subscript is "#", if yes, return NULL, if not, call createNode to construct a node root
  4. Call it recursively once and assign it to the left child of root
  5. Call it recursively again and assign it to the right child of root
  6. back to root

Algorithm time complexity:

  • The time complexity is O(N);
  • The space complexity is O(log 2 N);

2.2.2 Return the pointer function of the node corresponding to the x value

Node* findNode(Node* root, ElemType value);

return type: Node*;

Whether there are parameters: the root node of the binary tree, the key value x

step:

  • root is NULL and returns directly
  • The corresponding value of root is x, return root
  • Call the left child recursively, if the returned value is not empty, return the left child pointer
  • Call the right child recursively, if the returned value is not empty, return the right child pointer
  • Otherwise return empty, not found

Algorithm time complexity:

  • The time complexity is O(N);
  • The space complexity is O(log 2 N);

2.2.3. Concave printing binary tree function

void incurvatePrint(char preorder[], char DRLorder[]);

buildTree(preorder, DRLorder);

Node* buildByIndex(char preorder[], char inorder[], int start, int end);

void print(Node* root, int n);

int search(char inorder[], int start, int end, char key);

return type: no return value;

Whether there are parameters: Yes, pass in the preorder sequence and DRL sequence

step:

  1. Call the buildTree function in the incurvatePrint function to obtain the binary tree, and pass in two sequences
  2. Call the buildByIndex function to get a binary tree, pass in two sequences, and 0 and strlen(inorder) - 1 (from, to)
  3. The first value of the preorder traversal sequence is the root node. Find the subscript of the root node in the DRL. The left is the right subtree, and the right is the left subtree. Recursively call the buildByIndex function respectively.
  4. from > end returns NULL
  5. Finally incurvatePrint calls the print function, passing in root and 0(n) to print the binary tree with the concave method
  6. Every time a layer of recursion is entered, the second parameter passed in will be incremented by 1
  7. Before printing the corresponding value of root, recursive function is required, and the right child and n+1 are passed in. After that, n indents are printed, the carriage return is printed after printing the root value, and the recursive function is called, and the left child and n+1 are passed in.

Algorithm time complexity:

  • The time complexity is O(N 2 );
  • The space complexity is O(log 2 N);

2.2.4. Non-recursive preorder printing tree function

Stack basic functions:

typedef struct Stack {
    
    
	int size;
	int capacity;
	Node** arr;
}Stack;
Stack newStack() {
    
    
	Node** arr = calloc(N, sizeof(Node*));
	Stack stack = {
    
     0, N, arr };
	return stack;
}
void push(Stack* ps, Node* value) {
    
    
	if (ps->size == ps->capacity) {
    
    
		ps->arr = (Node**)realloc(ps->arr, 2 * ps->size * sizeof(Node*));
		ps->capacity *= 2;
	}
	ps->arr[ps->size] = value;
	(ps->size)++;
}
Node* pop(Stack* ps) {
    
    
	if (ps->size == 0) {
    
    
		printf("666,空了\n");
		return NULL;
	}
	return ps->arr[--(ps->size)];
}
int isEmptyStack(Stack* ps) {
    
    
	return ps->size == 0;
}

void preorderNormal(Node* root);

return type: no return value;

Whether there are parameters: Yes, pass in the root node of the binary tree

step:

  1. Build a stack, print the root node root, (return if it is NULL)
  2. Define cur as the left child of root
  3. Enter the loop, condition: the stack is not empty or cur is not empty
  4. Enter the loop: if the left child is not empty, push the stack and print the corresponding value of the node, cur=cur->left
  5. The loop stops cur assignment as the right child of the node popped from the stack
  6. Enter loop judgment
  7. End the loop, then the printing ends

Algorithm time complexity:

  • The time complexity is O(N);
  • The space complexity is O(log 2 N);

2.2.5. Inorder traversal and postorder traversal functions

void inorder(Node* root);

void postorder(Node* root);

No return value, with parameters, binary tree root node

step:

  1. Root node is NULL does not print
  2. Call the recursive function in turn, passing in the left child and the right child
  3. In-order traversal is between two calls, post-order traversal is after two calls

Complexity analysis:

Time complexity: O(N)

Space complexity: O(log N )

2.2.6. Level order traversal function

Common queue functions:

typedef struct Queue {
    
    
	DataType* arr;
	int size;
	int capacity;
}Queue;
Queue createQueue() {
    
    
	DataType* arr = (DataType*)calloc(N, sizeof(DataType));
	Queue queue = {
    
     arr, 0, N };
	return queue;
}

void offer(Queue* pq, DataType value) {
    
    
	if (pq->size == pq->capacity) {
    
    
		pq->arr = (DataType*)realloc(pq->arr, 2 * pq->size * sizeof(DataType));
		pq->capacity *= 2;
	}
	pq->arr[pq->size] = value;
	(pq->size)++;
}

DataType poll(Queue* pq) {
    
    
	if (isEmptyQueue(pq)) {
    
    
		printf("队列空\n");
		exit(0);
	}
	DataType ret = pq->arr[0];
	(pq->size)--;
	memmove(pq->arr, pq->arr + 1, pq->size * sizeof(DataType));
	return ret;
}

DataType peek(Queue* pq) {
    
    
	if (isEmptyQueue(pq)) {
    
    
		printf("队列空\n");
		exit(0);
	}
	return pq->arr[0];
}

int isEmptyQueue(Queue* pq) {
    
    
	return pq->size == 0;
}

void levelOrder(Node* root);

No return value, the incoming parameter is the root node of the binary tree

step:

  1. If the root node is NULL, then return
  2. Create a queue queue, and the root node enters the queue
  3. Enter the loop, condition: the queue is not empty
  4. Node* tmp accepts dequeued elements
  5. Print the value of tmp, put the left child of tmp into the queue, and put the right child into the queue (if it is NULL, it is not used)
  6. until out of loop

Complexity analysis:

Time complexity: O(N)

Space complexity: O(log 2 N)

2.2.7. Depth of the output tree

int getHeight(Node* root);

Returns the int depth, passed in the root node of the binary tree

step:

  1. root returns NULL
  2. Call the recursive function twice, passing in the left child and the right child in turn
  3. Returns the maximum of the return values ​​of two recursive functions plus 1

Complexity analysis:

Time complexity: O(N)

Space complexity: O(log 2 N)

2.2.8. Leaf nodes or non-leaf nodes of the output tree T

void printLeafNode(Node* root);

void printNotLeaf(Node* root);

No return value, pass in the root node of the binary tree

step:

  1. root is NULL, return
  2. root is not NULL,
    1. The printLeafNode function prints if the left and right children of the root are NULL
    2. In the printNotLeaf function, if the left and right children of root are not all NULL, print
  3. Call the recursive function twice, passing in the left child and the right child in turn

Complexity analysis:

Time complexity: O(N)

Space complexity: O(log 2 N)

2.2.9. Determine whether the binary tree is a complete binary tree

int isCompleteTree(Node* root)

Return an int yes or no, pass in the root node of the binary tree

step:

  1. If root is NULL, return 1
  2. Define the queue queue, the root node enters the queue
  3. Enter the loop, the loop condition is that the queue is not an empty queue
  4. Node* cur accepts dequeued elements
  5. If cur is not NULL, the left and right children are enqueued
  6. Otherwise, keep going out of the queue, if there is a non-NULL value in the queue, return 0, otherwise return 1
  7. If the loop ends, return 1

Complexity analysis:

Time complexity: O(N)

Space complexity: O(log 2 N)

2.2.10. Layer order traversal sequence to construct binary tree (using similar operations in 2.2.9)

Node* createTreeByLevelOrder(char string[]);

Returns the root node of the binary tree, passing in a character array

step:

  1. As long as the array is not empty, enqueue the first element of the array first, and use this value to create the root of the binary tree.
  2. Then enter the loop, the loop condition is that the queue is not empty, the element at the head of the queue is taken out, and the head of the queue is dequeued.
  3. As long as there are still elements in the array, first create a left child for the opposite element just taken out, and then the left child enters the queue.
  4. Same as above, create the right child and join the team.
  5. End a loop. back to 2

Complexity analysis:

Time complexity: O(N)

Space complexity: O(log 2 N)

2.2.11. main function


int main() {
    
    
	printf("===================================\n");
	Node* root = createTreeByFile();

	printf("===================================\n");
	Node* XTree = findNode(root, '*');

	printf("===================================\n");
	printf("凹入法打印:\n");
	incurvatePrint("-+a*b-cd/ef", "f/e-d-c*b+a");

	printf("===================================");
	printf("\n非递归前序:");
	preorderNormal(root);
	printf("\n递归前序:");
	preorder(root);
	printf("\nXTree前序:");
	preorder(XTree);

	printf("\n===================================");
	printf("\n递归中序:");
	inorder(root);
	printf("\n递归后序:");
	postorder(root);

	printf("\n===================================");
	printf("\n层序:");
	levelOrder(root);

	printf("\n===================================");
	printf("\n深度:%d", getHeight(root));

	printf("\n===================================");
	printf("\n叶子节点:");
	printLeafNode(root);
	printf("\n非叶子节点:");
	printNotLeaf(root);

	printf("\n===================================\n");
	printf("root%s完全二叉树", isCompleteTree(root) ? "是" : "不是");

	printf("\n===================================\n");
	levelOrder(createTreeByLevelOrder("123456789"));
}

2.3. Program running process and results

insert image description here

5. Summary

  • Encountered many problems in this process, such as null pointer exception, the result does not match the expected result
  • But as long as you debug well, it will always solve the problem
  • The focus of recursion is to transform into sub-problems, holistic thinking
  • Non-recursive implementation requires a combination of stack or queue!

6. Appendix: Source Code

Code address: code cloud connection

Guess you like

Origin blog.csdn.net/Carefree_State/article/details/130758288