二分木のトラバーサル+基本的な演習

以前の完全な二分木はデータの保存に適しており、メモリに継続的に保存されるため、シーケンステーブルで実装され、ヒープソートとTOP-Kの問題が発生します。

今日、私たちは二分木の探索問題について学び、二分木のいくつかの基本的な演習を完了します


コンテンツ

二分木のトラバーサル

先序

アクセス順序:

アイコン:

 中間シーケンス

アクセス順序:

アイコン:

ポストオーダー

アクセス順序:

アイコン:

連鎖二分木を手動で構築する 

意味

 ノードの作成

二分木を作成する 

プレオーダートラバーサル

順序のないトラバーサル

 注文後のトラバーサル

エクササイズ

二分木ノードツリーを見つける

二分木で子ノードの数を見つける 

k番目の層のノードの数 

二分木の深さ

値xの二分木検索ノード


二分木のトラバーサル

二分木のトラバーサルには、次のものが含まれます。preorder / inorder /postorderの再帰的構造トラバーサル:

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

次のバイナリツリーを例として取り上げ、プレオーダー、インオーダー、およびポストオーダートラバーサルを実行します。

先序

分析:ルートノードから始めて、最初にルートにアクセスし、次に左側のサブツリーにアクセスします(ルートノードは最初に左側のサブツリーにアクセスし、次に左側のサブツリーと右側のサブツリーにアクセスします)、最後に右側のサブツリーにアクセスします(最初にルートノードにアクセスし、次に左側のサブツリーにアクセスします。左側のサブツリーと右側のサブツリー)

アクセス順序:

最初にツリールート1にアクセスし、次にツリーの左側のサブツリーL1にアクセスします。

L1ルート2にアクセスしてから、左側のサブツリーLl2にアクセスします。

Ll2ルート3にアクセスしてから、左側のサブツリーにアクセスします。左側のサブツリーが空の場合は右側のサブツリーにアクセスし、右側のサブツリーが空の場合は前のサブツリーL1に戻ります。

この時点で、L1の左側のサブツリーへのアクセスが完了し、L1の右側のサブツリーがNULLにアクセスされます。空の場合は、前のツリーツリーが返されます。

この時点で、ツリールートと左側のサブツリーにアクセスし、ツリーの右側のサブツリーR1にアクセスします。

R1ルート4にアクセスしてから、R1の左側のサブツリーRl1にアクセスします。

Rl1ルート5にアクセスし、次にRl1の左側のサブツリーと右側のサブツリーNULLにアクセスして、前のツリーR1に戻ります。

この時点で、R1の左側のサブツリーRl1にアクセスし、次にR1の右側のサブツリーRr1にアクセスします。

Rr1ルート6にアクセスし、次にRr1 NULLの左右のサブツリーにアクセスして、前のツリーR1に戻ります。

この時点で、R1のルートと左右のサブツリーにアクセスし、前のツリーが返されます。

この時点で、ツリーのルートと左のサブツリーが訪問され、ツリー全体が訪問されます。

アイコン:

 中間シーケンス

分析:最初に左側のサブツリーにアクセスし、左側のサブツリーにアクセスした後にルートノードにアクセスし、ルートノードにアクセスした後に右側のサブツリーにアクセスします。左側のサブツリーと右側のサブツリーも、最初に左側のサブツリーにアクセスし、次にルートにアクセスし、最後に右側のサブツリーにアクセスします。

アクセス順序:

ツリーのルートから始めて、最初にその左側のサブツリーL1にアクセスします。

左側のサブツリーL1は空ではありません。L1の左側のサブツリーLl2にアクセスしてください。

左側のサブツリーLl2は空ではありません。Ll2の左側のサブツリーにアクセスしてください。

左側のサブツリーが空の場合は、Ll2のルート3にアクセスし、次にLl2の右側のノードにアクセスします。右側のノードが空の場合は、サブツリーL1に戻ります。

