二叉树的一些学习

         最近为了找实习,在学数据结构,都没怎么刷面试题了,记录一下这段时间以来学过的代码吧,有需要的可以参考一下。有什么不对的地方,望指正,也可以在评论区一起互动起来!!!

代码是参考看了一些厉害的博主才完善的,要是有侵权的,请联系我及时删除!!

        二叉树(Binary Tree)是一种常见的树状数据结构,它由节点组成,每个节点最多有两个子节点:一个左子节点和一个右子节点。这些子节点也可以是空的。

以下是二叉树的一些关键特点:

  1. 根节点:二叉树的顶部节点称为根节点。
  2. 父节点、子节点:每个节点都可以有零个、一个或两个子节点。与节点相连的较高节点称为父节点,而与其相连的较低节点称为子节点。
  3. 叶子节点:没有子节点的节点称为叶子节点。
  4. 深度:从根节点到某个节点的唯一路径上的边数。
  5. 高度:从该节点到叶子节点的最长路径上的边数。
  6. 层次:树中的每一层都有一组节点,根节点在第0层,其子节点在第1层,依此类推。
  7. 遍历:二叉树的遍历是按照某种顺序访问树中所有节点的过程。常见的遍历方式包括前序遍历、中序遍历和后序遍历。

二叉树有许多类型,例如平衡二叉树、二叉搜索树、完全二叉树等,每种类型都有不同的特点和应用场景。

这里我主要以二叉搜索树为主。

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
 
/*节点*/
typedef struct inode{
	int nValue;
	struct inode *left;
	struct inode *right;
}stTreeNode;
 
 
/************************************************
* 前序遍历
* 定义:先遍历根节点,然后左,再右
************************************************/
void tree_preScanLoop(stTreeNode *pRoot)
{
	if(pRoot == NULL)
		return;
	
	printf("nValue = %d\n",pRoot->nValue);
	tree_preScanLoop(pRoot->left);
	tree_preScanLoop(pRoot->right);
	return;
}
 
/************************************************
* 中序遍历
* 定义:先遍历左节点,然后根,再右
************************************************/
void tree_midScanLoop(stTreeNode *pRoot)
{
	if(pRoot == NULL)
		return;
 
	tree_midScanLoop(pRoot->left);
	printf("nValue=%d\n",pRoot->nValue);
	tree_midScanLoop(pRoot->right);
 
	return;
}
 
/************************************************
* 后序遍历
* 定义:先遍历左节点,然后右,再根
************************************************/
void tree_PostScanLoop(stTreeNode *pRoot)
{
	if(pRoot == NULL)
		return;
 
	tree_PostScanLoop(pRoot->left);
	tree_PostScanLoop(pRoot->right);
	printf("nValue=%d\n",pRoot->nValue);
 
	return;
}
 
/************************************************
* 创建节点
* 定义:先遍历左节点,然后右,再根
************************************************/
stTreeNode *tree_CreateInode(void)
{
	stTreeNode *pinode = NULL;
	pinode = (stTreeNode*)malloc(sizeof(stTreeNode));
	if(pinode!=NULL)
	{
		memset(pinode,0,sizeof(stTreeNode));
		return pinode;
	}
	
	return NULL;
}
 
 
/************************************************
* 销毁单个节点
* 定义:
************************************************/
void tree_destoryInode(stTreeNode *pinode)
{
	if(pinode!=NULL)
	{
		free(pinode);
		pinode	= NULL;
	}
}
 
 
/************************************************
* 获取树的深度
* 定义:深度即为数的层数
* 一棵树若只有一个节点那么其深度为0
************************************************/
int tree_getDeeps(stTreeNode *pRoot)
{
	int ndeep = 0;
	int nleftDeep = 0;
	int nrightDeep = 0;
 
	if(pRoot!=NULL)
	{
		nleftDeep = tree_getDeeps(pRoot->left);
		nrightDeep = tree_getDeeps(pRoot->right);
		ndeep = (nleftDeep > nrightDeep)?(nleftDeep+1):(nrightDeep+1);
	}
 
	return ndeep;
}
 
