Implementation and traversal of binary tree and binary tree sorting tree

  Before learning the binary tree, you must first learn the concept and representation method of [pre-knowledge] tree

Table of contents

1. Binary tree

2. Binary tree traversal

3. Binary sort tree


 foreword

For large amounts of data, the linear access time of linked lists is too slow to be used. This chapter will introduce a simple data structure: the tree, whose running time for most operations is O(logN) on average. Trees are very useful abstract concepts in data structures. In this article we will discuss the storage structure of binary trees, the traversal of binary trees and the implementation of binary sorting trees, laying the foundation for subsequent balanced trees and high-order search trees.

1. Binary tree

A binary tree is a tree in which each node can have at most two children (degree 2). When a tree has only left or right children, the worst case is called an oblique tree ;

When the degree of a tree is only 0 or 2, we call this kind of tree a full binary tree ; when the nodes of a tree are arranged strictly in order from top to bottom and from left to right, we call this kind of tree It is called a complete binary tree . According to the definition, a full binary tree is a special case of a complete binary tree; if we fill all empty nodes with NULL or other impossible keywords, then this tree is called an extended binary tree.

At the same time, there are some properties of binary trees that are often tested in exams. Here is a summary:

  1. There are at most  2^(i-1)  nodes on the binary tree at level  i  ;
  2. A binary tree with a depth of k has 2^k-1 nodes; 
  3. Suppose the nodes with degrees 0, 1, and 2 are n0, n1, and n2 respectively . Since the number of nodes in a tree is T , T = n0 + n2 + n2 , and the number of branches (connections between nodes) is T - 1 , so T - 1 = 2 * n2 + n1 , from these two relations: n0=n2+1;
  4. A complete binary tree with n nodes has a depth of (log2n)+1 rounded down;
  5. From property 4, it can be obtained that the nodes of a complete binary tree with n nodes are numbered hierarchically (from top to bottom, from left to right). If i = 1 is the root node, then for any node its parent node number is i / 2 (i > 1) is rounded down. If 2 * i > n , then node i must have no left child (and no right child), and 2 * i + 1 > n  has no right child;

For the implementation of the binary tree, we can also consider using a sequential structure or a chain structure. Using the sequential structure, because the search can be used quickly in the game, we can calculate the subscript relationship between the parent node and its children according to the above property 5;

And because a binary tree has at most two children, we can directly point to them with pointers when using a chain structure.

/*二叉树节点声明*/
typedef struct TreeNode
{
	int data;
	struct TreeNode* left, *right;
}Node;

As for the realization of the binary tree is very simple, wherever you have time, you can insert it;

Node* root = NULL;

void init(int key)
{
	root = (Node*)malloc(sizeof(Node));
	root->data = key;
	root->left = NULL;
	root->right = NULL;
}

Node* findNode(Node* node ,int parent)
{
	static Node* temp = NULL;
	if (node->data == parent)
	{
		temp = node;
	}
	if(node->left)
	{
		findNode(node->left, parent);
	}
	if (node->right)
	{
		findNode(node->right, parent);
	}
	return temp;
}

void createNode(int key, int parent)
{
	Node* temp = findNode(root, parent);
	if (temp == NULL)
	{
		printf("NOT FIND\n");
		return;
	}
	else
	{
		if (temp->left == NULL)
		{
			Node* newnode = (Node*)malloc(sizeof(Node));
			newnode->data = key;
			newnode->left = NULL;
			newnode->right = NULL;
			temp->left = newnode;
		}
		else if (temp->right == NULL)
		{
			Node* newnode = (Node*)malloc(sizeof(Node));
			newnode->data = key;
			newnode->left = NULL;
			newnode->right = NULL;
			temp->right = newnode;
		}
		else
		{
			//左右子树都存在
			printf("FULL\n");
			return;
		}
	}
}

2. Binary tree traversal

Next we focus on the traversal of the binary tree.

