二叉树的学习以及c语言实现二叉树相关函数

1.树的概念及结构

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:每个结点有零个或多 个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结 点可以分为多个不相交的子树 。

2.二叉树的概念及结构

二叉树是节点的有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树 的二叉树组成。

二叉树的特点是:每个节点最多有两个子节点;二叉树的子数有左右之分,不能随意互换。

两种特殊的二叉树:一种是满二叉树,一种是完全二叉树。满二叉树是每一层的节点个数都达到最大值,比如第k层,则该层节点数为2^(k-1)。完全二叉树是对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对 应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉。

3.二叉树的存储方式

二叉树有两种结构存储,一种是顺序存储,一种是链式存储。顺序存储就是用数组来存储,而一般用数组知识和表示完全二叉树。而链式存储是用链表来表示一颗二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表 中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结 点的存储地址 。在这里我实现的是链式结构的二叉树的存储,同时也包含其他相关函数功能。

首先时头文件部分

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<malloc.h>

typedef char BTDataType;

typedef struct BTNode
{
	struct BTNode* _pLeft;
	struct BTNode* _pRight;
	BTDataType _data;
}BTNode;

typedef struct {
	BTNode* root;
	int used;
}Result;

// 1. 创建二叉树 
Result* CreateBinTree(BTDataType* array, int size);

// 拷贝二叉树 
BTNode* CopyBinTree(BTNode* pRoot);

// 销毁二叉树 
void DestroyBinTree(BTNode** pRoot);
// 二叉树的三种遍历方式 
void PreOrder(BTNode* pRoot);//递归实现前序遍历
void PreOrderNor(BTNode* pRoot);//非递归实现前序遍历
void InOrder(BTNode* pRoot);//递归实现中序遍历
void InOrderNor(BTNode* pRoot);//递归实现中序遍历
void PostOrder(BTNode* pRoot);//递归实现后序遍历
void PostOrderNor(BTNode* pRoot);//递归实现后序遍历
void LevelOrder(BTNode* pRoot);//层序遍历

// 获取二叉树中节点的个数 
int GetNodeCount(BTNode* pRoot);

// 求二叉树的高度 
int Height(BTNode* pRoot);

// 检测二叉树是否平衡O(N^2) 
int IsBalanceTree(BTNode* pRoot);
// 检测二叉树是否平衡O(N) 
int IsBalanceTree_P(BTNode* pRoot, int* height);

// 获取二叉数中叶子节点的个数 
int GetLeafNodeCount(BTNode* pRoot);

// 获取二叉树第K层节点的个数 
int GetKLevelNodeCount(BTNode* pRoot, int K);

// 获取二叉树中某个节点的双亲节点 
BTNode* GetNodeParent(BTNode* pRoot, BTNode* pNode);

// 求二叉树的镜像 
void Mirror(BTNode* pRoot);

接下来是函数功能实现部分

#include"test.h"
#include"Stack.h"
#include<math.h>
#include"queue.h"
// 1. 创建二叉树 

Result* CreateBinTree(BTDataType* array, int size)
{
	//前序遍历创建二叉树,并且空节点出用‘#’代替
	Result* ret = NULL;
	if (size == 0)
	{
		ret->root = NULL;
		ret->used = 0;
		return ret;
	}
	if (array[0] == '#')
	{
		ret->root = NULL;
		ret->used = 1;
		return ret;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->_data = array[0];
	Result* leftroot = CreateBinTree(array + 1, size - 1);
	Result* rightroot = CreateBinTree(array + 1 + leftroot->used, size - 1 - leftroot->used);
	root->_pLeft = leftroot->root;
	root->_pRight = rightroot->root;
	ret->root = root;
	ret->used = 1 + leftroot->used + rightroot->used;
	return ret;
}





// 拷贝二叉树 
BTNode* CopyBinTree(BTNode* pRoot)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (pRoot == NULL)
	{
		return NULL;
	}
	else
	{
		node->_data = pRoot->_data;
		node->_pLeft = CopyBinTree(pRoot->_pLeft);
		node->_pRight = CopyBinTree(pRoot->_pRight);
		return node;
	}
}

