[Data structure] Construction of binary tree (C language implementation)

1. Tree concept and structure

1.1 The concept of a tree 

A tree is a non-linear data structure, which is a set of hierarchical relationships composed of n (n>=0) finite nodes. It is called a tree because it looks like an upside-down tree, which means it has the roots pointing up and the leaves pointing down.

  • There is a special node, called the root node , the root node has no predecessor nodes
  • Except the root node, other nodes are divided into M (M>0) disjoint sets T1**, T2 , ... , **Tm, each set Ti (1<= i
    <= m) is A subtree whose structure is similar to a tree. The root node of each subtree has one and only one predecessor, and can have 0 or more successors
  • Therefore, the tree is defined recursively

Note: In the tree structure, there can be no intersection between subtrees, otherwise it is not a tree structure

1.2 Related concepts of trees

  

 

Degree of a node : The number of subtrees contained in a node is called the degree of the node; as shown in the figure above: A is 6

Leaf node or terminal node : a node with a degree of 0 is called a leaf node; as shown in the above figure: B, C, H, I... and other nodes are leaf nodes

Non-terminal nodes or branch nodes : nodes whose degree is not 0; as shown in the above figure: nodes such as D, E, F, G... are branch nodes

Parent node or parent node : If a node contains child nodes, this node is called the parent node of its child nodes; as shown above: A is the parent node of B

Child node or child node : the root node of the subtree contained in a node is called the child node of the node; as shown above: B is the child node of A

Brother nodes : nodes with the same parent node are called brother nodes; as shown above: B and C are brother nodes

Degree of the tree : In a tree, the degree of the largest node is called the degree of the tree; as shown above: the degree of the tree is 6

The level of nodes : starting from the definition of the root, the root is the first level, the child nodes of the root are the second level, and so on;

Tree height or depth : the maximum level of nodes in the tree; as shown above: the height of the tree is 4

Cousin nodes : Nodes whose parents are on the same layer are cousins; as shown in the figure above: H and I are sibling nodes

Ancestors of a node : all nodes on the branch from the root to the node; as shown in the figure above: A is the ancestor of all nodes

Descendants : Any node in the subtree rooted at a node is called a descendant of the node. As shown above: all nodes are descendants of A

Forest : A collection of m (m>0) disjoint trees is called a forest. There are
many concepts, and we still need to understand them one by one!

1.3 Tree Representation

The tree structure is more complicated than the linear table, and it is more troublesome to store and express. Since the value range is saved, the relationship between nodes and nodes is also saved. In practice, there are many ways to represent trees, such as: parent representation , child representation, child parent representation, and child sibling representation. Here we simply understand the most commonly used child brother notation .

1.4 The practical application of the tree (representing the directory tree structure of the file system, pictures found on the Internet)

 

2. Concept and structure of binary tree

2.1 Related concepts

A binary tree is a finite set of nodes:

1. or empty

2. It consists of a root node plus two binary trees called left subtree and right subtree

  • A binary tree does not have a node with degree greater than 2
  • The subtrees of a binary tree are divided into left and right, and the order cannot be reversed, so a binary tree is an ordered tree

 Note: For any binary tree, it is composed of the following situations:

 2.2 Special binary tree:

1. Full binary tree : a binary tree, if the number of nodes in each layer reaches the maximum value, then this binary tree is a full binary tree. That is to say, if the number of layers of a binary tree is K and the total number of nodes is , then it is a full binary tree.

2. Complete binary tree : A complete binary tree is a very efficient data structure, and a complete binary tree is derived from a full binary tree. For a binary tree with a depth of K and n nodes, it is called a complete binary tree if and only if each of its nodes has a one-to-one correspondence with the nodes numbered from 1 to n in the full binary tree with a depth of K. It should be noted that a full binary tree is a special kind of complete binary tree.

It is difficult to understand the concept of closing, the picture above

 2.3 Properties of Binary Trees

If the number of layers of the root node is specified as 1, then there are at most 2^(i-1) nodes on the i-th layer of a non-empty binary tree.

If the number of layers of the root node is specified as 1, the maximum number of nodes in a binary tree with a depth of h is 2^(h-1)

For any binary tree, if the degree is 0, the number of leaf nodes is n0, and the number of branch nodes with degree 2 is n2, then n0 = n2 + 1

If the number of layers of the root node is specified as 1, the depth of a full binary tree with n nodes, h=long(n+1). (ps: log is based on 2, and n+1 is logarithm)