/************************************************
* 获取叶子节点数
* 定义:叶子无左右子节点的节点
************************************************/
int tree_getLeafCnts(stTreeNode *pRoot)
{
	static int nCounts = 0;
 
	if(pRoot!=NULL)
	{
		if(pRoot->left==NULL && pRoot->right == NULL){
			nCounts++;
		}
		tree_getLeafCnts(pRoot->left);
		tree_getLeafCnts(pRoot->right);
	}

	return nCounts;
}
 
 
/************************************************
* 查找节点
* 定义:查找规则,如果比根节点小则在左字数查找
************************************************/
stTreeNode *tree_searchInode(stTreeNode *pRoot,int nValue)
{	
	if(pRoot==NULL)
		return NULL;
	
	if(nValue == pRoot->nValue)
		return pRoot;
	else if(pRoot->nValue > nValue)
		return tree_searchInode(pRoot->left,nValue);
	else if(pRoot->nValue < nValue)
		return tree_searchInode(pRoot->right,nValue);
	else
		return NULL;

}

/************************************************
* 查找大节点
* 定义:查找规则,如果比根节点小则在左字数查找
************************************************/
stTreeNode *tree_findMax(stTreeNode *pRoot)
{
    // 方法一
	// if(pRoot){
	// 	/*往右找到最底,最底的最大*/
	// 	for(;pRoot->right!=NULL;pRoot = pRoot->right);
	// 	return pRoot;
	// }
	// return NULL;
    
    // 方法二
	if(pRoot==NULL){
		return NULL;
	}else if(pRoot->right==NULL){
		return pRoot;
	}else{
		return tree_findMax(pRoot->right);
	}
}
 
/************************************************
* 查找小节点
* 定义:查找规则,如果比根节点小则在左字数查找
************************************************/
stTreeNode* tree_findMin(stTreeNode *pRoot)
{
	// 方法一
	// if(pRoot){
	// 	/*往左找到最底,最底的最小*/
	// 	for(;pRoot->left!=NULL;pRoot=pRoot->left);
	// 	return pRoot;
	// }
	// return NULL;

	// 方法二
	if (pRoot == NULL)
        return NULL;
    if (pRoot->left == NULL)    // 结点没有左子树,即为最左叶子结点
        return pRoot;
    else        // 有左子树,则继续在左子树中查找
        return tree_findMin(pRoot->left);
}
 

// 删除最小结点
stTreeNode* DeleteMin(stTreeNode *BiTree)
{
    if (BiTree->left == NULL){
		stTreeNode *temp = BiTree->right;
        free(BiTree);
        return temp;
	}
    else {
        //这里是多次重复赋值,只有最后一步才是将右子树赋值给父子树的左节点
        BiTree->left = DeleteMin(BiTree->left); 
        return BiTree;
    }
}

// 删除最大结点
stTreeNode* DeleteMax(stTreeNode *BiTree)
{
    if (BiTree->right == NULL){
		stTreeNode *temp = BiTree->left;
        free(BiTree);
        return temp;
	}
    else {
        BiTree->right = DeleteMax(BiTree->right); //这里是多次重复赋值,只有最后一步才是将左子树赋值给父子树的右节点
        return BiTree;
    }
}

// 删除指定的节点的递归实现
stTreeNode *delNode(stTreeNode *pRoot,int nDeleteValue){

    // 先找到要删除的结点在二叉树中的位置
    if (pRoot == NULL) {    // 没有找到元素
        perror("can`t find element\n");
        return NULL;
    }
        
    if (pRoot->nValue < nDeleteValue) {        // 元素值大于结点元素值,则去结点右子树寻找
        pRoot->right = delNode(pRoot->right, nDeleteValue);
        return pRoot;
    }
    else if (pRoot->nValue > nDeleteValue) {    // 元素值小于结点元素值,则去结点左子树寻找
        pRoot->left = delNode(pRoot->left, nDeleteValue);
        return pRoot;
    }
	// 找到结点
    else if(pRoot->nValue == nDeleteValue){
		//找到要删除的节点
		if(pRoot->left == NULL && pRoot->right == NULL)//叶节点
		{
			tree_destoryInode(pRoot);
			return NULL;
		}else if(pRoot->left == NULL){//只有右孩子
			stTreeNode * temp = pRoot->right;
			tree_destoryInode(pRoot);
			return temp;
		}else if(pRoot->right == NULL){//只有左孩子
			stTreeNode* temp = pRoot->left;
			tree_destoryInode(pRoot);
			return temp;
		}else{ //左右都有
			//找左子树的最大节点		
			stTreeNode * tempNode = pRoot->right;
			stTreeNode *minNode = tree_findMin(tempNode);
			minNode->left = pRoot->left;
			tree_destoryInode(pRoot);
			return tempNode;
		}
    }
}


