二叉树C语言简单实现

一、树

非线性结构、有一个根结点(无前驱结点)、除根结点外其余节点被分为互不相交的集合、子树不能有交集。

相关概念

1、结点的度:一个结点含有的子树的个数称为该结点的度。如上图ROOT的度是3.

2、叶节点或终端结点:度为0的结点。H、I、J、E、G

3、双亲结点:若一个结点含有子结点,则其称为自己子节点的双亲结点。

4、孩子结点:一个结点含有的子树的根节点称为该节点的子节点。

5、兄弟结点:具有相同双亲结点的结点互称为兄弟结点。

6、树的度:一棵树中,最大的结点的度称为树的度。

7、结点的层次:从根开始,根为第一层以此类推。

8、树的高度或者深度:树中结点的最大层次

9、堂兄弟结点:双亲在同一层的结点互称为堂兄弟结点

10、森林:由大于零棵互不相交的树组成的集合称为森林。

树的表示

树表示起来,既要保存当前结点的值,还要保存结点与结点之间的关系。所以就有着很多的表示方法如:双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法等。其中最常用的是孩子兄弟表示法(左孩子右兄弟);

如下图即为孩子兄弟表示法

 二、二叉树

概念

或者为空或者由一个根结点加上两颗分别为左子树和右子树的二叉树组成,其不存在度大于2的结点,且二叉树的子树有左右之分,次序是不能颠倒的,因此二叉树是有序树。

特殊的二叉树

满二叉树

如果一个二叉树每一层的结点数都达到了最大值,则称为满二叉树,即如果一个满二叉树有K层,则满二叉树的结点总数为2^{k}-1

完全二叉树

完全二叉树是由满二叉树引出来的,对于深度为K,结点数为n的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号1到n的结点一一对应,则称其为完全二叉树。完全二叉树是一种存储效率很高的数据结构。

二叉树的性质 

1、对于任意一棵二叉树,如果度为0的结点的个数为n,度为2的结点数为k,则有n=k+1

2、对于一棵有着n个结点的完全二叉树,如果按照从上向下从左向右对所有结点从0开始编号,则对于序号为i的结点有:

如果其有双亲结点,则其双亲结点的编号为(i-1)/2;

如果其有孩子结点,则其左孩子结点的编号为2i+1;

二叉树的存储

顺序存储

顺序存储就是利用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一棵树。但是顺序存储只适合表示完全二叉树,因为非完全二叉树会造成空间浪费。现实中只有堆才会使用数组来存储。

链式存储

链式存储即用链表来表示一颗二叉树,分为二叉链和三叉链。

二叉树二叉链的简单实现

二叉树的结点需要包含三个域,保存该结点数据的域,保存指向该结点的左孩子和右孩子的指针。

typedef struct TreeNode
{
    BTDataType _val;
    struct TreeNode* _left;
    struct TreeNode* _right;
}TreeNode;

建树结点

BTNode* BuyBinaryTree(BTDataType x)//创建一个树的节点
{
	BTNode* BT = (BTNode*)malloc(sizeof(BTNode));
	if (BT == NULL)
	{
		printf("申请节点失败\n");
		exit(-1);
	}
	BT->_data = x;
	BT->_left = NULL;
	BT->_right = NULL;
	return BT;
}

树的遍历

树的遍历我们用树的递归性质来做。

void PreOrder(BTNode* root)//前序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->_data);
	PreOrder(root->_left);
	PreOrder(root->_right);
}
void InOrder(BTNode* root)//中序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->_left);
	printf("%c ", root->_data);
	InOrder(root->_right);
}
void PostOrder(BTNode* root)//后序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->_left);
	PostOrder(root->_right);
	printf("%c ", root->_data);
}

树的层序遍历 

其实树的层序遍历就是将一颗树从上到下,从左到右按顺序依次打印,这里我们可以利用队列的先进先出的特点。

首先判断根结点是否为空,不为空时将根结点压栈,利用循环不断取出栈顶元素进行出栈操作(注意此时并不是将其删除,只是将其从队列里面拿掉),再打印当前结点的数据。后判断是否有孩子结点,如果有则依次压栈。根据队列先进先出的特点,我们就可以层序遍历打印树的每一个结点。

void BinaryTreeLevelOrder(BTNode* root)//层序遍历
{
	Queue q;
	QueueInit(&q);
	if (root == NULL)
		return ;
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front= QueueFront(&q);
		QueuePop(&q);
		printf("%c", front->_data);
		if (front->_left)
		{
			QueuePush(&q, front->_left);
		}

		if (front->_right)
		{
			QueuePush(&q, front->_right);
		}
	}
	QueueDestroy(&q);
	printf("\n");
}

判断当前二叉树是否是完全二叉树

对于非完全二叉树来说,进行层序遍历之后,会出现NULL结点之后还会出现非NULL结点的情况,而对于完全二叉树来说,在空结点NULL之后全是NULL,利用这个特点我们对树进行层序遍历,遇到NULL就跳出循环,对队列中剩下的元素判断是否出现NULL。

