C言語のデータ構造 - 木構造の木と二分木

序文

バイナリツリーの用途は何ですか?
二分木は広く使われています。

オペレーティング システムのソース プログラムでは、ツリーとフォレストを使用してファイル システムを構築します。window や linux などのファイル管理システムはすべてツリー構造です。C コンパイラのソース コードなどのコンパイル システムでは、バイナリ ツリーの順序走査形式を使用して、C 言語の式を格納します。二分木自体の応用例も多く、例えば、ハフマン二分木はJPEGの符号化・復号化システム(圧縮・伸張処理)のソースコードで使われており、プロセッサを書くための命令にも使えます。可変長命令システムを形成するためのバイナリ ツリー. さらに、バイナリ ソート ツリーは、データの並べ替えと高速検索に使用されます。

目次

1. 木の概念と構造
2. 二分木の概念と構造
3. 二分木のチェーン構造の実装

1. 木の構造と概念(理解)

1.1 ツリーの概念

ツリーは非線形データ構造であり、n (n>=0) 個の有限ノードで構成される階層関係のセットです。根が上を向き、葉が下を向いている逆さまの木のように見えることから、ツリーと呼ばれます。
ルート ノードと呼ばれる特別なノードがあります. ルート ノードには先行ノードはありません
. ルート ノードを除いて, 他のノードは M (M>0) 個の互いに素な集合 T1, T2, ..., Tm に分割されます. ここで各集合はTi (1<= i <= m) は、構造がツリーに似たサブツリーです。各サブツリーのルート ノードには、先行ノードが 1 つだけあり、後続ノードが 0 個以上ある可能性がある
ため、ツリーは再帰的に定義されます。

ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入
ノードの次数: ノードに含まれるサブツリーの数は、ノードの次数と呼ばれます; 上図に示すように: A は 6 葉ノード
または終端ノードです: 次数 0 のノードは葉と呼ばれますノード; 上図に示すように: B, C, H, I... などのノードは葉ノード、
非終端ノードまたは枝ノードです: 次数が 0 ではないノード; 上図に示すように: 次のようなノードD、E、F、G... は分岐ノードです。
親ノードまたは親ノード: ノードに子ノードが含まれる場合、このノードはその子ノードの親ノードと呼ばれます。上の図に示すように、A はB 子ノード
または子ノードの親ノード : ノードに含まれるサブツリーのルート ノードは、ノードの子ノードと呼ばれます; 上図に示すように: B A の子ノードです.
兄弟ノード: ノード同じ親ノードを持つノードは兄弟ノードと呼ばれます; 上の図に示すように: B と C は
兄弟ノード ツリーの次数です: ツリーでは、最大のノードの次数はツリーの次数と呼ばれます; 上の図のように: ツリーのレベルは 6
ノードです: ルートの定義から始めて、ルートが最初のレベル、ルートの子ノードが 2 番目のレベル、というように、
ツリーの高さまたは深さ: 最大ツリー内のノードのレベル; 上の図のように: ツリーの高さは 4
ノードの祖先です: ルートからノードの枝のすべてのノードまで; 上の図に示すように: A はすべてのノードの祖先の子孫です
:ノードをルートとするサブツリー内のノードは、ノードの子孫と呼ばれます。上の図に示すように: すべてのノードは A
Forestの子孫です: m (m>0) 個の互いに素な木の集合はフォレストと呼ばれます; (データ構造における学習と検索の本質は
フォレストです)

1.2 ツリー表現

ツリー構造は線形テーブルよりも複雑で、格納と表現が面倒ですが、実際には、親表現、子表現、子兄弟表現など、さまざまな方法でツリーを表現できます。ここでは、最も一般的に使用される子兄弟表記を簡単に理解できます。

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

ここに画像の説明を挿入
ここに画像の説明を挿入

1.3 実際のツリーの適用 (ファイル システムのディレクトリ ツリー構造を表す)

ここに画像の説明を挿入

2. 二分木の概念と構造

2.1 コンセプト

バイナリ ツリーはノードの有限セットであり、空であるか、ルート ノードと左右のサブツリーと呼ばれる 2 つのバイナリ ツリーで構成されます。
二分木の特徴:

  1. 各ノードには最大で 2 つのサブツリーがあります。つまり、二分木には次数が 2 を超えるノードはありません。
  2. 二分木の部分木は左右に分かれており、部分木の順序を逆にすることはできません。

2.2 実際の二分木:

ここに画像の説明を挿入

2.3 データ構造の二分木:

ここに画像の説明を挿入

2.4 特別な二分木:

  1. 完全二分木: 二分木。各層のノード数が最大値に達すると、この二分木は完全二分木になります。つまり、バイナリ ツリーに K 層があり、ノードの総数が (2^k) -1 の場合、それは完全なバイナリ ツリーです。
  2. 完全二分木: 完全二分木は非常に効率的なデータ構造であり、完全二分木は完全二分木から派生します。深さが K でノードが n のバイナリ ツリーの場合、各ノードが、深さのあるフル バイナリ ツリーで 1 から n までの番号が付けられたノードと 1 対 1 で対応している場合にのみ、完全なバイナリ ツリーと呼ばれます。 Kの 完全二分木は特別な種類の完全二分木であることに注意してください。

