データ構造---二分木チェーン構造の実現

1.二分木チェーン構造のトラバース

いわゆるトラバーサル(トラバーサル)とは、特定の検索ルートをたどり、ツリー内の各ノードに1回だけアクセスすることを指します。アクセスノードによって実行される操作は、特定のアプリケーションの問題によって異なります。トラバーサルは、バイナリツリーで最も重要な操作の1つであり、バイナリツリーでの他の操作の基礎となります。

1.1プレオーダー/ミドルオーダー/ポストオーダーの再帰的構造トラバーサル

いわゆるフロントミドルポストシーケンスは、ルートノードへのアクセスの場所に応じて名前が付けられます。
この部分をよりよく理解するには、まず、バイナリツリーを次の3つの部分として扱う必要があります。①ルートノード②左サブツリー③右サブツリー、ルートノードが空になるまで、トラバーサルは完了したと見なされ、停止します。

  1. NLR:プレオーダートラバーサル(プレオーダートラバーサルとも呼ばれます)-ルートノードにアクセスする操作は、その左右のサブツリーをトラバースする前に発生します。(単純な順序は、ルートの左側のサブツリーと右側のサブツリーです)

  2. LNR:インオーダートラバーサル-ルートノードにアクセスする操作は、左右のサブツリーをトラバースするときに発生します。(単純な順序は、左サブツリールート右サブツリーです)

  3. 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;
}

ここに画像の説明を挿入
TreeSizeTreeLeafSizeTreeDepthのサイズについて考える方法は注目に値しますか?

再帰を使用せずにローカルサイズを自分で定義すると、計算で再帰するたびにサイズが再定義され、サイズが同じサイズに累積されず、結果は「いいえ」になります。ただし、グローバル変数を使用してサイズを定義すると、サイズを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);
}

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/MEANSWER/article/details/112606952