二分木鎖構造の実現
1.二分木チェーン構造のトラバース
いわゆるトラバーサル(トラバーサル)とは、特定の検索ルートをたどり、ツリー内の各ノードに1回だけアクセスすることを指します。アクセスノードによって実行される操作は、特定のアプリケーションの問題によって異なります。トラバーサルは、バイナリツリーで最も重要な操作の1つであり、バイナリツリーでの他の操作の基礎となります。
1.1プレオーダー/ミドルオーダー/ポストオーダーの再帰的構造トラバーサル
いわゆるフロントミドルポストシーケンスは、ルートノードへのアクセスの場所に応じて名前が付けられます。
この部分をよりよく理解するには、まず、バイナリツリーを次の3つの部分として扱う必要があります。①ルートノード②左サブツリー③右サブツリー、ルートノードが空になるまで、トラバーサルは完了したと見なされ、停止します。
-
NLR:プレオーダートラバーサル(プレオーダートラバーサルとも呼ばれます)-ルートノードにアクセスする操作は、その左右のサブツリーをトラバースする前に発生します。(単純な順序は、ルートの左側のサブツリーと右側のサブツリーです)
-
LNR:インオーダートラバーサル-ルートノードにアクセスする操作は、左右のサブツリーをトラバースするときに発生します。(単純な順序は、左サブツリールート右サブツリーです)
-
LRN:ポストオーダートラバーサル-ルートノードにアクセスする操作は、その左右のサブツリーをトラバースした後に発生します。(単純な順序は、左側のサブツリーと右側のサブツリールートです)
説明する図:プレオーダー/ミドルオーダー/ポストオーダーのトラバーサルプロセス
プロローグ:
A B D NULL NULL E NULL NULL C NULL NULL
但是在显示的时候NULL是不显示的所以顺序为:A B D E C
ミドルオーダー:
一定要记住是先从左子树开始遍历,所以要不停的找到左子树为空的时候停止。
NULL D NULL B NULL E NULL A NULL C NULL
在不显示NULL的情况下的顺序为:D B E A C
シーケンス後:
NULL NULL D NULL NULL E B NULL NULL C A
在不显示NULL的情况下的顺序为:D B E A C
1.2シーケンストラバーサル
一次トラバーサル、中次トラバーサル、および後次トラバーサルに加えて、バイナリツリーもレイヤー順にトラバースできます。二分木のルートノードが1であると仮定すると、シーケンストラバーサルは二分木のルートノードから始まり、最初に第1層のルートノードにアクセスし、次に第2層のノードに左から右にアクセスします。次に、3番目のレイヤーのノードなど、ツリーのノードを上から下、左から右にレイヤーごとに訪問するプロセスは、レイヤーシーケンストラバーサルです。
2.完全なコード
#include<stdio.h>
#include<stdlib.h>
typedef char BTDateType;
typedef struct BinaryTreeNode
{
BTDateType _date;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
//前序遍历
//(先根遍历) 根 左子树 右子树
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->_date);//先打印根
PrevOrder(root->_left);
PrevOrder(root->_right);
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->_left);
printf("%c ", root->_date);//打印根
InOrder(root->_right);
}
//后续遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->_left);
PostOrder(root->_right);
printf("%c ", root->_date);//打印根
}
//这里的这个size如何解决才是真正需要好好处理思考的问题!
//int size = 0; 但是你遍历全局变量也不好,因为当你两次进行求这个TreeSize值的时候,他的值是累加的,
//int TreeSize(BTNode* root)
//{
// if (root == NULL)
// return 0;
//
// //int size = 0; 这个size是一个局部变量,我每次调用一次我就定义了一次size,所加的size不是同一个值,所以要把这个size变成全局变量
// size++;
// TreeSize(root->_left);
// TreeSize(root->_right);
// return size;
//}
//如果这里传的是int size 相当于传值,那么我第一次进去是++了,但是第二次进去如果不为空,++的size又不在是原来的了
//所以这里要传址,保证我一直是在通一个size上面进行累加
//void TreeSize(BTNode* root,int* psize)
//{
// if (root == NULL)
// return 0;
// else
// (*psize)++;
//
// TreeSize(root->_left, psize);
// TreeSize(root->_right,psize);
//}
//递归的方式求
int TreeSize(BTNode* root)
{
if (root == NULL)
return 0;
else
return 1 + TreeSize(root->_left) + TreeSize(root->_right); //这里很巧妙的避开了这个size值累加的值
}
//求叶子结点的个数
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->_left == NULL && root->_right == NULL)
return 1;
return TreeLeafSize(root->_left) + TreeLeafSize(root->_right);
}
//深度
int TreeDepth(BTNode* root)
{
//空的话,深度就是0
if (root == NULL)
return 0;
//此时既不是空也不是叶子的时候,我求出左的深度,再求出右的深度,取两个深度中较大的那一个,然后在+1
int LeftTreeDepth = root->_left;
int RighttTreeDepth = root->_right;
//避免掉代码过长导致冗余
return LeftTreeDepth > RightTreeDepth ? LeftTreeDepth+1:RightTreeDepth+1;
}
BTNode* CreateNode(char x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
node->_date = x;
node->_left = NULL;
node->_right = NULL;
return node;
}
int main()
{
BTNode* A = CreateNode('A');
BTNode* B = CreateNode('B');
BTNode* C = CreateNode('C');
BTNode* D = CreateNode('D');
BTNode* E = CreateNode('E');
//将他们链起来
A->_left = B;
A->_right = C;
B->_left = D;
B->_right = E;
PrevOrder(A);
printf("\n");
InOrder(A);
printf("\n");
PostOrder(A);
printf("\n");
//int sizea = 0;
//TreeSize(A, &sizea);
//printf("TreeSize: %d\n", sizea);
printf("TreeSize: %d\n", TreeSize(A)); 如果使用全局变量size,那么在打印一次size的时候上一次的值就会累加,这里就会变成10
//int sizeb = 0;
//TreeSize(B, &sizeb);
//printf("TreeSize: %d\n", sizeb);
printf("TreeSize: %d\n", TreeSize(A));
printf("TreeLeafSize: %d\n", TreeLeafSize(A));
printf("TreeDepth: %d\n", TreeDepth(A));
getchar();
return 0;
}
TreeSize、TreeLeafSize、TreeDepthのサイズについて考える方法は注目に値しますか?
再帰を使用せずにローカルサイズを自分で定義すると、計算で再帰するたびにサイズが再定義され、サイズが同じサイズに累積されず、結果は「いいえ」になります。ただし、グローバル変数を使用してサイズを定義すると、サイズを1回要求すると、サイズは前のサイズの値を保持してから累積されるため、問題が発生するという欠点があります。したがって、サイズ変数を彼に渡すことができると考えますが、渡される値は一時的なコピーにすぎず、サイズはまだ累積効果を達成できないため、アドレスを渡す必要があります。
ここでのトラバーサルプロセスは再帰的思考です。再帰的思考を使用して特別な思考を書き出すと、残りがどのように変更されても、思考は変更されません。渡されたルートが空の場合、TreeSizeに0を返すように依頼しました。他の条件は、左側のサブツリーのノード+右側のサブツリーのノードである必要があり、もちろんルートのルートノードは1である必要があります。TreeLeafSizeに、左右のサブツリーが空の場合にのみリーフノードと呼ばれるように依頼します。TreeDepthの深さを求めることは、左側のサブツリーの深さと右側のサブツリーの深さのどちらか大きい方を比較してから、ルートノードの深さを追加することです。
2.1前のコードに3つの欠落点を追加します
補足:①
二分木のKレベルのノード数②二分木の検索値がxのノード
③破壊する
//二叉树第K层结点个数
//当前树的第K层可以转换成左右子树的第K-1层
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
//二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDateType x)
{
if (root == NULL)
return NULL;
if (root->_date == x)
return root;
//如果在左边直接找到了,就不用再去右边找了
BTNode* node = BinaryTreeFind(root->_left, x);
if (node)
return node;
//没有在左边找到,那再去右边找
node = BinaryTreeFind(root->_right, x);
if (node)
return node;
return NULL;
}
//销毁
void DestoryTree(BTNode* root)
{
//最好使用后续遍历,如果使用先序遍历你会发现,root被你干掉了,那么你就找不到你的左右子树了
if (root == NULL)
return NULL;
DestoryTree(root->_left);
DestoryTree(root->_right);
free(root);
}