36 C语言二叉树插入、删除、查找、遍历详解

进一步理解平衡二叉树(插入) 前言 前面我们了解了二叉搜索树,也进行了对二叉搜索树的简单实现。但是二叉搜索树存在一个缺陷。 当要在搜索树里插入或者删除数据时,都需要进行查找。查找体现了搜索树的效率。当插入二叉树搜索树的集合是一个有序序列时,二叉搜索树会形成一个单支树。变成一个链状结构,这样查找的效率大大降低,最坏情况下,查找的效率为O(N)。并没有很好的体现出搜索树的性质。 而平衡二叉树就很好的体现出了搜索树的性质,查找的效率是树的高度次,最坏的情况下查找的效率是O(logN)。 ... 阅读详情

36.1 前言

数据结构对于一个嵌软工程师来说,用到的最多的有数组、链表(单向、双向、循环链表等),对于其他的数据结构除非在开发存储模块,否则很难会用到其它的数据结构。但为了防范于未然,将来可能需要用到,因此我特地花了一些时间想要把那些软件工程师天天挂嘴边的二叉树啥的学习了一遍,并手写代码。写此篇博客,一是为了总结学习二叉树中获得的零散知识;二是希望把自己学习过程及理解分享,便于大家参考更快的学习二叉树这个东西!

36.2 概念

36.2.1 四种数据结构

在入手学习二叉树前,我一个舍友(已入坑机器学习)在学习数据结构的时候经常讲什么树啊、图啊、线性表啊,一大堆东西,对于没接触的我听了又听,还是不太懂;对于这些数据结构,新手入门时又一定难度,需要反复思考学习才能很好掌握,甚至使用。而现在的我可能是为了一口气(就想试试水到底是不是像别人说的那样难),前端时间终于抽出空想砍掉那复杂多变的二叉树(越学习,发现树这东西内容真多啊)。

数据结构分类为物理结构和逻辑结构,逻辑结构分为线性结构、几何结构、树形结构和图形结构四大结构。

常四种基本的数据结构:

(1)集合结构:结构中的元素关系为“属于同一个集合”。

集合这种数据结构应用还是挺广泛的吧,用于分类、区别,如现在热门的机器学习中,学习数据的不同类别,模型训练出来后,对输入的进行分类划分到一个集合里面等等。

2)线性结构:数据元素之间存在一对一的关系。

线性表属于数据结构中的逻辑结构中的线性结构,常用的线性结构有:线性表、栈、队列、数组等,

线性表特点:(1)一个有限序列;(2)第一个元素只有后继,最后一个元素只有前继;(3)只能依次访问(?);

(3)树型结构:元素之间存在一对多的关系。

树这种数据结构属于数据结构中的逻辑结构,即元素与元素之间的逻辑是怎样的。对于书这种数据结构,我现在知道的就是在数据存储方面(数据查找)有很大的实用吧。因此,此篇我们学习的二叉树的实际用处可能是在未来编写组织数据存储的时候会用到,其它一般开发情况下用线性表就可满足了吧。

(4)图形结构:数据元素之间存在多对多的关系,也称为网状结构,其每个结点的前驱结点数和后续结点数可以任意多个,元素关系也是任意的,主要研究形状和图形数据元素之间的关系,它主要谈论几何形体在计算机内部的表示以及期间进行运算的基本方法。(对此我就了解这么多,感觉图是挺诡异和难啊)。

36.2.2 树

前面说了那么多废话,现在开始继续废话介绍一下树及二叉树的基本知识。

(1)树:树其实就是不包含回路的连通无向图

特点:1、树中任意两节点,有且仅有一个唯一的一条路径联通;2、一棵树有n个节点,那么他必有n-1条边;3、在一棵树中加一条边将会构成一个回路;

(2)二叉树:树的一种特殊化,其每个节点最多有两个后继;

(3)完全二叉树:在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树(Complete Binary Tree);

(4)满二叉树:树中每个节点都有两个后继,所有叶节点深度相同;

特点:1、深度为h的二叉树,其有2^h -1个节点。

