Detailed explanation of binary trees and heaps (easy to understand)


Preface

Tree structure refers to a data structure in which there is a "one-to-many" tree relationship between data elements. It is an important type of nonlinear data structure. This article focuses on explaining it.


1. What is a tree:

1. Basic concept of tree:

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

Let’s first look at a picture to try to understand the tree structure:
Insert image description here
We can see that each node has one or more nodes going down, so Like the branches of a tree, the tree-shaped structure resembles an upside-down tree.

  • There is a special node called the root node. The root node has no predecessor node.
  • Except for the root node, the remaining nodes are divided into M (M>0) disjoint sets T1, T2,..., Tm, where each set Ti (1<= i <= m) is another tree structure. A subtree 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.

2. Related concepts of trees:

Let’s learn about some basic noun concepts:
Insert image description here

3. Representation method of tree:

The tree structure is more complicated than the linear table, and it is more troublesome to store and represent it. It is necessary to save both the value range and the relationship between nodes. Relationship, in fact, there are many ways to represent trees, such as parent representation, child representation, child parent representation, and child brother representation, etc. Here we will briefly understand the most commonly used child brother representation.

typedef int DataType;
struct Node
{
    
    
 struct Node* _firstChild1; //第一个孩子结点
 struct Node* _pNextBrother; //指向其下一个兄弟结点
 DataType _data; //结点中的数据域
};

Insert image description here

2. Binary tree:

1. Basic concepts and structure:

A binary tree is a finite set of nodes, which is:

  1. It consists of a root node plus two binary trees, also known as the left subtree and the right subtree.
  2. or empty

Insert image description here
As can be seen from the picture above:

  1. There is no node with degree greater than 2 in a binary tree
  2. The subtrees of a binary tree can be divided into left and right subtrees, and the order cannot be reversed, so the binary tree is an ordered tree.

Note: Any binary tree is composed of the following situations:

Insert image description here

2. Special binary tree type:

  1. Full Binary Tree: A binary tree. If the number of nodes in each layer reaches the maximum, then the binary tree is a full binary tree. That is to say, if the number of levels 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. A complete binary tree is derived from a full binary tree. For a binary tree with depth K and n nodes, it is called a complete binary tree if and only if each node corresponds one-to-one with the nodes numbered from 1 to n in the full binary tree with depth K. It should be noted that a full binary tree is a special kind of complete binary tree.
    Insert image description here

3. Properties of binary trees:

  1. If the number of levels of the root node is specified to be 1, then there will be at most 2^(i-1) nodes on the i-th level of a non-empty binary tree.

  2. If the number of levels of the root node is specified to be 1, then the maximum number of nodes of a binary tree with depth h is 2^h - 1.

  3. 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

  4. If the number of levels of the root node is specified to be 1, the depth of a full binary tree with n nodes, h= log2(n+1). (ps: log2(n+1) is log with base 2, n+1 is logarithm)

  5. For a complete binary tree with n nodes, if all nodes are numbered starting from 0 in array order from top to bottom, left to right, then for the node with serial number i:

    1. If i>0, the parent number of the node at position i is: (i-1)/2; i=0, there is no parent node
    2. If 2i+1< n, left child number: 2i+1, 2i+1>=n otherwise there is no left child
    3. If 2i+2<n, right child number: 2i+2, 2i+2> =nOtherwise there is no right child

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 to store. Generally, arrays are only suitable for representing complete binary trees, because if they are not complete binary trees, there will be a waste of space. In reality, only heaps use arrays for storage. Binary tree sequential storage is physically an array and logically a binary tree.
    Insert image description here
  2. Linked storage: The linked storage structure of a binary tree means that a linked list is used to represent a binary tree, that is, a chain is used to indicate the logical relationship of elements. The usual method is that each node in the linked list consists of three fields, the data field and the left and right pointer fields. 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. Chain type The structure is divided into binary chains and trifurcated chains. Currently, we generally study binary chains.
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
    
    
 struct BinTreeNode* _pLeft; //指向当前节点左孩子
 struct BinTreeNode* _pRight; //指向当前节点右孩子
 BTDataType _data; //当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
    
    
 struct BinTreeNode* _pParent; //指向当前节点的双亲
 struct BinTreeNode* _pLeft; //指向当前节点左孩子
 struct BinTreeNode* _pRight; //指向当前节点右孩子
 BTDataType _data; //当前节点值域
}

5. Binary tree traversal:

The simplest way to learn a 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 sequence according to certain specific rules, and each node is only operated once. The operations performed by accessing nodes depend 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.

According to the rules, binary tree traversal includes: pre-order/in-order/post-order recursive structure traversal:

  1. Preorder traversal (also known as preorder traversal) - the operation of accessing the root node occurs before traversing its left and right subtrees.
  2. Inorder Traversal (InorderTraversal) - The operation of accessing the root node occurs between traversing its left and right subtrees.
  3. Postorder Traversal - The operation of accessing the root node occurs after traversing its left and right subtrees.