/************************************************
* 删除指定的节点(循环实现)
************************************************/
void tree_deleteInode(stTreeNode *pRoot,int nDeleteValue)
{	
	stTreeNode *pRetNode = NULL;
	stTreeNode *pNode = pRoot;
	stTreeNode *parentNode = pRoot;
 
	if(pNode == NULL) // 没有找到元素
		return;
 
	while(pNode)
	{
		if(pNode->nValue == nDeleteValue)
		{
			if(pNode->left == NULL && pNode->right == NULL)/*删除叶子节点*/
			{
				if(pNode == pRoot) //要是是根节点
				{
					tree_destoryInode(pNode);
				}
				else if(parentNode->left == pNode) //是左边的节点
				{
					parentNode->left = NULL;
					tree_destoryInode(pNode);
				}
				else if(parentNode->right == pNode) //是右边的节点
				{
					parentNode->right = NULL;
					tree_destoryInode(pNode);
				}
			}
			else if(pNode->left == NULL)/*左节点空,表示右节点不为空*/
			{
				if(parentNode->left == pNode)  //是左边的节点
				{
					parentNode->left = pNode->right;
					tree_destoryInode(pNode);
				}
				else if(parentNode->right == pNode)  //是右边的节点
				{
					parentNode->right = pNode->right;
					tree_destoryInode(pNode);
				}
			}
			else if(pNode->right == NULL)/*右节点空,表示左节点不为空*/
			{
				if(parentNode->left == pNode) //是左边的节点
				{
					parentNode->left = pNode->left;
					tree_destoryInode(pNode);
				}
				else if(parentNode->right == pNode)  //是右边的节点
				{
					parentNode->right = pNode->left;
					tree_destoryInode(pNode);
				}
			}
			else  //删除有双节点的节点
			{
				if(parentNode->left == pNode) //是左边的节点
				{
					/*找到最小的节点*/
					pRetNode = tree_findMin(pNode->right);
					/*替换内容*/
					pRetNode->left=pNode->left;
					parentNode->left = pNode->right;
				}
				else if(parentNode->right == pNode) //是右边的节点
				{
					/*找到最小的节点*/
					pRetNode = tree_findMin(pNode->right);
					/*替换内容*/
					pRetNode->left=pNode->left;
					parentNode->right=pNode->right;
				}
				tree_destoryInode(pNode);
			}
		}
		else if(nDeleteValue < pNode->nValue)/*往左*/
		{
			parentNode = pNode;
			pNode = pNode->left;
		}
		else if(nDeleteValue > pNode->nValue)/*往右*/
		{
			parentNode = pNode;
			pNode = pNode->right;
		}
 
	}
 
	return;
}

 
/************************************************
* 插入节点
* 定义:插入规则为:左<根<右
************************************************/
int tree_insertNode(stTreeNode **ppRoot,int nVal)
{
	stTreeNode * pNewInode = tree_CreateInode();
	stTreeNode *pinodeTmp = NULL;
	
	if(pNewInode==NULL){
		printf("%s tree_CreateInode failed at (%d) line\n",__FUNCTION__,__LINE__);
		return -1;
	}
	pNewInode->nValue = nVal;
	
	if(pNewInode ==NULL){
		printf("%s input paras error\n",__FUNCTION__);
		return -1;
	}
 
	/*如果是空树*/
	if(*ppRoot==NULL)
	{
		*ppRoot = pNewInode;
	}
	else
	{
		/*树根开始*/
		pinodeTmp = *ppRoot;
		
		while (pinodeTmp !=NULL)
		{
			/*插入节点小于当前节点*/
			if(pNewInode->nValue < pinodeTmp->nValue)
			{
				if(pinodeTmp->left == NULL) //左孩子节点为空,插入
				{
					pinodeTmp->left = pNewInode;
					break;
				}
				else
				{
					pinodeTmp = pinodeTmp->left;
				}
			}
			/*插入节点大于当前节点*/
			else if(pNewInode->nValue > pinodeTmp->nValue)
			{
				/*右孩子节点为空,插入*/
				if(pinodeTmp->right == NULL)
				{
					pinodeTmp->right = pNewInode;
					break;
				}
				else
				{
					/*右孩子节点非空,继续向右孩子节点找*/
					pinodeTmp = pinodeTmp->right;
				}
			}else{
				break;  //遇到相等节点时要break,不然就会卡死 获取上面的 > < 换成 <= >=
			}
		}
	}

 
	return 0;
}


