リンクされた二分木検索、トラバーサル (再帰的な実装) およびその他のインターフェイスの実装

目次

序文:

1: 二分木の確立

(1) 本論文で用いた二分木表現法

(2) 二分木を手動で構築する

2: 二分木の探索

(1) 二分木の3つの走査方法

(2) 思想の分割統治

(3) 予約トラバーサル

 (4) 順序通りのトラバーサル

(5) ポストオーダートラバーサル

3: 二分木のノードと高さ (深さ) を見つける

(1) 二分木のノードを見つける

①二分木のすべてのノードを見つける

②二分木の葉ノードを探す

③二分木のk層目のノード数を求める

(2)二分木の高さ(深さ)を求める

4: 二分木探索


序文:

二分木について簡単に説明し、ヒープなどの特殊な二分木を実装しましたが、今回は連鎖二分木のトラバーサル(連鎖二分木の非常に重要な部分)や検索などの機能を実装します。

初めて二分木へのリンクが添付されています: http://t.csdn.cn/pMOia

1: 二分木の確立

(1) 本論文で用いた二分木表現法

①各ノードは構造体です。

②各ノードはデータを格納するだけでなく、自分の子ノードのアドレス(構造体ポインタ)も格納します。

③ ノードに子がない場合は、空を指します

回路図:

コード:

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	//存储左孩子的地址
	struct BinaryTreeNode* left;
	//存储右孩子的地址
	struct BinaryTreeNode* right;
	BTDataType data;
}BTNode;

(2) 二分木を手動で構築する

malloc( )関数を呼び出してスペースを適用し、データを挿入します。

②ノードを順番にリンクします。

③スペースの申請やデータの挿入を何度も行う必要があるため、この部分を関数 BuyNewNode( ) にカプセル化します。

コード:

//申请新节点
BTNode* BuyNewNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		printf("malloc error\n");
		exit(-1);
	}

	newnode->data = x;
	newnode->left = newnode->right = NULL;
	
	return newnode;
}


void test1()
{
    //手动建立一个二叉树
	BTNode* nodeA = BuyNewNode('A');
	BTNode* nodeB = BuyNewNode('B');
	BTNode* nodeC = BuyNewNode('C');
	nodeA->left = nodeB;
	nodeA->right = nodeC;
	BTNode* nodeD = BuyNewNode('D');
	BTNode* nodeE = BuyNewNode('E');
	BTNode* nodeF = BuyNewNode('F');
	nodeB->left = nodeD;
	nodeC->left = nodeE;
	nodeC->right = nodeF;
}

この二分木のグラフ:

2: 二分木の探索

(1) 二分木の3つの走査方法

1.プリオーダー トラバーサル(プリオーダー トラバーサル、プリオーダー トラバーサルとも呼ばれる) -ルート ノードにアクセスする操作は、その左右をトラバースするときに発生します。
サブツリーの前
2. Inorder Traversal (Inorder Traversal) -ルート ノードにアクセスする操作は、左右のサブツリーをトラバースする際に発生します。
(間)。
3. Postorder Traversal (Postorder Traversal) -ルート ノードにアクセスする操作は、左右のサブツリーをトラバースした後に発生します

(2) 思想の分割統治

分割統治とは、大雑把に言えば、複雑に見える問題を単純な小さな問題に変えることであり、最終的に問題を解決するという考え方もこの記事の核心です。
たとえば、学校が生徒数を数えたい場合は、校長に一人ずつ数えてもらうか、校長が学年担任に、校長が校長に、校長が寮に伝えます。頭。

(3) 予約トラバーサル

 preorder traversal を行う場合、この二分木を見てみましょう。

最初に A を出力し、次に A の左側の部分木をトラバースします。

B を出力し、B の左側の部分木をトラバースします。

D を出力し、D の左側の部分木をトラバースします。

空である場合、D の右側の部分木をトラバースします。

空の場合、B の左側のサブツリーのトラバースが終了し、B の右側のサブツリーがトラバースされます。

空の場合、A の左側のサブツリーのトラバースが終了し、A の右側のサブツリーがトラバースされます。

……………………