void PreOrder(BTNode* root)//前序遍历
{
    
    
	if (root == NULL)
	{
    
    
		printf("# ");//为空打印#
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}
void InOrder(BTNode* root)//中序遍历
{
    
    
	if (root == NULL)
	{
    
    
		printf("# ");//为空打印#
		return;
	}
	InOrder(root->left);
	printf("%d", root->data);
	InOrder(root->right);
}
void PostOrder(BTNode* root)//后序遍历
{
    
    
	if (root == NULL)
	{
    
    
		printf("# ");//为空打印#
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

3. Heap:

1. Basic concepts and structure:

If there is a set, all its elements are stored in a one-dimensional array in the order of a complete binary tree, and satisfy: The value of each node is always no greater than or no less than the value of its parent node; The heap with the largest root node is called the maximum heap or large root heap, and the heap with the smallest root node is called the minimum heap or small root heap.
Properties of heap:

  • The value of a node in the heap is always no greater than or no less than the value of its parent node;
  • The heap is always a complete binary tree

Insert image description here

2. Implementation of heap:

a. Adjust the algorithm downward:

We can logically view an array as a complete binary tree. We can adjust it into a small heap through the downward adjustment algorithm starting from the root node. The downward adjustment algorithm has a premise: the left and right subtrees must be a heap before they can be adjusted.
Insert image description here

b. Creation of heap:

We can logically think of an array as a complete binary tree, and the leaf nodes can be regarded as a heap, so starting from the last non-leaf node and adjusting the algorithm downwards in order can create a heap.

Code demo:

void HeapInit(Heap* ph)//堆的初始化
{
    
    
	assert(ph);
	ph->arr = NULL;
	ph->capacity = ph->size = 0;
}
void HeapDestroy(Heap* ph)//堆的销毁
{
    
    
	assert(ph);
	free(ph->arr);
	ph->arr = NULL;
	ph->size = ph->capacity = 0;
}
void Swap(HeapDataType* child, HeapDataType* parent)//交换堆数组内容
{
    
    
	HeapDataType tmp = *child;
	*child = *parent;
	*parent = tmp;
}
void AdjustUp(int* arr, int child)//向上调整函数
{
    
    
	while (child > 0)
	{
    
    
		int parent = (child - 1) / 2;
		if (arr[parent] > arr[child])
		{
    
    
			Swap(&arr[child], &arr[parent]);//交换堆数组内容
			child = parent;
		}
		else
		{
    
    
			break;
		}
	}
}
void HeapPush(Heap* ph, HeapDataType x)//插入数据
{
    
    
	assert(ph);
	if (ph->capacity == ph->size)
	{
    
    
		int newcapacity = ph->capacity = 0 ? 4 : ph->capacity * 2;
		HeapDataType* tmp = (HeapDataType*)realloc(ph->arr, sizeof(HeapDataType) * newcapacity);
		if (tmp == NULL)
		{
    
    
			perror("malloc");
			exit(-1);
		}
		ph->arr = tmp;
		ph->capacity = newcapacity;
	}
	ph->arr[ph->size] = x;
	ph->size++;
	AdjustUp(ph->arr, ph->size - 1);//向上调整函数
}
void AdjustDown(HeapDataType* arr, int size, int parent)//向下调整函数
{
    
    
	int child = parent * 2 + 1;
	while (child < size)
	{
    
    
		if (child + 1 < size && arr[child] > arr[child + 1])
		{
    
    
			child++;
		}
		if (arr[parent] > arr[child])
		{
    
    
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
    
    
			break;
		}
	}
}
void HeapPop(Heap* ph)//堆的删除
{
    
    
	assert(ph);
	assert(!HeapEmpty(ph));
	Swap(&ph->arr[0], &ph->arr[ph->size - 1]);
	ph->size--;
	AdjustDown(ph->arr, ph->size, 0);//向下调整函数
}
HeapDataType HeapTop(Heap* ph)//返回堆顶元素
{
    
    
	assert(ph);
	assert(!HeapEmpty(ph));
	return ph->arr[0];
}
bool HeapEmpty(Heap* ph)//堆的判空
{
    
    
	assert(ph);
	return ph->size == 0;
}
int HeapSize(Heap* ph)//返回堆中元素个数
{
    
    
	assert(ph);
	return ph->size;
}
void HeapPrint(Heap* ph)//打印堆中数据
{
    
    
	assert(ph);
	assert(!HeapEmpty(ph));
	for (int i = 0; i < ph->size; i++)
	{
    
    
		printf("%d ", ph->arr[i]);
	}
	printf("\n");
}

c. Heap sort:

Heapsort refers to a sorting algorithm designed using a data structure such as a stacked tree (heap). It is a type of selection sorting. It selects data through the heap. It should be noted that a large heap should be built in ascending order, and a small heap should be built in descending order.

Principle: For example, build a large heap in ascending order, that is, the top element of the heap is the maximum value of the sequence. For example, the subscript of the element at the end of the heap is end. Exchange the top element and the element at the end of the heap so that the maximum value reaches the end of the heap, then end-1, and then move to the end of the heap. Adjust down and continue to exchange, and so on, and finally end=0 sorting ends.
Insert image description here

void HeapSort(int* arr, int sz)
{
    
    

	int end1 = (sz - 1 - 1) / 2;//求最后一个节点的父节点,这个父节点即最后一个非叶子节点
	for (int i = end1; i >= 0; i--)
	{
    
    
		AdjustDown(arr, sz, i);
	}
	//开始排序
	//升序 -- 用大堆
	//降序 -- 用小堆
	int end2 = sz - 1;
	while (end2 > 0)
	{
    
    
		Swap(&arr[0], &arr[end2]);
		AdjustDown(arr, end2, 0);
		end2--;
	}
	for (int i = 0; i < sz; i++)
	{
    
    
		printf("%d ", arr[i]);
	}
}

Summarize

The above is the basic content of binary trees and heaps that I will talk about today. This article only briefly introduces the basic content of tree structures, binary trees and heaps. If it is helpful to you who have just read this blog, don’t forget to give the blogger a three-way oh!

Guess you like

Origin blog.csdn.net/weixin_61661271/article/details/125258907