For a complete binary tree with n nodes, if all nodes are numbered from 0 according to the array order from top to bottom and from left to right, then for the node with the sequence number i:

1. If i>0 , the parent number of the node at i position: (i-1)/2; i=0 , i is the root node number, no parent node

2. If 2i+1<n , the left child number: 2i+1, 2i+1>=n otherwise there is no left child

3. If 2i+2<n , the serial number of the right child: 2i+2, 2i+2>=n , otherwise there is no right child
 

After understanding the nature of the binary tree, we might as well get started with a few questions

1. There are 399 nodes in a binary tree, among which there are 199 nodes with degree 2, then the number of leaf nodes in the binary tree is ( ) A There is no
such binary tree
B 200
C 198
D 199

Answer: B. n0 = n2+1, that is, n0=199+1 = 200
2. Among the following data structures, the sequential storage structure is not suitable for ( )
A Incomplete binary tree
B Heap
C Queue
D Stack

Answer: A.

3. In a complete binary tree with 2n nodes, the number of leaf nodes is ( )
A n
B n+1
C n-1
D n/2

Answer: A. Summarize the number of points n = n0+n1+n2; and n2 = n0-1, both n=n0+n1+n0-1, in the complete binary tree, n1 has only 0 or 1, substitute into A

4. The number of nodes in a complete binary tree is 531, then the height of this tree is ( )
A 11
B 10
C 8
D 12

Answer: B

5. A complete binary tree with 767 nodes, the number of leaf nodes is ()
A 383
B 384
C 385
D 386

Answer: B. Similar to question 3, the summary point is n = n0+n1+n0-1; n1 is 0 or 1, and it is divisible to choose B

2.4 Storage structure of binary tree

Binary trees can generally be stored using two structures, a sequential structure and a chain structure.

1. Sequential storage

Sequential structure storage is to use arrays for storage. Generally, arrays are only suitable for representing complete binary trees, because not complete binary trees will waste space. In reality, only the heap will be stored in an array, and we will specifically explain the heap in the following chapters. Binary tree sequential storage is physically an array and logically a binary tree.

 2. Chain structure

The linked storage structure of the binary tree means that a linked list is used to represent a binary tree, that is, a link is used to indicate the logical relationship of elements. The usual method is that each node in the linked list is composed of three fields, the data field and the left and right pointer fields, and the left and right pointers are used to give the storage addresses of the link points where the left child and right child of the node are located. The chain structure is further divided into binary chains and triple chains. At present, we generally use binary chains in our studies. Later courses will learn high-level data structures such as red-black trees, which will use triple chains.

3. Sequence structure and implementation of binary tree

3.1 Sequential structure of binary tree

Ordinary binary trees are not suitable for storage in arrays, because there may be a lot of wasted space. The complete binary tree is more suitable for sequential structure storage. In reality, we usually store the heap (a binary tree) in an array of sequential structures. It should be noted that the heap here and the heap in the virtual process address space of the operating system are two different things. One is the data structure, and the other is the management in the operating system. A region of memory is segmented.

 4. Realization of binary tree chain structure

Before learning the basic operations of a binary tree, you need to create a binary tree before you can learn its related basic operations. In order to reduce everyone's learning costs, a simple binary tree is manually and quickly created here.

The code implementation of this tree:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;


BTNode* CreatTree()
{
	BTNode* n1 = (BTNode*)malloc(sizeof(BTNode));
	assert(n1);
	BTNode* n2 = (BTNode*)malloc(sizeof(BTNode));
	assert(n2);
	BTNode* n3 = (BTNode*)malloc(sizeof(BTNode));
	assert(n3);
	BTNode* n4 = (BTNode*)malloc(sizeof(BTNode));
	assert(n4);
	BTNode* n5 = (BTNode*)malloc(sizeof(BTNode));
	assert(n5);
	BTNode* n6 = (BTNode*)malloc(sizeof(BTNode));
	assert(n6);

	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;
	n5->data = 5;
	n6->data = 6;

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n2->right = NULL;
	n3->left = NULL;
	n3->right = NULL;
	n4->left = n5;
	n4->right = n6;
	n5->left = NULL;
	n5->right = NULL;
	n6->left = NULL;
	n6->right = NULL;

	return n1;
}

 After building the framework, we proceed to the next step

4.1 Layer order traversal

Pre-order, in-order, post-order traversal recursive operations:

//先序
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

//中序
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