②ツリー全体を先行順にトラバースしたい場合、それを見つけるのは難しくありません

最初に A を訪問し、次に A の左側の部分木と右側の部分木を preorder でトラバースするように変換できます

A の左部分木の事前順序走査は、最初に B を訪問し、次に B の左部分木と右部分木の事前順序走査に変換できます

B の右側のサブツリーの事前順序探索は、最初に D を訪問し、次に D の左右のサブツリーの事前順序探索に変換できるため、より大きな問題を非常に小さな問題に変換できます。

コード:

//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("空  ");
		return;
	}

	//打印
	printf("%c  ", root->data);
	//左子树
	PreOrder(root->left);
	//右子树
	PreOrder(root->right);
}

この種の再帰に遭遇して理解できない場合は、再帰的な展開図を描くことができます:

 (4) 順序通りのトラバーサル

この二分木を順番にたどりたい場合。

最初に A の左部分木をトラバースします。

B の左部分木をトラバースします。

D の左部分木をトラバースします。

空にして D を出力し、D の右側の部分木をトラバースします。

B を空にして出力し、B の右部分木をたどります。

空にして A を出力し、A の右側の部分木をトラバースします。

………………

②ツリー全体を順番にたどり、

A の左側の部分木を順番にトラバースし、次に右側の部分木を inorder でトラバースした後、 A を訪問するように変換できます

A の左側のサブツリーの順序通りの走査は、B の左側の部分木の順序通りの走査に変換でき、次に B を訪問し、次に右側の部分木の順序通りの走査に変換できます

B の右側のサブツリーの順序どおりのトラバーサルは、D の左側のサブツリーの順序どおりのトラバーサルに変換してから、D にアクセスし、次に右側のサブツリーの順序どおりのトラバーサルに変換できます。小さな問題。

コード:

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("空  ");
		return;
	}

	//左树
	InOrder(root->left);
	//打印
	printf("%c  ", root->data);
	//右树
	InOrder(root->right);
}

再帰展開図:

(5) ポストオーダートラバーサル

この二分木に対してポストオーダートラバーサルを行いたい場合。

最初に A の左部分木をトラバースします。

B の左部分木をトラバースします。

D の左部分木をトラバースします。

空、D の右側の部分木をトラバースします。

D を空にして出力し、B の右側の部分木をトラバースします。

空にして B を出力し、A の右側の部分木をトラバースします。

……………………

②ツリー全体のポストオーダートラバーサル、

A を訪問するために、 A の左側のサブツリーと右側のサブツリーのポストオーダー トラバーサルに変換できます

A の左側のサブツリーのポストオーダー トラバーサルは、B の左右のサブツリーのポストオーダー トラバーサルに変換してから、 Bにアクセスできます。

B の右側のサブツリーの後順トラバーサルは、D の左右のサブツリーの後順トラバーサルに変換され、次に D にアクセスできますこれにより、大きな問題が非常に小さな問題に変換されます。

コード:

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("空  ");
		return;
	}
	//左树
	PostOrder(root->left);
	//右树
	PostOrder(root->right);
	//打印
	printf("%c  ", root->data);
}

再帰展開図:

3: 二分木のノードと高さ (深さ) を見つける

(1) 二分木のノードを見つける

①二分木のすべてのノードを見つける

アイデア:

①ノードアドレスが空でない限り、ノードと見なされます。

②ツリーノード全体を見つけるには、 A の左サブツリーのノード数に A の右サブツリーのノード数を加えたものに 1 を加えたものに変換できます

A の左側の部分木のノード数は、B の左側の部分木のノード数 + B の右側の部分木のノード数 + 1に変換できます

B の左側の部分木のノード数は、D の左側の部分木のノード数 + D の右側の部分木のノード数 + 1に変換できます

D の左右の部分木は空で、B の左側の部分木のノード数は 1 です

………………………………

コード:

//求树的节点数
int BinaryTreeSize(BTNode* root)
{
	/*if (root == NULL)
	{
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;*/
	//更加简洁的写法
	return root == NULL ? 0 :
		BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

再帰展開図:

②二分木の葉ノードを探す

アイデア:

左右の子が空のノードは葉ノードとして数えます

木全体の葉ノードを見つけることは、A の左部分木の葉ノードとAの右部分木の葉ノードを見つけることに変換できます。

A の左部分木の葉ノードを見つけることは、B の左部分木の葉ノードと B の右部分木の葉ノードを見つけることに変換できます。

D の左右の子はすべて空で、B の左側のサブツリーの葉ノードは 1 です。

…………………………

コード:

//求叶子节点
int BinaryLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if ((root->left == NULL) && (root->right == NULL))
	{
		return 1;
	}

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

再帰展開図:

③二分木のk層目のノード数を求める

アイデア:

k が 3 であるとします。

木の第1層のノード数は1です

②空のノードとは、ノード数が 0 であることを意味します

木全体の第 3 層のノード数を求めることは、 Aの左部分木と右部分木の第 2 層のノード数の合計を求めることに変換できます。

A の左側の部分木の2番目の層にあるノードの数を求めることは、B の左側の部分木の最初の層と右側の部分木のノードの数の合計を求めることに変換できます。

B の左部分木は空ではなく、層の数は 1 で、ノードの数は 1 です

B の右側のサブツリーは空で、ノードの数は 0 です

………………………………

コード:

//求第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root,int k)
{
	//非法输入直接报错
	assert(k >= 1);

	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}

	return BinaryTreeLevelKSize(root->left, k - 1)
		+ BinaryTreeLevelKSize(root->right, k - 1);
}

再帰展開図:

(2)二分木の高さ(深さ)を求める

アイデア:

空の木の高さは 0 です

ツリールートノードの左右の子はすべて空で、高さは 1 です

ツリーの最終的な高さは、左右のサブツリーのうち深さの大きい方に 1 を加えたものになります

木全体の高さを求めることは、A の左右の部分木のうち、高さが大きい方に 1 を加算することに変換できます。

A の左側の部分木の高さを求めることは、B の左側と右側の部分木の高さに 1 を加算することに変換できます。

B の左側の部分木の高さを求めることは、D の左側と右側の部分木の高さに 1 を加算することに変換できます。

D の左右の子は空で、B の左側のサブツリーの高さは 1 です

B の右側の部分木は空の木で、B の右側の部分木の高さは 0 です

大きい方の辺に 1 を足すと、A の左側の部分木の高さは 2 になります

コード:

//求二叉树的高度(深度)
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	return max(BinaryTreeDepth(root->left), BinaryTreeDepth(root->right)) + 1;
}

再帰展開図:

4: 二分木探索

機能:検索するデータ xを入力し、ノードのアドレスを返します。

アイデア:

Eを見つけると仮定します

見つかった場合はノードアドレスを返し、見つからなかった場合は空を返す

②再帰的に呼び出す場合は戻り値で見つかったかどうかを判断する必要があります

空でない場合は見つかったことを意味し、検索を続行する必要はなく、ノード アドレスが返されます

③ツリー全体でEを検索し、まずルートがAの左右のサブツリーではなく、Eであるかどうかを確認します。

まず、B の左部分木と右部分木を検索するのではなく、A の左部分木のルートが E であるかどうかを確認します。

まず、D の左部分木と右部分木を検索する代わりに、B の左部分木のルートが E であるかどうかを確認します。

D の左右のサブツリーが空の場合は、空を返します。

B の右側の部分木が空の場合、空を返します。

A の左部分木を検索した後、見つからない場合は、A の右部分木を検索します。

最初に、A の右部分木の根が C の左部分木と右部分木ではなく、E であるかどうかを確認します。

………………………………

コード:

//查找值为x的节点
BTNode* BianrtTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}

	BTNode* leftRet = BianrtTreeFind(root->left,x);
	if (leftRet != NULL)
	{
		return leftRet;
	}

	BTNode* rightRet = BianrtTreeFind(root->right,x);
	if (rightRet != NULL)
	{
		return rightRet;
	}

	return NULL;
}

グラフを再帰的に展開します (E を見つけます)。

おすすめ

転載: blog.csdn.net/2301_76269963/article/details/130231257