(5)二叉排序树(BST/搜索树):排序树中节点满足:左孩子小于根节点,右孩子大于根节点;

(6)平衡二叉树(AVL):它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且所有节点左右两个子树都是一棵平衡二叉树;

注(重要):平衡二叉树是对二叉排序树的改进,我们知道排序二叉树的生成,跟插入值和插入顺序有很紧密的关系,排序二叉树生成的最坏情况那就是,该树只有一个分支(即变成一条单链表),这以致于失去了二叉树的优势,而平衡二叉树则是在排序二叉树生成过程中对树的结构进行调整,以便于生成对数据组织最优化的数结构。

上面简单介绍了一下树的几种,下面则介绍一下关于树的基本术语吧。

(1)深度:表示根节点到x节点的路径长度,故根节点深度为0;

(2)高度:高度是从下往上数的,往上路径数即为高度数,叶子节点高度为0;

(3)度:节点的子树个数,即为节点的度;

(4)森林:n颗互不相交的树构成的集合就是森林;

(5)兄弟节点:就是有共同父节点的节点是兄弟节点;

(6)层次:根节点在第一层,其它每向下一个孩子层次+1;

36.2.3 二叉树性质

性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1);
性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1);
性质3:包含n个结点的二叉树的高度至少为log2 (n+1);
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1;

性质5:如果二叉排序树是平衡的查找效率为O(log2 n),反之O(n),故查找性能在O(log2 n)-O(n);

性质6:完全二叉树总节点n,n为偶数时,叶子节点数为:(n)/2;n奇数时,叶子节点(n+1)/2;

性质7:完全二叉树总节点n,树的父节点为(n)/2;

性质8:完全二叉树总节点n,n为偶数时,节点为2的父节点数为(n)/2;n为奇数时,(n)/2 -1;

性质9:完全二叉树总节点n,该完全二叉树深度为log2 (n+1)(向上取整);

 

36.3 二叉排序树插入、遍历、查找、删除

在进行二叉树的插入、删除等讲解之前,我们先说一下节点的定义。由二叉树认识可知,二叉树节点有一个值代表着节点的大小;每个节点各有可能存在的两个子节点(或称为后继),所以节点中就有三个元素了,但节点如果想要知道自己的父节点是谁呢,是谁把它作为子节点的呢?这时就引出了第四个元素(用以保存指向父节点的地址)。本篇是基于c语言的二叉树实现,故以c的风格将节点定义如下:

/*节点*/  
typedef struct inode{  
int nValue;  
struct inode *left;  
struct inode *right;  
struct inode *parent;  
}stTreeNode;

36.3.1 二叉树插入

二叉树的生成需要一定规则,对于普通的排序二叉树生成,其规则为:父节点大于左孩子节点,且小于右孩子节点。如下图示,2<35<44,其中数据的插入顺序为35、2、44或35、44、2;

插入代码如下:

/************************************************ 
* 插入节点 
* 定义:插入规则为:左<根<右 
************************************************/  
int tree_insertNode(stTreeNode **ppRoot,stTreeNode *pNewInode)  
{  
    stTreeNode *pinodeTmp = NULL;  
      
    if(pNewInode ==NULL){  
        printf("%s input paras error\n",__FUNCTION__);  
        return -1;  
    }  
  
    /*如果是空树*/  
    if(*ppRoot==NULL)  
    {  
        *ppRoot = pNewInode;  
        pNewInode->parent = *ppRoot;  
    }  
    else  
    {  
        /*树根开始*/  
        pinodeTmp = *ppRoot;  
          
        while (pinodeTmp !=NULL)  
        {  
            /*插入节点小于当前节点*/  
            if(pNewInode->nValue< pinodeTmp->nValue)  
            {  
                if(pinodeTmp->left == NULL) //左孩子节点为空,插入  
                {  
                    pinodeTmp->left = pNewInode;  
                    pNewInode->parent = pinodeTmp;  
                    break;  
                }  
                else  
                {  
                    pinodeTmp = pinodeTmp->left;  
                }  
            }  
            /*插入节点大于当前节点*/  
            else  
            {  
                /*左孩子节点为空,插入*/  
                if(pinodeTmp->right == NULL)  
                {  
                    pinodeTmp->right = pNewInode;  
                    pNewInode->parent = pinodeTmp;  
                    break;  
                }  
                else  
                {  
                    /*左孩子节点非空,继续向左孩子节点找*/  
                    pinodeTmp = pinodeTmp->right;  
                }  
            }  
        }  
    }  
  
    return 0;  
} 