サブツリーL1の左側のサブツリーにアクセスした後、L1のルート2にアクセスし、次にL1の右側のサブツリーにアクセスします。空の場合は、ツリーツリーに戻ります。

ツリーの左側のサブツリーにアクセスした後、ツリーのルート1にアクセスし、次にツリーの右側のサブツリーR1にアクセスします。

右側のサブツリーR1は空ではありません。R1の左側のサブツリーRl1にアクセスしてください。

Rl1は空ではありません。Rl1の左側のサブツリーにアクセスし、左側のサブツリーは空です。Rl1のルート5にアクセスしてから、右側のサブツリーにアクセスします。

右側のサブツリーが空の場合は、前のツリーR1に戻ります。

R1の左側のサブツリーにアクセスした後、そのルート4にアクセスし、次に右側のサブツリーRr1にアクセスします。

Rr1は空ではありません。左側のサブツリーにアクセスし、左側のサブツリーにアクセスし、Rr1ルート6にアクセスしてから、右側のサブツリーにアクセスして、R1を返します。

この時点で、ツリーの左側のサブツリー、ルート、および右側のサブツリーがすべて訪問されます。

アイコン:

ポストオーダー

分析:最初に左側のサブツリー(左側のサブツリーは左側のサブツリー、右側のサブツリー、ルートでもあります)にアクセスし、次に右側のサブツリー(右側のサブツリーは左側のサブツリー、右側のサブツリー、ルートでもあります)にアクセスし、最後にルートノードにアクセスします。

アクセス順序:

最初にツリーにアクセスします。空でない場合は、左側のサブツリーL1にアクセスします。L1が空でない場合は、左側のサブツリーLl2にアクセスします。

Ll2は空ではなく、左側のサブツリーにアクセスします。空です。右側のサブツリーにアクセスします。空です。ルート3にアクセスし、前のツリーL1に戻ります。

L1の左側のサブツリーにアクセスした後、右側のサブツリーにアクセスします。空です。ルート2にアクセスし、前のツリーに戻ります。

ツリーの左側のサブツリーにアクセスした後、右側のサブツリーR1にアクセスします。R1が空でない場合は、左側のサブツリーRl1にアクセスします。

Rl1は空ではなく、左側のサブツリーにアクセスします。空です。右側のサブツリーにアクセスします。空の場合は、ルート5にアクセスし、前のツリーR1に戻ります。

R1の左側のサブツリーにアクセスした後、右側のサブツリーRr1にアクセスします。Rr1が空でない場合は、左側のサブツリーにアクセスします。

Rr1の左側のサブツリーは空です。右側のサブツリーにアクセスします。空の場合は、ルート6にアクセスして、前のツリーR1に戻ります。

この時点で、R1の左側のサブツリーの右側のサブツリーへのアクセスが完了し、そのルート4にアクセスして、前のツリーに戻します。

このとき、ツリーの左右のサブツリーが訪問され、そのルート1が訪問され、ツリー全体が訪問されます。

アイコン:

連鎖二分木を手動で構築する 

上記のトラバーサルが常に中央の前のツリーに戻ることを見つけるのは難しくありません。再帰的なアイデアが使用されています。ここでは、単純な連鎖バイナリツリーを手動で実装して、プレオーダー、インオーダー、注文後のトラバーサル。

意味

各ノードがデータ、左側のサブツリーアドレス、右側のサブツリーアドレスで構成されることを定義します

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;  //数据
	struct BinaryTreeNode* left;   //左子树地址
	struct BinaryTreeNode* right;  //右子树地址
}BTNode;

 ノードの作成

作成したノードに固定データを配置し、左右のサブツリーポインタを空のままにします

BTNode* BuyNode(BTDataType x)
{
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->data = x;
	root->left = NULL;
	root->right = NULL;
	return root;
}

二分木を作成する 

手動でノードを作成し、左右のサブツリーポインタを固定位置に向けます。例として上記の二分木を取り上げます。