// 销毁二叉树 
void DestroyBinTree(BTNode** pRoot)
{
	if (*pRoot)
	{
		if ((*pRoot)->_pLeft)
		{
			DestroyBinTree(&(*pRoot)->_pLeft);
		}
		if ((*pRoot)->_pRight)
		{
			DestroyBinTree(&(*pRoot)->_pRight);
		}
		free(*pRoot);
		*pRoot = NULL;
	}
}
// 二叉树的三种遍历方式 
//前序遍历 根——>左子树——>右子树
//中序遍历 左子树——>根——>右子树
//后序遍历 左子树——>右子树——>根
//前序遍历递归实现
void PreOrder(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return;
	}
	else
	{
		printf("%c ", pRoot->_data);
		PreOrder(pRoot->_pLeft);
		PreOrder(pRoot->_pRight);
	}
}
//前序遍历非递归实现,非递归的实现需要利用栈,但是由于利用的是c语言,所以需要先写好一个有关栈的头文件
//中序和后序的非递归实现也是如此
void PreOrderNor(BTNode* pRoot)
{
//先把根节点入栈,遍历左子树,边遍历边打印,并且把左子树入栈,当第一轮左子树和根都遍历完了,则开始进入右子树
	Stack* s;
	BTNode* p = pRoot;
	if (pRoot == NULL)		
	{
		return;
	}
	while (!StackEmpty(s)||p)
	{
		while (p)
		{
			printf("%c ", StackTop(s));	//打印栈顶元素
			StackPush(s, p->_data); //根节点入栈
			p = p->_pLeft;	//遍历左子树
		}
		if (!StackEmpty(s))
		{
			p = StackTop(s);	//保存栈顶元素
			StackPop(s);	//取出栈顶元素
			p = p->_pRight;		//遍历右子树
		}
	}
}
//中序遍历
void InOrder(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return;
	}
	else
	{
		InOrder(pRoot->_pLeft);
		printf("%c ", pRoot->_data);
		InOrder(pRoot->_pRight);
	}
}
//非递归实现中序遍历
void InOrderNor(BTNode* pRoot)
{
	BTNode* p = pRoot;
	Stack* s = (Stack*)malloc(sizeof(Stack));
	while (!StackEmpty(s)||p)
	{
		while (p)
		{
			StackPush(s,p->_data);//根节点入栈
			p = p->_pLeft;//遍历左子树
		}
		if (StackEmpty(s))
		{
			p->_data = StackTop(s);//第一次是保存最后一个左子树节点
			StackPop(s);//第一次是取出最后一个左子树节点
			printf("%c ", p->_data);//打印保存的节点
			p = p->_pRight;//遍历右子树
		}
	}
}
//后序遍历
void PostOrder(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return;
	}
	else
	{
		PostOrder(pRoot->_pLeft);
		PostOrder(pRoot->_pRight);
		printf("%c ", pRoot);
	}
}
//非递归实现后序遍历
void PostOrderNor(BTNode* pRoot)
{
	//同样的方法类似于上面的前序和中序遍历
	Stack* s;
	if (pRoot == NULL)
	{
		return;
	}
	BTNode* p = pRoot;
	BTNode* last = NULL;
	while (p)
	{
		StackPush(s, p->_data);
		p = p->_pLeft;
	}
	while (!StackEmpty(s))
	{
		p = StackTop(s);
		StackPop(s);
		if ((p->_pRight == NULL) || (p->_pRight == last))
		{
			printf("%c ", p->_data);
			last = p;
		}
		else
		{
			StackPush(s, p->_data);
			p = p->_pRight;
			while (p)
			{
				StackPush(s, p->_data);
				p = p->_pRight;
			}
		}
	}
}
//层序遍历
void LevelOrder(BTNode* pRoot)
{
//层序遍历需要用到的是队列,所以也需要提前写好有关队列的头文件
	Queue* q;
	BTNode* p = NULL;
	if (pRoot == NULL)
	{
		return;
	}
	QueuePush(q, pRoot->_data);//首先将根节点入队
	while (!QueueEmpty(q))
	{
		p = QueueFront(q);//保存队首元素
		QueuePop(q);//取出队首元素
		printf("%c ", p->_data);//打印保存的元素
		if (p->_pLeft)
		{
			QueuePush(q,p->_pLeft);//将保存的元素的左子树入队
		}
		if (p->_pRight)
		{
			QueuePush(q, p->_pRight);//将保存的元素的左子树入队
		}
	}
}