36.3.2 二叉树查找

在二叉树查找中又分为三种:(1)二叉树节点查找;(2)查找二叉树最小节点;(3)查找二叉树最大节点。

二叉树查找原理:因为二叉排序树特性,父节点大于左孩子节点且小于右孩子节点,因此,如果要查找的值小于当前节点的值,那么则往左查找,直到左孩子为空时结束;同理,大于当前节点时,往右查找,直到右孩子为空时结束;

下面为查找节点的代码:

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

	if(nValue == pRoot->nValue)
		return pRoot;
	else if(nValue < pRoot->nValue)
		return tree_searchInode(pRoot->left,nValue);
	else if(nValue > pRoot->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;
}

而查找树最小节点则是从根节点开始,一直往左孩子节点,直到为空结束,代码如下:

/************************************************
* 查找小节点
* 定义:查找规则,往左
************************************************/
stTreeNode* tree_findMin(stTreeNode *pRoot)
{
	if(pRoot){
		/*往左找到最底,最底的最小*/
		for(;pRoot->left!=NULL;pRoot=pRoot->left);
		return pRoot;
	}
	return NULL;
}

36.3.3 二叉树遍历

对于二叉树的遍历有三种:

(1)前序遍历(中左右);

(2)中序遍历(左中右);

(3)后序遍历(左右中)。

上述三种遍历这样记比较好:就是前中后,都是相对于父节点排序来说的。比如前序遍历,父节点先,再左孩子节点,再右孩子节点。其中,中序遍历结果是从小到大,即按一定的小大顺序输出的,如下列二叉树,对于先序遍历的逻辑结构为:

以下为三种遍历方式,遍历路径图示:

先序遍历代码(递归方式):

/************************************************
* 前序遍历
* 定义:先遍历根节点,然后左,再右
************************************************/
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;
}

36.3.4 二叉树深度计算

对于树深度定义,就是根节点到某一叶子节点经过路径最多的数称为树的深度。那么该如何代码实现去计算呢?那就是从根节点开始,向左右两边一直往下找,每往下一步累计一次,最后判断根节点的左右子树哪个步数最大,最大的为数的深度;

树深度计算代码实现(递归方式):

/************************************************
* 获取树的深度
* 定义:深度即为数的层数
* 一棵树若只有一个节点那么其深度为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;
}

36.3.5 二叉树叶子节点计算

根据树叶子节点的定义,如果一个节点无左右孩子节点,那么这个节点称为叶子节点;因此计算方式也是从根节点开始,向左右孩子节点进行遍历,如果某个节点无左右孩子节点,那么累计1。

计算叶子节点实现代码(递归):

/************************************************
* 获取叶子节点数
* 定义:叶子无左右子节点的节点
************************************************/
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;
}

36.3.5 二叉树节点删除

二叉树节点删除是二叉树编程中最为操作繁杂的一环,因为作为一颗有序树,删掉一个节点之后依然是要保持有序的状态,因此删除一个节点不是简单的将节点从树中剔除,另外删除树节点有以下三种情况:

(1)删除的节点是叶子节点;

(2)删除的节点有且只有一个孩子节点;

(3)删除的节点有两个孩子节点;

       因此上述问题导致二叉树删除变得复杂了一些,下面图文并茂解说二叉排序树节点的删除。

第一删除叶子节点:

如上图示,在先序遍历下树的逻辑结构是从小到大有序排列的,如果删除上述叶子节点,直接删除,其结果不会导致从小到大顺序的改变,其依然是有序的。因此,删除叶子节点是最简单的。

第二删除含有一个孩子节点的节点:

如上图示,如果要删除10这个节点,删除之后逻辑存储顺序依然要保持有序状态,删除10节点后,必须把21移到10原本这个位置,然后把32节点左孩子置空,这样才能保持有序树仍然为有序树。那么删除10节点之后,就必须找到自己右孩子的左边的孩子中的最小节点,然后将该最小节点替换到删除节点10的位置,并将32节点左孩子置空。如果32节点没有左孩子,那么直接删除10节点,并将2节点挂到8节点的右孩子节点即可。

第三删除含有两个子孩子的节点:

如上图示,如果要删除6节点,那么就需要找出自己右孩子节点8的左孩子节点7,并将7节点放置6节点位置,并将8节点的左孩子置空;如果删除8节点,同样需要查看自己右节点10是否有左孩子节点,如果有则将最左的左孩子节点替换8节点;

二叉树节点删除代码实现:

/************************************************
* 删除节点
************************************************/
void tree_deleteInode(stTreeNode *pRoot,int nDeleteValue)
{	
	stTreeNode *pRetNode = NULL;
	stTreeNode *pNode = pRoot;
	stTreeNode *parent = pRoot;
	int child = 0;  //0表示左子树,1表示右子树;

	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(parent->left == pNode)
				{
					parent->left = NULL;
					tree_destoryInode(pNode);
				}
				else if(parent->right == pNode)
				{
					parent->left = NULL;
					tree_destoryInode(pNode);
				}
				pNode = NULL;
			}
			else if(pNode->left == NULL)/*左节点空,表示右节点不为空*/
			{
				if(parent->left == pNode)
				{
					parent->left = pNode->right;
					tree_destoryInode(pNode);
				}
				else if(parent->right == pNode)
				{
					parent->right = pNode->right;
					tree_destoryInode(pNode);
				}
				pNode = NULL;
			}
			else if(pNode->right == NULL)/*右节点空,表示左节点不为空*/
			{
				if(parent->left == pNode)
				{
					parent->left = pNode->left;
					tree_destoryInode(pNode);
				}
				else if(parent->right == pNode)
				{
					parent->right = pNode->left;
					tree_destoryInode(pNode);
				}
				pNode = NULL;

			}
			else  //删除有双节点的节点
			{
				/*找到最小的节点*/
				pRetNode = tree_findMin(pNode->right);
				/*替换内容*/
				pNode->nValue = pRetNode->nValue;
				if(pRetNode->right!=NULL)
					pRetNode->parent->left = pRetNode->right;
				else
					pRetNode->parent->left = NULL;
				tree_destoryInode(pRetNode);
				pRetNode = NULL;
			}
		}
		else if(nDeleteValue < pNode->nValue)/*往左*/
		{
			parent = pNode;
			pNode = pNode->left;
		}
		else if(nDeleteValue > pNode->nValue)/*往右*/
		{
			parent = pNode;
			pNode = pNode->right;
		}

	}

	return;
}

36.4 二叉树代码实现

二叉树插入、查找、遍历、删除完成代码:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

/*节点*/
typedef struct inode{
	int nValue;
	struct inode *left;
	struct inode *right;
	struct inode *parent;
}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(nValue < pRoot->nValue)
		return tree_searchInode(pRoot->left,nValue);
	else if(nValue > pRoot->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;
}

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


