以前の完全な二分木はデータの保存に適しており、メモリに継続的に保存されるため、シーケンステーブルで実装され、ヒープソートとTOP-Kの問題が発生します。
今日、私たちは二分木の探索問題について学び、二分木のいくつかの基本的な演習を完了します
コンテンツ
二分木のトラバーサル
二分木のトラバーサルには、次のものが含まれます。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; }