// 获取二叉树中节点的个数 
int GetNodeCount(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return 0;
	}
	return GetNodeCount(pRoot->_pLeft) + GetNodeCount(pRoot->_pRight) + 1;
	
}

// 求二叉树的高度 
int Height(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return 0;
	}
	int leftheight = Height(pRoot->_pLeft) + 1;//左子树高度,+1是加上根节点
	int rightheight = Height(pRoot->_pRight) + 1;//右子树高度
	return (leftheight > rightheight) ? leftheight : rightheight;
}

// 检测二叉树是否平衡O(N^2) 
int IsBalanceTree(BTNode* pRoot)
{
//平衡树要求左子树高度与右子树高度不好过1
	if (pRoot == NULL)
	{
		return 0;
	}
	int leftheight = Height(pRoot->_pLeft) + 1;
	int rightheight = Height(pRoot->_pRight) + 1;
	if (abs(leftheight - rightheight) > 1)
	{
		return -1;
	}
	else
		return 1;
}
// 检测二叉树是否平衡O(N) 
int IsBalanceTree_P(BTNode* pRoot, int* height)
{
	if (pRoot == NULL)
	{
		return 0;
	}
	int leftheight = 0;
	int rightheight = 0;
	if (IsBalanceTree_P(pRoot->_pLeft, &leftheight) &&
		IsBalanceTree_P(pRoot->_pRight, &rightheight))
	{
		if (abs(leftheight - rightheight) <= 1)
		{
			*height = 1 + (leftheight < rightheight ? rightheight : leftheight);
			return 1;
		}
		else
			return -1;
	}
	else
		return -1;
}

// 获取二叉数中叶子节点的个数 
int GetLeafNodeCount(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return 0;
	}
	if (pRoot->_pLeft == NULL &&pRoot->_pRight == NULL)
	{
		return 1;
	}
	int left = GetLeafNodeCount(pRoot->_pLeft);
	int right = GetLeafNodeCount(pRoot->_pRight);
	return left+right;
}

// 获取二叉树第K层节点的个数 
int GetKLevelNodeCount(BTNode* pRoot, int K)
{
	if (pRoot == NULL)
	{
		return 0;
	}
	if (K == 1)
	{
		return 1;
	}
	int left = GetKLevelNodeCount(pRoot->_pLeft, K - 1);//根节点每往下一层,K-1,当根节点到达第K层时,K-1 = 1;
	int right = GetKLevelNodeCount(pRoot->_pRight, K - 1);
	return left + right;
}

// 获取二叉树中某个节点的双亲节点 
BTNode* GetNodeParent(BTNode* pRoot, BTNode* pNode)
{
	if (pRoot == NULL)
	{
		return NULL;
	}
	if (pRoot->_pLeft == pNode || pRoot->_pRight == pNode)
	{
		return pRoot;
	}
	BTNode* ret = GetNodeParent(pRoot->_pLeft, pNode);//让根节点的左子树与目标子数比较
	if (ret != NULL)//若ret不为NULL索命找到了双亲结点,就不需要继续往下找了
	{
		return ret;
	}
	ret = GetNodeParent(pRoot->_pRight, pNode);//让根节点的左子树与目标子数比较
	return ret;
}

// 求二叉树的镜像 
void Mirror(BTNode* pRoot)
{
	if (pRoot == NULL)
	{
		return;
	}
	if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)
	{
		return;
	}
	BTNode* mirror = pRoot->_pLeft;
	pRoot->_pLeft = pRoot->_pRight;
	pRoot->_pRight = mirror;
	if (pRoot->_pLeft)
	{
		Mirror(pRoot->_pLeft);
	}
	if (pRoot->_pRight)
	{
		Mirror(pRoot->_pRight);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值