/************************************************
* 删除节点
************************************************/
void tree_deleteInode(stTreeNode *pRoot,int nDeleteValue)
{	
	stTreeNode *pRetNode = NULL;
	stTreeNode *pNode = pRoot;
	stTreeNode *parent = pRoot;
	int child = 0;  //0表示左子树,1表示右子树;

	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(parent->left == pNode)
				{
					parent->left = NULL;
					tree_destoryInode(pNode);
				}
				else if(parent->right == pNode)
				{
					parent->left = NULL;
					tree_destoryInode(pNode);
				}
				pNode = NULL;
			}
			else if(pNode->left == NULL)/*左节点空,表示右节点不为空*/
			{
				if(parent->left == pNode)
				{
					parent->left = pNode->right;
					tree_destoryInode(pNode);
				}
				else if(parent->right == pNode)
				{
					parent->right = pNode->right;
					tree_destoryInode(pNode);
				}
				pNode = NULL;
			}
			else if(pNode->right == NULL)/*右节点空,表示左节点不为空*/
			{
				if(parent->left == pNode)
				{
					parent->left = pNode->left;
					tree_destoryInode(pNode);
				}
				else if(parent->right == pNode)
				{
					parent->right = pNode->left;
					tree_destoryInode(pNode);
				}
				pNode = NULL;

			}
			else  //删除有双节点的节点
			{
				/*找到最小的节点*/
				pRetNode = tree_findMin(pNode->right);
				/*替换内容*/
				pNode->nValue = pRetNode->nValue;
				if(pRetNode->right!=NULL)
					pRetNode->parent->left = pRetNode->right;
				else
					pRetNode->parent->left = NULL;
				tree_destoryInode(pRetNode);
				pRetNode = NULL;
			}
		}
		else if(nDeleteValue < pNode->nValue)/*往左*/
		{
			parent = pNode;
			pNode = pNode->left;
		}
		else if(nDeleteValue > pNode->nValue)/*往右*/
		{
			parent = pNode;
			pNode = pNode->right;
		}

	}

	return;
}


/************************************************
* 插入节点
* 定义:插入规则为:左<根<右
************************************************/
int tree_insertNode(stTreeNode **ppRoot,stTreeNode *pNewInode)
{
	stTreeNode *pinodeTmp = NULL;
	
	if(pNewInode ==NULL){
		printf("%s input paras error\n",__FUNCTION__);
		return -1;
	}

	/*如果是空树*/
	if(*ppRoot==NULL)
	{
		*ppRoot = pNewInode;
		pNewInode->parent = *ppRoot;
	}
	else
	{
		/*树根开始*/
		pinodeTmp = *ppRoot;
		
		while (pinodeTmp !=NULL)
		{
			/*插入节点小于当前节点*/
			if(pNewInode->nValue< pinodeTmp->nValue)
			{
				if(pinodeTmp->left == NULL) //左孩子节点为空,插入
				{
					pinodeTmp->left = pNewInode;
					pNewInode->parent = pinodeTmp;
					break;
				}
				else
				{
					pinodeTmp = pinodeTmp->left;
				}
			}
			/*插入节点大于当前节点*/
			else
			{
				/*左孩子节点为空,插入*/
				if(pinodeTmp->right == NULL)
				{
					pinodeTmp->right = pNewInode;
					pNewInode->parent = pinodeTmp;
					break;
				}
				else
				{
					/*左孩子节点非空,继续向左孩子节点找*/
					pinodeTmp = pinodeTmp->right;
				}
			}
		}
	}

	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);
		tree_destory(pleft);
		tree_destory(pright);
	}
	return;
}


int main()
{
	int ni = 0;
	int nRet = -1;
	int nSqe[]={35,2,1,6,8,10,6,3,44,32,65,7,21};
	
	stTreeNode *pTreeRoot = NULL;
	stTreeNode *pNewTreeNode = NULL;
	
	for(ni= 0;ni<(sizeof(nSqe)/sizeof(int));ni++)
	{
		pNewTreeNode = tree_CreateInode();
		if(pNewTreeNode==NULL){
			printf("%s tree_CreateInode failed at (%d) line\n",__FUNCTION__,__LINE__);
			break;
		}
		pNewTreeNode->nValue = nSqe[ni];

		nRet = tree_insertNode(&pTreeRoot,pNewTreeNode);
		if(nRet<0){
			printf("%s tree_insertNode failed at (%d) line\n",__FUNCTION__,__LINE__);
			break;
		}
	}
	
	printf("------------------------------------------------------\n");
	tree_preScanLoop(pTreeRoot);	
	printf("------------------tree_midScanLoop2--------------------\n");
	tree_midScanLoop(pTreeRoot);
	printf("------------------------------------------------------\n");
	tree_PostScanLoop(pTreeRoot);
	printf("------------------------------------------------------\n");
	nRet = tree_getDeeps(pTreeRoot);
	printf("tree_getDeeps=%d\n",nRet);
	nRet = tree_getLeafCnts(pTreeRoot);
	printf("tree_getLeafCnts=%d\n",nRet);


	if(tree_searchInode(pTreeRoot,8)!=NULL)
		printf("tree_searchInode find\n");
	else
		printf("tree_searchInode not find\n");

	tree_deleteInode(pTreeRoot,8);
	printf("------------------tree_midScanLoop2--------------------\n");
	tree_midScanLoop(pTreeRoot);
	
	if(tree_searchInode(pTreeRoot,8)!=NULL)
		printf("tree_searchInode find\n");
	else
		printf("tree_searchInode not find\n");

	tree_destory(pTreeRoot);
	return 0;
}