/************************************************
*  删除全部节点(递归实现) 
************************************************/
void tree_destory(stTreeNode *pRoot)
{
	stTreeNode *pleft = NULL;	
	stTreeNode *pright = NULL;
 
	if(pRoot==NULL){
		return;
	}
	else
	{
		pleft	= pRoot->left;
		pright	= pRoot->right;
		tree_destoryInode(pRoot); //要放上面,不要放下面(因为是只需要释放一次pRoot节点即可)
		tree_destory(pleft);
		tree_destory(pright);
	}
	return;
}
 

// 插入一个结点(< 或 > 不会插入重复节点,<= 或 >= 则会插入重复节点)
stTreeNode * InsertNode(stTreeNode **pRoot, int n)
{
    if (*pRoot == NULL) {    //树为空,或已到达叶子结点
        // 为新结点分配内存
        stTreeNode* newNode = (stTreeNode*)malloc(sizeof(stTreeNode));
        if (newNode == NULL) {
            perror("malloc failed!\n");
            // exit(-1);
			return NULL;
        }
        newNode->nValue = n;
        newNode->left = NULL;
        newNode->right = NULL;
        *pRoot = newNode;
    }
    else if ((*pRoot)->nValue <= n) {  // 插入值大于当前结点的值,则将元素插入到结点的右子树
        (*pRoot)->right = InsertNode(&(*pRoot)->right, n);
    }
    else if ((*pRoot)->nValue >= n) {  // 插入值小于当前结点的值,则将元素插入到结点的左子树
        (*pRoot)->left = InsertNode(&(*pRoot)->left, n);
    }
    
    return *pRoot; 
}


int main()
{
	int ni = 0;
	int nRet = -1;
	int nSqe[]={35,2,6,8,10,3,44,1,32,65,7,21};
	int delNum=0;
	
	stTreeNode *pTreeRoot = NULL;
	stTreeNode *pNewTreeNode = NULL;
	stTreeNode *tempNode = NULL;
	
	for(ni= 0;ni<(sizeof(nSqe)/sizeof(int));ni++)
	{

		nRet = tree_insertNode(&pTreeRoot,nSqe[ni]);
		if(nRet<0){
			printf("%s tree_insertNode failed at (%d) line\n",__FUNCTION__,__LINE__);
			break;
		}
	}

	printf("delNum is : ");
	
	scanf("%d",&delNum);
	

	tree_preScanLoop(pTreeRoot);	
	printf("------------------------------------------------------\n");
	delNode(pTreeRoot,delNum);
	
	tree_preScanLoop(pTreeRoot);	
	printf("------------------------------------------------------\n");
 
	tree_destory(pTreeRoot);
	return 0;
}

 

        删除节点的那块得多花时间,二叉树删除节点有四种情况,其实就是要注意删除节点后,其子节点的位置调整。

        我自己写的流程图就放这了,有不懂的可以来看看。(需要结合上面的代码去分析,不然就会一脸懵逼)

        这里有很多地方我都使用了 递归 循环遍历 两种方式实现,把它们搞懂,是学会二叉树必不可少的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值