int BinaryTreeComplete(BTNode* root)//判断是否为完全二叉树,是则返回1,否则返回0,
{
	//根据完全二叉树的性质特点,进行层序遍历遇到NULL跳出循环,若接下来遍历中遇到的全是NULL,则是完全二叉树
	Queue q;
	QueueInit(&q);
	if (root == NULL)
		return 0;
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)//层序遍历,遇到第一个NULL跳出。
		{
			break;
		}
		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
	}
	while (!QueueEmpty(&q))//在后面序列中继续判断是否有非空
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);//销毁
			return 0;
		}
	}
	QueueDestroy(&q);
	return 1;
}

 求树结点的个数

int SizeBinaryTree(BTNode* root)//求树的大小,即节点的个数
{
	if (root == NULL)
		return 0;
	else
		return 1 + SizeBinaryTree(root->_left) + SizeBinaryTree(root->_right);
}

树的叶子个数

int SizeLeafTree(BTNode* root)//求树的叶子的个数
{
	if (root == NULL)
		return 0;
	else if (root->_left == NULL && root->_right == NULL)
		return 1;
	else
		return SizeLeafTree(root->_left) + SizeLeafTree(root->_right);
}

树的深度

int MaxDepthTree(BTNode* root)//求树的深度
{
	
	if (root == NULL)
	{
		return 0;
	}
	else
	{
		int Ldep = MaxDepthTree(root->_left);
		int Rdep = MaxDepthTree(root->_right);
		return 1 + ((Ldep >= Rdep) ? Ldep : Rdep);
	}	
}

二叉树第K层结点的个数

int BinaryTreeLevelKSize(BTNode* root,int k)//二叉树第K层节点的个数
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	else
		return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

查找值为x的结点,返回其结点 

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)//二叉树查找值为X的节点,并能返回相对应的节点
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->_data == 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 BinaryTreeDestroy(BTNode* root)//销毁二叉树,后序遍历销毁,不然先销毁头部可能会找不到子树
{
	if (root == NULL)
		return;
	BinaryTreeDestroy(root->_left);
	BinaryTreeDestroy(root->_right);
	free(root);
}

运行结果 

BTNode* CreatBinaryTree()//创建树
{
	BTNode* A = BuyBinaryTree('A');
	BTNode* B = BuyBinaryTree('B');
	BTNode* C = BuyBinaryTree('C');
	BTNode* D = BuyBinaryTree('D');
	BTNode* E = BuyBinaryTree('E');
	BTNode* F = BuyBinaryTree('F');
	A->_left = B;
	A->_right = C;
	B->_left = D;
	B->_right = E;
	C->_right = F;
	return A;
}
int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	PostOrder(root);
	printf("\n");
	int Tsz = SizeBinaryTree(root);
	printf("BinaryTree:%d\n", Tsz);
	int Lsz = SizeLeafTree(root);
	printf("LeafTree:%d\n", Lsz);
	int depth = MaxDepthTree(root);
	printf("DepthTree:%d\n", depth);
	int LevelK = BinaryTreeLevelKSize(root, 3);
	printf("BinaryTreeLevelKSize:%d\n", LevelK);
	BinaryTreeLevelOrder(root);
	int Btc=BinaryTreeComplete(root);
	printf("BinaryTreeComplete:%d\n", Btc);
	return 0;
}

 

 

 

 

 

 

 

 

线索二叉树(Threaded Binary Tree),又称为双亲链接树,是一种数据结构,它在二叉树的基础上添加了一些特殊的指针,使得查找、插入和删除操作更加高效。通常,线索二叉树会通过修改节的前驱(left)和后继(right)指针来指示查找路径的方向。 以下是一个简单的线索二叉树结构体和基本的操作函数的C语言实现: ```c // 节结构 typedef struct Node { int data; struct Node* left, *right, *prev, *next; // 添加 prev 和 next 指针 } Node; // 初始化新节 Node* createNode(int value) { Node* newNode = (Node*)malloc(sizeof(Node)); if (!newNode) { printf("Memory allocation failed.\n"); return NULL; } newNode->data = value; newNode->left = newNode->right = newNode->prev = newNode->next = NULL; return newNode; } // 插入节的基本步骤:如果节为空,创建新节;否则沿着线索找到合适的位置并更新线索 void insert(Node** root, int value) { Node* newNode = createNode(value); if (*root == NULL) { // 根节为空,直接作为根 *root = newNode; } else { Node* current = *root; while (true) { if (value < current->data) { if (current->left == NULL) { // 插入左侧,设置线索 current->left = newNode; newNode->prev = current->prev; newNode->next = current; if (current->prev != NULL) current->prev->next = newNode; else *root = newNode; break; } current = current->left; } else { if (current->right == NULL) { // 插入右侧,设置线索 current->right = newNode; newNode->prev = current; newNode->next = current->next; if (current->next != NULL) current->next->prev = newNode; break; } current = current->right; } } } } // 示例性的查找过程,实际应用需要结合线索更新 void search(Node* root, int value) { Node* current = root; while (current != NULL) { if (value == current->data) { // 找到了,处理后续操作... break; } else if (value < current->data) { current = current->left; } else { current = current->right; } } } // 更复杂的操作如删除和遍历都需要额外处理线索 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值