//后序
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

We found that the level order traversal of the binary tree does not seem to be difficult, but if we understand it deeply, we will find that the recursive call process is quite complicated. Let's try to understand the recursive call expansion diagram.

 

In addition to these three traversal methods, binary trees also have layer order traversal :

Level-order traversal: In addition to pre-order traversal, in-order traversal, and post-order traversal, level-order traversal of binary trees can also be performed. Assuming that the root node of the binary tree is at 1 layer, the layer order traversal is to start from the root node of the binary tree where it is located, first visit the root node of the first layer, then visit the nodes on the second layer from left to right, and then the third The nodes of the layer, and so on, the process of visiting the nodes of the tree layer by layer from top to bottom and from left to right is the layer sequence

So how to implement layer order traversal?

We can borrow a queue, put the nodes into the queue, and not leave the queue for empty, bring the children into the queue, and then put the children into the queue, and so on, until all the nodes are out of the queue, that is, the queue is an empty sequence The traversal is over. To put it simply: it is to bring in the next layer of nodes when the previous layer of nodes comes out. This is implemented in C language: so we first need to write a queue by hand.

//二叉树
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//队列(用链表实现,data的类型是BTNode*)
typedef BTNode* QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}

	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	int size = 0;
	while (cur)
	{
		++size;
		cur = cur->next;
	}

	return size;
}

//层序遍历
void TreeLevelOrder(BTNode*root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ",front->data);

		//下一层
		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);

	}
	printf("\n");

	QueueDestroy(&q);
}

4.2 Other operations

number of nodes

// 节点的个数
int TreeSize(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left)  +
		TreeSize(root->right) + 1;
}

The number of leaf nodes


// 叶子节点个数
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL
		&& root->right == NULL)
		return 1;

	return TreeLeafSize(root->left)
		+ TreeLeafSize(root->right);
}

 height of binary tree

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int lh = TreeHeight(root->left);
	int rh = TreeHeight(root->right);

	return lh > rh ? lh + 1 : rh + 1;
}

The number of nodes in the kth layer

// 第K层节点个数
int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;

	// 转换成求子树第k-1层
	return TreeKLevel(root->left, k - 1)
		+ TreeKLevel(root->right, k - 1);
}

returns the node where x is located

// 返回x所在的节点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)
		return root;

	// 先去左树找
	BinaryTreeNode*lret = TreeFind(root->left, x);
	if (lret)
		return lret;

	// 左树没有找到,再到右树找
	BinaryTreeNode*rret = TreeFind(root->right, x);
	if (rret)
		return rret;

	return NULL;
}

Destruction of the binary tree

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

Test the above several functions through the main function


int main()
{
	BTNode* root = CreateTree();
	PreOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	printf("Tree size:%d\n", TreeSize(root));
	printf("Tree size:%d\n", TreeSize(root));
	printf("Tree size:%d\n", TreeSize(root));

	printf("Tree Leaf size:%d\n", TreeLeafSize(root));
	printf("Tree Height:%d\n", TreeHeight(root));
	printf("Tree K Level:%d\n", TreeKLevel(root, 3));
	printf("Tree Find:%p\n", TreeFind(root, 8));

	BTNode* ret = TreeFind(root, 7);
	ret->data *= 10;

	PreOrder(root);
	printf("\n");

	return 0;
}

4.3 Judgment of complete binary tree

How do we judge whether it is a complete binary tree? This can be used for the layer order traversal we mentioned above:

We go layer by layer. For a complete binary tree, we go layer by layer. After encountering an empty space, there will be no non-empty (because the complete binary tree is continuous from left to right), there are non-empty If it is empty, it is not a complete binary tree.

//判断是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	//遇到空以后,后面全是空——完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestory(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

Table of contents

1. Tree concept and structure

1.1 The concept of a tree 

1.2 Related concepts of trees

1.3 Tree Representation

1.4 The practical application of the tree (representing the directory tree structure of the file system, pictures found on the Internet)

2. Concept and structure of binary tree

2.1 Related concepts

 2.2 Special binary tree:

 2.3 Properties of Binary Trees

2.4 Storage structure of binary tree

3. Sequence structure and implementation of binary tree

3.1 Sequential structure of binary tree

 4. Realization of binary tree chain structure

4.1 Layer order traversal

4.2 Other operations

4.3 Judgment of complete binary tree


Guess you like

Origin blog.csdn.net/weixin_64214213/article/details/126530778