Over!!第一次写一篇blog用时这么长的,用了6/7个小时吧

 

 

 

UIPath 获取网页元素做遍历点击的实现 阅读详情

相关推荐

PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现

PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现 BRDF简介 再介绍BRDF之前我们要引入渲染方程这个东西: 其中L表示辐射率,其公式为: 它表示了一个拥有辐射强度Φ的光源在单位面积A,单位立体角ω上的辐射出的总能量。 辐射率是辐射度量学上表示一个区域平面上光线总量的物理量,它受到入射光线与平面法线间的夹角θ的余弦值cosθ的影响:当直接...

小龙虾的博客 1万+

二叉搜索树(查找、插入、删除的讲解实现+图文并茂)

本文讲解并模拟实现二叉搜索树,详解它的查找、插入、删除及实现,并对二叉搜索树的应用场景进行描述,最后进行了性能分析!

bang___bang_的博客 1万+

保姆级教程:用BMS的HVIL检测电路,手把手教你排查高压互锁故障(附PWM/直流源方案对比)

本文详细解析了BMS中的HVIL检测电路,通过对比直流源与PWM两种方案,提供了一套系统化的高压互锁故障诊断流程。从电路原理到实战技巧,帮助维修人员精准定位故障根源,提升电动汽车高压系统的安全性和可靠性。

weixin_29300241的博客 390

二叉树的插入与删除

排序规则:定义一个vector数组,将数组中的第一个值作为根节点,此后将后面的值与根节点的值比较,如果小于根节点的值,则放在根节点的左边,如果大于根节点的值,放在根的右边。以此类推,根节点的左子树(右子树)又是一棵二叉树。此外,在二叉树排序中不允许出现两个相同的值。当二叉树完成之后,可以用中序遍历的方式将它从小到达排序。 定义结构体BstNode和BSTree分别定义了它们的属性和根节点,这里会增加一个双亲指针,它可以让我们的子节点指向它的父节点,但是这样也会降低它的存储密度。即原来12个字节中可以存储

wu___hao的博客 5436

别再为CAD和GIS数据对不上而头疼了!一份完整的ArcGIS for AutoCAD坐标系定义与数据套合指南

本文详细介绍了如何通过ArcGIS for AutoCAD插件解决CAD与GIS数据坐标系不匹配的问题,实现数据的精准套合。文章从坐标系冲突分析、插件核心功能解析到五步标准化工作流,提供了完整的解决方案,帮助工程师和设计师提升跨平台协作效率。

weixin_42531886的博客 216

Rstan下载安装攻略

准备把Rtools删掉,重新下载(胡乱猜测是安装顺序的问题),但是卸载时显示Rtools被占用,但我已经把该关的都关了。这一问题的出现,直接让我卸载掉了Rstudio,又重新安装了一个,不过这次安装在另一个磁盘,因为原来的内存不多了。或者参考知乎,使用清华镜像网址进行下载:(知乎链接)https://zhuanlan.zhihu.com/p/346947595(清华下载链接)https://mirrors.tuna.tsinghua.edu.cn/CRAN/bin/windows/Rtools/

m0_64643017的博客 3099

数据结构——平衡二叉树(AVL树)之插入

