[Data structure] Implementation of binary tree chain structure and its common operations

Table of contents

1. Hand rubbing binary tree

2. Binary tree traversal

2.1 Preorder, inorder and postorder traversal

2.2 Level order traversal of binary tree

3. Common operations on binary trees

3.1 Find the number of binary tree nodes

3.2 Find the number of leaf nodes of the binary tree

3.3 Find the number of nodes in the kth layer of the binary tree

3.3 Find the depth of the binary tree

3.4 Find the node whose value is x in the binary tree

4. Destruction of the binary tree


1. Hand rubbing binary tree

Before learning the basic operations of a binary tree, you need to create a binary tree before you can learn its related basic operations. Since we don't have a deep grasp of the binary tree structure, in order to reduce the learning cost, here is a quick manual to create a simple binary tree, and quickly enter the binary tree operation learning. When the binary tree structure is almost understood, we will turn around and study the real creation of the binary tree. Way.

 Define the nodes of a binary tree:

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

Handwritten binary tree according to the binary tree structure above:

BTNode* BuyNode(BTDataType data)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	assert(node);
	node->data = data;
	node->left = node->right = NULL;
	return node;
}

BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	return node1;
}

In this way we have written a simple binary tree.

2. Binary tree traversal

2.1 Preorder, inorder and postorder traversal

The easiest way to learn the binary tree structure is to traverse it. The so-called binary tree traversal (Traversal) is to perform corresponding operations on the nodes in the binary tree in turn according to certain specific rules, and each node is only operated once. The operations performed by the access nodes depend on the specific application problem. Traversal is one of the most important operations on a binary tree, and it is also the basis for other operations on a binary tree.

 According to the rules, the traversal of the binary tree includes: preorder/inorder/postorder recursive structure traversal:

  1. Preorder traversal (Preorder Traversal, also known as preorder traversal) - the operation of visiting the root node occurs before traversing its left and right subtrees.
  2. Inorder Traversal (Inorder Traversal) - the operation of visiting the root node occurs while traversing its left and right subtrees (in the middle).
  3. Postorder Traversal—The operation of visiting the root node occurs after traversing its left and right subtrees.

Since the visited node must be the root of a certain subtree, N (Node), L (Left subtree) and R (Right subtree) can be interpreted as the root, the left subtree of the root, and the right subtree of the root . NLR, LNR, and LRN are also called first-root traversal, middle-root traversal, and back-root traversal, respectively.

For binary tree traversal, the code is very simple to write, but for beginners, it is a bit difficult to understand. Here are three traversal codes, you can take a look first:

preorder traversal

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

Inorder traversal

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

post order traversal

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

After reading the code, do you feel that these three traversals are very similar? Let's run the code of the three traversals on the compiler:

int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	PostOrder(root);
	return 0;
}

operation result:

 Recursive expansion graph of preorder traversal:

 Inorder traversal and subsequent traversal are similar to this picture.

2.2 Level order traversal of binary tree

The level-order traversal of a binary tree is a breadth-first search (BFS) method. It traverses the binary tree layer by layer in hierarchical order, that is, starting from the root node, first traverses the first layer nodes, then traverses the second layer nodes, and so on, until all layers are traversed.

A common way to implement level-order traversal is to use a queue. The specific ideas are as follows:

  1. Create an empty queue and enqueue the root node.
  2. Loop through the following steps until the queue is empty:
  • Dequeue a node, storing its value into the result list.
  • If the node has a left child, enqueue the left child.
  • If the node has a right child, enqueue the right child.

In this way, when the queue is empty, the traversal process is completed, and the result of the hierarchical traversal is stored in the result list.

 The following code uses the queue in C++STL to avoid the trouble of handwriting the queue:

void LevelOrder(BTNode* root)
{
	assert(root);
	queue<BTNode*> a;
	a.push(root);
	while (!a.empty())
	{
		BTNode* front = a.front();
		a.pop();
		printf("%d ", front->data);
		if (front->left)
		{
			a.push(front->left);
		}
		if (front->right)
		{
			a.push(front->right);
		}
	}
}

int main()
{
	BTNode* root = CreatBinaryTree();
	LevelOrder(root);
	return 0;
}

Output result:

3. Common operations on binary trees

3.1 Find the number of binary tree nodes

method one:

Define a global variable count, and then traverse each node, each time a node is traversed, the count will increase by 1

code:

int count = 0;
void TreeSize1(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	count++;
	TreeSize1(root->left);
	TreeSize1(root->right);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	count = 0;
	TreeSize1(root);
	printf("%d\n", count);
	return 0;
}

Method Two:

int TreeSize2(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return TreeSize2(root->left) + TreeSize2(root->right) + 1;
}
int main()
{
	BTNode* root = CreatBinaryTree();
	printf("TreeSize2: %d\n", TreeSize2(root));
	return 0;
}