The traversal of the binary tree is divided into: pre-order traversal, in-order traversal, post-order traversal and layer-order traversal according to different usage scenarios;

  • Preorder traversal: first visit the root node, then visit the left subtree, and then visit the right subtree;

In the binary tree shown above, the order of preorder traversal is: 1 -> 2 -> 4 -> 5 -> 3 -> 6;

The specific steps are shown in the figure below, the access sequence (from first to last) green  ,  red  ,  blue   

  • In-order traversal: first visit the left subtree, then visit the root node, and then visit the right subtree;

In the binary tree shown above, the order of inorder traversal is: 4 -> 2 -> 5 -> 1 -> 6 -> 3

 

  • Post-order traversal: first visit the left subtree, then visit the right subtree, and then visit the root node;

In the binary tree shown above, the order of post-order traversal is: 4 -> 5 -> 2 -> 6 -> 3 -> 1

Through the above case, we can get an important property: the first one of the pre-order traversal must be the root, and the last one of the post-order traversal must be the root , then the tree can be deduced through the pre-order and in-order or post-order and in-order abstract model;

It is easiest to use recursion to implement pre-middle-post order traversal with code:

//前序
void prev_order(Node* node)
{
	if (node == NULL)
	{
		return;
	}
	printf("%d", node->data);
	prev_order(node->left);
	prev_order(node->right);
}

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

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

As for layer order traversal, it can intuitively output each layer of the tree. Of course, if you want to do this, simple recursion will definitely not work. Here we use queues to achieve:

 The first step: we first put the root node into the queue; the second step: judge whether the queue is empty, if not, remove the head of the queue and enqueue its children, and so on, until the queue is empty and jump out;

 For the code, I will be a little lazy here, using the queue implemented by STL in C++, students who don’t know how to write the queue normally, the idea is the same;

//层序
void level_order(Node* node)
{
    //创建队列
	std::queue<int> q;
	if (node != NULL)
	{
		q.push(node->data);
	}
	while(!q.empty())
	{
		int temp = q.front();
		printf("%d ", temp);
		Node* tempNode = findNode(root, temp);
		q.pop();
		if (tempNode->left)
		{
			q.push(tempNode->left->data);
		}
		if (tempNode->right)
		{
			q.push(tempNode->right->data);
		}
	}
}

3. Binary sort tree

A binary sort tree is also called a binary search tree: if a binary tree's left subtree is not empty, then all the values ​​of the left subtree are less than the root node; if the right subtree is not empty, then all the values ​​of the right subtree are greater than the root node, this is a binary sort tree (binary search tree).

As shown above is a binary sort tree. The binary sorting tree was created to make our search more convenient (binary search). Due to the particularity of the binary sorting tree, the value on the left is always smaller than the root node, and the value on the right is always greater than the root node. You can It is found that when we traverse the tree in order, its result is an ordered sequence: 1 3 4 6 7 8 10 13 14;

The structure of its nodes is the same as that of a general binary tree;

typedef struct TreeNode
{
	int data;
	struct TreeNode* left, * right;
}Node;

It is also very simple to build a binary sorting tree, just insert it according to its nature;

//默认没有重复的key的情况
void insert(Node** root, int key)
{
	Node* prev = NULL;//用来指向正确的插入位置的父节点
	Node* temp = *root;
	while (temp != NULL)
	{
		//追随root的上一个位置
		prev = temp;
		//如果插入的值小于该节点 往左边找
		if (key < temp->data)
		{
			temp = temp->left;
		}
		//如果插入的值大于该节点 往右边找
		else if (key > temp->data)
		{
			temp = temp->right;
		}
	}
	//找到了要插入的位置
	Node* newnode = (Node*)malloc(sizeof(Node));
	if (newnode == NULL)
	{
		printf("ERR\n");
		return;
	}
	else
	{
		newnode->data = key;
		newnode->left = NULL;
		newnode->right = NULL;
		if (*root == NULL)//如果根节点没有初始化
		{
			*root = newnode;
		}
		else if (key < prev->data)
		{
			prev->left = newnode;
		}
		else if (key > prev->data)
		{
			prev->right = newnode;
		}
	}
}