ここに画像の説明を挿入

2.5 二分木の格納構造

二分木は、一般に、逐次構造と連鎖構造の 2 つの構造を使用して格納できます。
二分木の性質

  1. ルート ノードの層の数が 1 として指定されている場合、空でない二分木 のi 番目の層には最大 2^(i-1) 個のノードがあります。
  2. ルート ノードの層数が 1 として指定されている場合、深さ h の二分木のノードの最大数は 2^h-1 です。
  3. 任意の二分木で、次数が 0、葉ノードの数が n0、次数 2 の枝ノードの数が n2 の場合、n0=n2+1
  4. ルート ノードの層数を 1 と指定すると、ノードが n 個の完全な二分木の深さ h=LogN

2.5.1 順次保存:

順次構造ストレージは、ストレージに配列を使用することです. 一般に、配列は完全なバイナリ ツリーを表現する場合にのみ適しています. 実際には、ストレージに配列を使用するのはヒープだけです。二分木順次記憶域は、物理的には配列であり、論理的には二分木です

ここに画像の説明を挿入

2.5.2 チェーンストレージ

二分木の連結格納構造とは、二分木を表現するために連結リストを使用すること、つまり要素の論理関係を示すためにリンクを使用することを意味します。
通常の方法では、リンク リストの各ノードは、データ フィールドと左右のポインター フィールドの 3 つのフィールドで構成され、左右のポインターを使用して、左の子と右の子がリンク ポイントの格納アドレスを指定します。ノードの右の子が配置されます。鎖構造は、さらに二分鎖と三重鎖に分けられます. 現在、私たちの研究では一般的に二分鎖を使用しています. 後のコースでは、三重鎖を使用する赤黒木などの高レベルのデータ構造を学習します.

ここに画像の説明を挿入
以下では、バイナリ リンク リストを使用してバイナリ ツリーを実装します。

//二叉树结构的定义
typedef char BTDataType;

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

序文

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

順番通りに

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

後続

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

ノードの数を見つける

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

葉の数

//叶子数
int LeafNodeSize(BTNode* root)
{
    
    
	//空节点
	if (root == NULL)
	{
    
    
		return 0;
	}
	//叶子节点
	if (root->left == NULL && root->right==NULL)
	{
    
    
		return 1;
	}
	//既不是叶子节点也不是空节点

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

Queue を使用して幅優先探索を実装し、バイナリ ツリーをトラバースする

void LevelOrder(BTNode* root)
{
    
    
	Queue q;
	QueueInit(&q);
	if(root)
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
    
    
		BTNode* Front = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", Front -> data);
		if (Front->left)
		{
    
    
			QueuePush(&q, Front->left);
		}
		if (Front->right)
		{
    
    
			QueuePush(&q, Front->right);
		}

	}
	printf("\n");
	QueueDestroy(&q);

}

Queue.h

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
struct BinaryTreeNode;
typedef struct BinaryTreeNode* 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);

//返回队列长度
int QueueSize(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
//初始化
void QueueInit(Queue* pq)
{
    
    
	assert(pq);
	pq->head = NULL;
	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->tail = newnode;
		pq->head = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = newnode;


	}

}

//队头出
void QueuePop(Queue* pq)
{
    
    
	//1.一个
	//2.多个
	assert(pq);
	assert(pq->head);
	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(pq->head);
	return pq->head->data;
}

//取队列的队尾元素
QDataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(pq->head);
	return pq->tail->data;
}

//返回队列长度
int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;
	while (!cur)
	{
    
    
		size++;
		cur = cur->next;
	}
	return size;
}

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

test.c

//测试
int main()
{
    
    
	BTNode* A =(BTNode*)malloc(sizeof(BTNode));
	A->data = 'A';
	A->left = NULL;
	A->right = NULL;

	BTNode* B=(BTNode*)malloc(sizeof(BTNode));
	B->data = 'B';
	B->left = NULL;
	B->right = NULL;

	

	BTNode* C = (BTNode*)malloc(sizeof(BTNode));
	C->data = 'C';
	C->left = NULL;
	C->right = NULL;


	BTNode* D = (BTNode*)malloc(sizeof(BTNode));
	D->data = 'D';
	D->left = NULL;
	D->right = NULL;
	

	BTNode* E = (BTNode*)malloc(sizeof(BTNode));
	E->data = 'E';
	E->left = NULL;
	E->right = NULL;
	
	A->left = B;
	A->right = C;
	B->right = E;
	B->left = D;


	PrevOrder(A);
	printf("\n");
	InOrder(A);
	printf("\n");
	PostOrder(A);
	printf("\n");
	printf("BTNodeSize: %d\n", BTNodeSize(A));
	printf("BTNodeSize: %d\n", BTNodeSize(B));
	printf("BTNodeSize: %d\n", BTNodeSize(C));
	printf("叶子节点数:%d\n", LeafNodeSize(A));

	LevelOrder(A);
	return 0;
}

コードを実行した結果:
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_63181097/article/details/130050123