文章目录前言一.定义二.基本操作1.查找,删除2.插入(如何调整)如何调整代码实现插入 前言 首先我们来思考一下一个普通二叉树保存数据,如果想查找一个数据,由于普通二叉树保存数据是随机的,要找到数据的时间复杂度为O(n)。后面为了方便 ,我们又学习二叉搜索树,它的定义是将比根节点小的数放左边,比根节点大的数放右边,并且每一课子树都是二叉搜索树这样使得数据在树上存储有一定 的规律,在一定情况下查找起来很方便。但是,二叉搜索数当给出数据的顺序不同时,二叉搜索树也会不同。比如如果给出的序列为{1,2,3,4,5}

weixin_57023347的博客 8503

[VT虚拟化驱动]利用EPT实现无痕HOOK

本章利用讲EPT无痕HOOK

吾无法无天的博客 1万+

【数据结构】树与二叉树(十六):二叉树的基础操作:插入结点(算法Insert)

本文介绍了二叉树的基础操作——插入结点(左插入、右插入),包括ADL语言、时间复杂度分析及C语言实现

天地玄黄 魑魅魍魉 风花雪月 商角徵羽 1832

LVGL 拨盘菜单(Dial Menu)

h。

摸鱼专家 191

基于Simulink使用激光雷达数据跟踪车辆仿真(附源码)

这些模型将长方体的尺寸定义为状态转换期间的常量,并且它们的估计值在滤波器的校正阶段随时间演变。下面的动画显示了在滤波器的预测阶段将具有不同概率的恒定速度和恒定转弯率模型混合在一起的效果。此示例显示了如何使用带有 IMM 过滤器的 JPDA 跟踪器来跟踪使用激光雷达传感器的对象。图像和激光雷达数据读取器从 MAT 文件中读取记录的数据,并分别输出参考图像和点云中点的位置。跟踪器块选中“启用所有轨道输出”和“启用可检测的跟踪 ID 输入”复选框,以从跟踪器输出所有轨道,并将其检测概率作为其状态的函数进行计算。

珞瑜的博客 2565

二叉查找树(查找、插入、删除)——C语言

二叉查找树 二叉查找树(BST:Binary Search Tree)是一种特殊的二叉树,它改善了二叉树节点查找的效率。二叉查找树有以下性质: (1)若左子树不空,则左子树上所有节点的值均小于它的根节点的值 (2)若右子树不空,则右子树上所有节点的值均大于它的根节点的值 (3)左、右子树也分别为二叉排序树 (4)没有键值相等的节点 二叉查找树节点的定义: 1 ty...

weixin_30437481的博客 528

OpenClaw 股票自动化交易实践指南:从原理到实战的完整技术路径

量化交易策略的开发是一个系统工程,需要综合运用金融知识、编程技能和数据分析能力。OpenClaw通过引入提示词工程技术,大大降低了策略开发的技术门槛,使得更多投资者能够参与到量化交易中来。但技术只是工具,真正决定策略成败的是对市场的深刻理解和严格的风险管理。本章系统介绍了OpenClaw环境下的策略开发方法,从提示词驱动到传统代码实现,从参数优化到回测验证,涵盖了策略开发的完整流程。提示词驱动方式适合快速验证交易想法,传统代码方式适合实现复杂的定制逻辑,两种方式可以协同使用,发挥各自的优势。

lovely_yoshino的博客 5989

二叉树的构造和遍历

1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct _NODE//节点结构 5 { 6 struct _NODE* leftChild; 7 int value; 8 struct _NODE* rightChild; 9 ...

a278256076的博客 205

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

二叉查找树的基本操作(建立,插入,删除,遍历)

#include <stdio.h> #include <stdlib.h> struct TreeNode; typedef struct TreeNode *Position; typedef struct TreeNode *SearchTree; typedef int ElementType; struct TreeNode { ElementType Element; S

tengyuan93的博客 689

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

二叉排序树中节点的查找/插入/删除

1 #include <stdio.h> 2 #include<stdlib.h> 3 #include <conio.h> 4 #define ARRAYLEN 11 5 int source [] = {55,94,6,65,11,38,40,39,91,67,66}; 6 typedef struct bst { ...