The focus of the binary sort tree is on the delete operation. For the deletion of binary sorting trees, we generally divide them into three situations:

  • The first case: the leaf node is deleted, and it is enough to delete it directly, because deleting it will not affect the whole tree;
  • The second case: the deleted node has a child (regardless of left and right), at this time it is necessary to move its child to the position of the deleted node;
  • The third case: the deleted node has two children. At this time, it is necessary to find the direct predecessor or direct successor of the node to replace the deleted node;

The first two cases are easy to understand, let's talk about the third case in detail. Taking the ordered sequence 1 3 4 6 7 8 10 13 14 above as an example, if we want to delete the node with a value of 8, in order not to affect the entire ordered sequence, the deleted result should be 1 3 4 6 7 10 13 14, that is, we need to find the node whose value is 7 or 10 to replace the 8 node, and then delete the original 7 or 10 according to the first and second cases. This 7 node is the direct predecessor of the deleted node, and similarly, node 10 is the direct successor of the deleted node;

It is also very simple to find the direct predecessor or direct successor: from the figure below, we can see that the direct predecessor node must be the maximum value in the left subtree of the deleted node, and the direct successor node must be the maximum value in the right subtree of the deleted node. minimum value ;

Taking the direct predecessor node 7 as an example, if the left subtree of the node to be deleted exists, we only need to visit the left subtree first, and then visit its right subtree all the time.

/*真正的删除操作*/
void del(Node* node, Node* prev)
{
	Node* temp = NULL;
	//只有左子树或者只有右子树的情况 把要删除的节点删除 并把孩子替换上去
	if (node->left == NULL && node->right != NULL)
	{
		temp = node;
		temp = temp->right;
		node->data = temp->data;
		node->left = temp->left;
		node->right = temp->right;
		free(temp);
	}
	else if (node->right == NULL && node->left != NULL)
	{
		temp = node;
		temp = temp->left;
		node->data = temp->data;
		node->left = temp->left;
		node->right = temp->right;
		free(temp);
	}
	//叶子节点的情况
	else if (node->right == NULL && node->left == NULL)
	{
		//左叶子的情况
		if (node->data < prev->data)
		{
			prev->left = NULL;
		}
		else
		{
			prev->right = NULL;
		}
		free(node);
	}
	//左右子树都不为空的情况
	else
	{
		temp = node;
		Node* s = node;//指向左子树的最大值的指针
		//找左子树的最大值
		s = s->left;
		while (s->right != NULL)
		{
			temp = s;//这里的temp指向s的父节点
			s = s->right;
		}
		//替换数据
		node->data = s->data;
		//还要删除s节点 又分两种情况
		//但是不管哪种情况此时的s要么没有孩子要么只有左孩子
		if (temp!= node)
		{
			//说明s往下走了很多步 这个时候s一定在temp的右边
			temp->right = s->left;
		}
		else
		{
			//这个时候s一定在temp的左边
			temp->left = s->left;
		}
	}
}

/*封装删除操作*/
void deleteNode(Node* root, int key)
{
	if(root == NULL)
	{
		return;
	}
	else
	{
		static Node* prev = NULL;//prev是删除节点的父节点
		//递归地去寻找要删除的点
		if (key < root->data)
		{
			prev = root;
			deleteNode(root->left, key);
		}
		else if (key > root->data)
		{
			prev = root;
			deleteNode(root->right, key);
		}
		else
		{
			//删除
			del(root, prev);
		}
	}
}

Inorder traversal of test results 

Afterword

Normally, the complexity of a binary sorting tree depends on the height h of the tree , but what if the value I insert is 12345...? Does it become what we call the worst case with a complexity of O( N) - a slanted tree, in order to ensure search efficiency, the first self-balancing tree in human history - a binary balanced tree was born ! We will explain in detail the addition, deletion, checking and modification of the binary balanced tree next time.

Guess you like

Origin blog.csdn.net/ZER00000001/article/details/125948512