//手动创建
BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}

プレオーダートラバーサル

プレオーダートラバーサルの順序の分析によると、ルート、左サブツリー、右サブツリー、プレオーダーコードを記述します。

// 二叉树前序遍历
void PreOrder(BTNode* root)
{
	if (root==NULL)
	{
		return;
	}
	printf("%d ",root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

順序のないトラバーサル

// 二叉树中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

 注文後のトラバーサル

// 二叉树后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

エクササイズ

二分木ノードツリーを見つける

方法1:カウント変数をプレオーダー、インオーダー、ポストオーダーに追加します(再帰中にカウントがリセットされないように、変数はグローバルまたは静的です)

短所:繰り返し呼び出されるとカウントが累積され、呼び出されるたびにカウントを0にリセットする必要があります。

//定义全局或者静态变量
//多次调用会累加
int count = 0;
int BTreeSize(BTNode* root)
{
	if (root == NULL)
		return;
	++count;
	BTreeSize(root->left);
	BTreeSize(root->right);
	return  count;
}

方法2:トラバース+カウント(トラバース時にトラバーサルアドレスを渡す)

//遍历+计数
//将变量地址传过去,计数---思想最优
void BTreeSize(BTNode* root,int* count)
{
	if (root == NULL)
		return;
	++(*count);
	BTreeSize(root->left,count);
	BTreeSize(root->right,count);
}

方法3:再帰-分割統治法

ルートが空の場合は、0、左側のサブツリーノードの数+右側のサブツリーノードの数+ 1(ルートノード自体)を返します。

//递归--分治思想--节点个数
int BTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BTreeSize(root->left) + BTreeSize(root->right) + 1;
}

二分木で子ノードの数を見つける 

二分木の子ノードの数=左のサブツリーのノードの数+右のサブツリーのノードの数

リーフノード:左側のサブツリーと右側のサブツリーが空のノード

//叶子节点个数
int BTreeLeafSize(BTNode* root)
{
	if (root==NULL)
	{
		return 0;
	}
	if (root->left == NULL&&root->right==NULL)
	{
		return 1;
	}
	return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}

k番目の層のノードの数 

 ツリーの第3レベルでノードの数を見つけます

つまり、L1とR1の第2層ノードの合計です。

つまり、Ll1、Rl1、およびRr1の第1層ノードの合計です。

k = 1の場合、1を返すだけです

//第k层节点个数
int BTreeLeveSize(BTNode* root,int k)
{
	assert(k>=1);
	if (root==NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BTreeLeveSize(root->left, k - 1) + BTreeLeveSize(root->right, k - 1);

二分木の深さ

二分木の深さ=左のサブツリーと右のサブツリーの最大の深さ+1

左右のサブツリーの高さを比較して、どちらを返すかを決定する必要があります

//二叉树深度
int BTreeDepth(BTNode* root)
{
	if (root==NULL)
	{
		return 0;
	}
	int leftdepth = BTreeDepth(root->left);
	int rightdepth = BTreeDepth(root->right);
	return leftdepth >rightdepth ? leftdepth + 1 : rightdepth + 1;
}

値xの二分木検索ノード

ルートが探しているノードであるかどうかを判断し、そうである場合はノードアドレスを返します

そうでない場合は、左側のサブツリーに移動して検索し、戻りノードアドレスを見つけ、見つからない場合は空を返します

次に、検索する適切なサブツリーを入力し、リターンノードアドレスを見つけます。見つからない場合は空を返します。

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root==NULL)
	{
		return NULL;
	}
	if (root->data == x)
		return root;
	if (BinaryTreeFind(root->left, x))
		return BinaryTreeFind(root->left,x);
	if (BinaryTreeFind(root->right, x))
		return BinaryTreeFind(root->right, x);
	return NULL;
}

おすすめ

転載: blog.csdn.net/weixin_53316121/article/details/124155945