2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
A tree is aNonlinearThe data structure is a hierarchical set of n (n>=0) finite nodes.It is called a tree because it looks like an upside-down tree, meaning its roots are facing upwards and its leaves are facing downwards.
- Root node: The root node has no predecessor node.
- Except for the root node, the remaining nodes are divided into a subtree with a structure 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 isRecursive definitionof.
The tree structure is more complicated than the linear table, and it is more troublesome to store and represent.It is necessary to save both the value range and the relationship between nodes.In practice, there are many ways to represent trees, such as:Parent representation, child representation, child-parent representation, and child-sibling representationwait.
In the process of introducing the following storage structure, we use the following tree as an example.
We assume that the nodes of the tree are stored in a set of continuous spaces, andIn each node, an indicator is attached to indicate the position of its parent node in the linked list.That is to say, in addition to knowing who it is, each node also knows where its parents are.
Data is the data field, which stores the data information of the node, and parent is the pointer field, which stores the index of the parent of the node in the array.
Below is the node structure definition code for our parent representation.
/*树的双亲表示法结点结构定义*/
#define MAX_TREE_SIZE 100
typedef int TElemType; //树结点的数据类型,目前暂定为整型
/*结点结构*/
typedef struct TreeNode
{
TElemType data; //结点数据
int parent; //双亲位置
}TreeNode;
/*树结构*/
typedef struct
{
TreeNode nodes[MAX_TREE_SIZE]; //结点数组
int r, n; //根的位置和结点数
}PTree;
With this storage structure, we can easily find the parent node of a node based on its parent pointer.The time complexity is 0(1), until parent is -1, it means the root of the tree node is found. But if we want to know what the children of the node are, sorry, please traverse the entire structure.
Just now we studied the storage structure of the tree from the perspective of the parent and from the perspective of the child. What if we look at it from the perspective of the brothers of the tree nodes? Of course, for a hierarchical structure like a tree, it is not enough to only study the brothers of the nodes. After observation, we found that for any tree, the first child of its node is unique if it exists, and its right brother is also unique if it exists. Therefore, we set two pointers, pointing to the first child of the node and the right sibling of this node.
/*树的孩子兄弟表示法结构定义*/
typedef struct TreeNode
{
TElemtype data;
struct TreeNode *firstchild, *rightsib;
} TreeNode,* pTreeNode;
A binary tree is a finite set of nodes that:
- Or empty
- It consists of a root node plus two binary trees, also called left subtree and right subtree.
As can be seen from the above figure:
- There is no node with degree greater than 2 in a binary tree.
- The subtrees of a binary tree are divided into left and right, and the order cannot be reversed, so a binary tree isOrdered Tree
Note: Any binary tree is composed of the following situations:
Binary trees can generally be stored using two structures:A sequential structure and a chain structure.
Ordinary binary trees are not suitable for storage in arrays because there may be a lot of space waste.Complete binary trees are more suitable for sequential structure storageIn reality, we usually useA heap (a binary tree) uses an array of sequential structures to storeIt should be noted that the heap here is different from the heap in the operating system's virtual process address space. One is a data structure, and the other is a segmented area for managing memory in the operating system.
For specific implementation and application, please see:[Data Structure] 08. Heap and its application
The easiest way to learn about binary tree structures is to traverse them.Binary tree traversal is to perform corresponding operations on the nodes in the binary tree in sequence according to certain specific rules, and each node is operated only onceThe operation performed when visiting a node depends on the specific application problem. Traversal is one of the most important operations on a binary tree and is also the basis for other operations on a binary tree.
//根 左子树 右子树
void PrevOrder(pTreeNode root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->val);
PrevOrder(root->left);
PrevOrder(root->right);
}
The following figure shows the recursive process:
//左子树 根 右子树
void InOrder(pTreeNode root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->val);
InOrder(root->right);
}
The following is the recursive process:
//左子树 右子树 根
void PostOrder(pTreeNode root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->val);
}
The following is the recursive process:
Level-order traversal: In addition to pre-order traversal, in-order traversal, and post-order traversal, you can also perform level-order traversal on a binary tree. Assume that the root node of the binary tree is located at level 1. Level-order traversal starts from the root node of the binary tree, first visits the root node of the first level, then visits the nodes on the second level from left to right, then the nodes on the third level, 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 called level-order traversal.。
Diagram:
We introduce a queue here to perform a pre-order traversal of the binary tree.
// 层序遍历
void LevelOrder(pTreeNode root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
pTreeNode front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
printf("NULL ");
}
else
{
printf("%d ", front->val);
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
QueueDestory(&q);
}
To create and destroy a binary tree, we useBinary Tree TraversalTake this as an example.
//二叉树的创建
struct TreeNode* Creat(char* arr,int n,int* i)
{
if(*i<n&&arr[*i]=='#')
{
(*i)++;
return NULL;
}
TreeNode* newnode=(TreeNode*)malloc(sizeof(TreeNode));
newnode->left=NULL;
newnode->right=NULL;
newnode->val=arr[(*i)++];
newnode->left=Creat(arr,n,i);
newnode->right=Creat(arr,n,i);
return newnode;
}
//二叉树的销毁
void TreeDestroy(struct TreeNode* root)
{
if(root==NULL)
{
return;
}
TreeDestroy(root->left);
TreeDestroy(root->right);
free(root);
}
The following operations are all carried out along the idea of traversal.
// 二叉树节点个数
int TreeSize(pTreeNode root)
{
if (root == NULL)
{
return 0;
}
return TreeSize(root->left) + TreeSize(root->right) + 1;
}
// 二叉树叶子节点个数
int TreeLeafSize(pTreeNode root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
// 二叉树第k层节点个数
int TreeLevelKSize(pTreeNode root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
// 二叉树查找值为x的节点
pTreeNode TreeFind(pTreeNode root, TreeDataType x)
{
if (root == NULL)
{
return NULL;
}
//相等就返回
if (root->val == x)
return root;
//找左子树
pTreeNode left=TreeFind(root->left, x);
if (left)
{
return left;
}
//找右子树
pTreeNode right = TreeFind(root->right, x);
if (right)
{
return right;
}
//都没找到
return NULL;
}
//二叉树的高度
int TreeHeight(pTreeNode root)
{
if (root == NULL)
{
return 0;
}
int max_left = TreeHeight(root->left) ;
int max_right = TreeHeight(root->right);
return max_left > max_right ? max_left + 1 : max_right + 1;
}
//判断是否是完全二叉树
bool TreeComplete(pTreeNode root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
pTreeNode front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
break;
}
else
{
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
while (!QueueEmpty(&q))
{
pTreeNode front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestory(&q);
return false;
}
}
QueueDestory(&q);
return true;
}