weixin_34406796的博客 227

数据结构算法C语言实现(二十)--- 6.3.1遍历二叉树

  一.简述   二叉树的遍历主要是先序、中序、后序及对应的递归和非递归算法,共3x2=6种,其中后序非递归在实现上稍复杂一些。二叉树的遍历是理解和学习递归及体会栈的工作原理的绝佳工具!   此外,非递归所用的栈及相关操作是第三章实现的,但数据类型做了更改。   二.头文件 1 //3_1.h 2 /** 3 author:zhaoyu 4 email:zh...

diancha6978的博客 254

数据结构实验题目(C语言)

数据结构的两个实验题目,供大家参考

EBYB的博客 4980

数据结构常用(C语言)

数据结构必会算法(C语言版),建议收藏随时复习!

zhuwenyua的博客 1308

LeetCode 刷题笔记(C语言版 9月)

LeetCode 刷题笔记(C语言版)

一条菜鸟鱼的博客 4062

C语言二叉查找(排序树)的建立以及遍历

1 #include"stdio.h" 2 #include"malloc.h" 3 typedef int elemtype; 4 typedef struct btnode 5 { 6 elemtype data;/*关键字域*/ 7 struct btnode *lchild,*rchild; 8 }btnode,*bitre...

weixin_30266885的博客 348

c语言提高学习笔记——03-c提高10day_数据结构

在学习c语言提高-数据结构总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。 03-c提高10day_数据结构 目录:一、队列(Queue)1、队列基本概念2、队列的链式存储练习1:队列的链式存储二、树和二叉树1、树的基本概念2、二叉树基本概念3、二叉树的遍历练习1:二叉树递归遍历(先序遍历)练习2:二叉树的高度和叶子节点数目练习3:二叉树的拷贝和释...

Alliswell_WP 327

(C语言)BinarySrearchTree二叉搜索树 --- 标准插入(递归,非递归)、遍历(前,中,后序)、查找(递归,非递归)、根插入递归(左旋,右旋)、最小最大值、删除节点

1 #include &lt;stdio.h&gt; 2 #include &lt;stdlib.h&gt; 3 4 struct node{ 5 int data; 6 struct node *left; 7 struct node *right; 8 }; 9 10 struct node *creat...

trb331617的博客 375

C语言实现有序二叉树(1)

在cpp中使用的C语言 头文件 1 /* 有序二叉树 BsTree */ 2 #ifndef _BT_H 3 #define _BT_H 4 /*节点*/ 5 typedef struct BsTreeNode 6 { 7 int data;/* 数据 */ 8 struct BsTreeNode* le...

weixin_30332705的博客 148

(C语言)二叉树层次遍历(数据结构十六)

1.数据类型定义在代码中为了清楚的表示一些错误和函数运行状态,我们预先定义一些变量来表示这些状态。在head.h头文件中有如下定义: 登录后复制 //定义数据结构中要用到的一些变量和类型 #ifndef HEAD_H #define HEAD_H #include <stdio.h> #include &...

weixin_40626815的博客 87

C语言之二叉树

规定:根节点的值大于左节点但小于右节点的值,所以二叉树的值插入是唯一的,最后形成的树只跟根节点有关 定义节点: struct tree_node { TypeElem elem; struct tree_node *fa; struct tree_node *left; struct tree_node *right;}; 1 #incl...

weixin_30375247的博客 97

(C语言) AVL树 - 自平衡二叉树:插入、删除

数组a[9] = {4,2,6,1,3,5,7,16,15};说明:1、层序遍历AVL树,括号内为每个节点的高度值          2、第二行为删除节点“5”之后的AVL树Reference:  https://blog.youkuaiyun.com/silence2015/article/details/50966748                   https://blog.youkuaiyun.com/u0...

trb331617的博客 783
上一篇: Linux ping本网段所有Ip
下一篇: 35 Linux 485串口编程及接收数据错乱解决
Chasing_Chasing
博客等级 码龄10年 233粉丝 87原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值