First check whether the root node is empty, if it is empty, it means that this is an empty tree, and return 0 directly. If the root node is not empty, call itself recursively to count the number of nodes in the left and right subtrees, then add the number of nodes in the left subtree, the number of nodes in the right subtree, and the number of the root node itself (1 node) , and finally returns the result.

In this recursive way, the function can calculate the total number of all nodes in the binary tree.

3.2 Find the number of leaf nodes of the binary tree

Specific ideas:

First check whether the root node is empty, if it is empty, it means that this is an empty tree, and return 0 directly. Next, determine whether the current node is a leaf node by judging whether the left subtree and the right subtree of the root node are both empty . If it is a leaf node, return 1. If it is not a leaf node, recursively call itself to calculate the number of leaf nodes in the left subtree and right subtree, and add them as the result and return.

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);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	printf("TreeLeafSize: %d\n", TreeLeafSize(root));
	return 0;
}

3.3 Find the number of nodes in the kth layer of the binary tree

Ideas:

First check whether the root node is empty, if it is empty, it means that this is an empty tree, and return 0 directly. If k is equal to 1, it means that the current layer is the target layer and returns 1. If k is greater than 1, call itself recursively to count the number of nodes at level k-1 in the left and right subtrees, and add them together and return as the result.

In this recursive way, the function can calculate the total number of kth level nodes in the binary tree.

int TreeKLevel(BTNode* root, int k)
{
	assert(k >= 1);
	if (root == NULL) return 0;
	if (k == 1) return 1;
	return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	printf("TreeKLevel: %d\n", TreeKLevel(root, 2));//第2层节点数量
	printf("TreeKLevel: %d\n", TreeKLevel(root, 3));//第3层节点数量
	printf("TreeKLevel: %d\n", TreeKLevel(root, 4));//第4层节点数量
	return 0;
}

The recursive expansion graph of the function:

3.3 Find the depth of the binary tree

int TreeDepth(BTNode* root)
{
	if (root == NULL) return 0;
	int l = TreeDepth(root->left); //左子树的深度
	int r = TreeDepth(root->right); //右子树的深度
	return (l > r ? l : r) + 1; //返回左右子树深度的较大值加自身的深度1
}
int main()
{
	BTNode* root = CreatBinaryTree();
	printf("TreeDepth: %d\n", TreeDepth(root));
	return 0;
}

First check whether the root node is empty, if it is empty, it means that this is an empty tree, and return to depth 0 directly. Next, the function computes the depths of the left and right subtrees by calling itself recursively, storing the results in variables l and r, respectively. Then, by comparing the size of l and r, select the larger value, and add 1 to it (representing the depth of the current node) as the depth of the entire binary tree.

In this recursive way, the function can calculate the depth (height) of the binary tree.

3.4 Find the node whose value is x in the binary tree

When looking for a node with a value of x in a binary tree, try to use preorder traversal to improve efficiency.

BTNode* TreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL) return NULL;
	if (root->data == x) return root;
	BTNode* ret1 = TreeFind(root->left, x);
	if (ret1)
	{
		return ret1;
	}
	BTNode* ret2 = TreeFind(root->right, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL;
}

int main()
{
	BTNode* root = CreatBinaryTree();
	BTNode* ret = TreeFind(root, 3);
	if (ret)
	{
		printf("找到了:%d\n", ret->data);
	}
	else
	{
		printf("找不到\n");
	}
	return 0;
}

First check whether the root node is empty, if it is empty, it means that this is an empty tree, and return NULL directly. Next, the function checks whether the data of the current node is equal to the target value x. If it is equal, it means that the target node has been found, and returns a pointer to the current node. If it is not equal, recursively call the function to find the target value x in the left subtree and right subtree respectively. If the returned pointer is not empty, it means that the target node is found in the subtree, and return the pointer directly. If the target node is not found in the left and right subtrees, NULL is returned.

With this recursion, a function can look up a node of a particular value in a binary tree and return a pointer to that node. Returns NULL if the target value cannot be found.

4. Destruction of the binary tree

For the destruction of the binary tree, we cannot use preorder traversal, because if we use preorder traversal, the root node of the binary tree will be destroyed first, so that the left and right subtrees of the root node cannot be found. If we must use the first In order traversal, the left subtree and right subtree of the node must be saved first. But if you use post-order traversal, you can easily solve it.

Use post-order traversal to destroy a binary tree in the order of left child, right child, and root node.

void TreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	TreeDestory(root->left);
	TreeDestory(root->right);
	free(root);
}
int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	TreeDestory(root);
	root = NULL;
}

Guess you like

Origin blog.csdn.net/m0_73648729/article/details/132289236