二叉树-----补充

本期只讲解部分接口,相当于对之前内容的一些补充,需要一定基础,建议先看之前的文章后再看本期

(1条消息) 树和二叉树-CSDN博客

目录

 

树的相关概念

二叉树的结构

二叉树的前、中、后序遍历

二叉树的节点个数

二叉树的深度

二叉树第k层节点个数

二叉树查找值为x的节点

层序遍历

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

二叉树销毁


树的相关概念

节点的度 :一个节点含有的子树的个数称为该节点的度; 如上图: A 的为 6
叶节点或终端节点 :度为 0 的节点称为叶节点; 如上图: B C H I... 等节点为叶节点
非终端节点或分支节点 :度不为 0 的节点; 如上图: D E F G... 等节点为分支节点
双亲节点或父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图: A B 的父节点
孩子节点或子节点 :一个节点含有的子树的根节点称为该节点的子节点; 如上图: B A 的孩子节点
兄弟节点 :具有相同父节点的节点互称为兄弟节点; 如上图: B C 是兄弟节点
树的度 :一棵树中,最大的节点的度称为树的度; 如上图:树的度为 6
节点的层次 :从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;
树的高度或深度 :树中节点的最大层次; 如上图:树的高度为 4
堂兄弟节点 :双亲在同一层的节点互为堂兄弟;如上图: H I 互为兄弟节点
节点的祖先 :从根到该节点所经分支上的所有节点;如上图: A 是所有节点的祖先
子孙 :以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是 A 的子孙
森林 :由 m m>0 )棵互不相交的树的集合称为森林;

 这些内容以及其他的相关知识都在之前的文章里介绍过了,这里再简单提一下,之后的相关概念不再赘述,我们直接进入正题

二叉树的结构

typedef int BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

二叉树可以被分为3个部分,根、左子树、右子树,所以他的结构也是如此,我们需要左右子树的指针

二叉树-----补充_第1张图片

二叉树的每一个节点,我们都应该分为3部分来看待,即根、左子树和右子树,并且二叉树的叶子节点我们也应该这样看待,只是叶子节点的左右子树为空 

二叉树的前、中、后序遍历

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root) {
	assert(root);
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {
	assert(root);
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%d ", root->data);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
	assert(root);
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%d ", root->data);
}

前序遍历也叫先根遍历,中序遍历叫做中根遍历,后序叫后根,意思是访问根节点的顺序,比如前序遍历,我们要先访问根节点,即我们这里打印出根节点的元素,然后再去访问左子树,然后去访问右子树,中序是先访问左子树,接着是访问根节点,最后访问右子树,后序为左子树、右子树、根的顺序,这里我们将空也打印出来,因为空也属于二叉树的一部分

二叉树的节点个数

// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
	assert(root);
	if (root == NULL) {
		return 0;
	}
	int left = BinaryTreeSize(root->left);
	int right = BinaryTreeSize(root->right);
	return left + right + 1;
}

我们分别计算左右子树的节点树,遇到空返回0,接着我们将左右子树的节点个数加起来然后再加1,即加上这个节点自己,我们使用递归的思想即可完成,这段代码也可以使用一个三目运算符来统一起来

二叉树的深度

int maxDepth(struct TreeNode* root){
    if(root==NULL){
        return 0;
    }
    int leftDepth=maxDepth(root->left);
    int rightDepth=maxDepth(root->right);
    return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
}

与计算节点个数类似,不过这段代码不推荐用三目运算符来实现,因为我们在第一次比较两个子树高度后,?后面的语句+1时还会再次进行计算,即重复运行代码,效率太低, 所以我们先将结果保存起来,然后比较

二叉树第k层节点个数

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
	assert(k > 0);
	assert(root);
	if (root == NULL) {
		return 0;
	}
	if (k == 1) {
		return 1;
	}
	int leftnum = BinaryTreeLevelKSize(root->left, k - 1);
	int rightnum = BinaryTreeLevelKSize(root->right, k - 1);
	return leftnum + rightnum;
}

第k层的节点个数等于左子树的第k-1层节点数+右子树的第k-1层节点数

这里的判断条件除了为空时要判断,k==1时也要判断并返回,因为为1时的那层就是我们需要的那层(根节点为第1层)

二叉树查找值为x的节点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
	assert(root);
	if (root == NULL) {
		return NULL;
	}
	if (root->data == x) {
		return root;
	}
	BTNode* left  = BinaryTreeFind(root->left,x);
	BTNode* right = BinaryTreeFind(root->right, x);
	if (left != NULL) {
		return left;
	}
	else {
		return right;
	}
}

我们寻找值为x的节点,同样的使用递归来找,找到了就层层返回,找不到返回NULL,最后两句代码还可以更细节一点,比如加上如果右不为空返回右,最后再返回空也是可以的,但差别不大

层序遍历

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
	assert(root);
	Queue queue;
	QueueInit(&queue);
	if(root!=NULL)
		QueuePush(&queue,root);
	while (!QueueEmpty(&queue)) {
		
		BTNode* tmp = QueueFront(&queue);
		QueuePop(&queue);
		printf("%d ", tmp->data);
		if (tmp->left) {
			QueuePush(&queue, tmp->left);
		}
		if (tmp->right) {
			QueuePush(&queue, tmp->right);
		}
	}
	QueueDestroy(&queue);
}

层序遍历是需要使用到队列的,写队列的文章我给大家附上链接

(1条消息) 栈和队列-----重制版_KLZUQ的博客-CSDN博客

层序遍历即为一层一层的遍历,思路为我们使用队列先将根节点入队列,接着将根节点的出队列,出的同时将他的左右子树都入队列,以此类推,我们就可以进行层序遍历了,最后不要忘记释放队列的空间 

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

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {
	Queue queue;
	if (root != NULL)
		QueuePush(&queue, root);
	while (!QueueEmpty(&queue)) {
		BTNode* tmp = QueueFront(&queue);
		QueuePop(&queue);
		if (tmp == NULL) {
			break;
		}
		else {
			QueuePush(&queue, root->left);
			QueuePush(&queue, root->right);
		}
	}
	//判断是否为完全二叉树,此时队列里应该都为空
	while (!QueueEmpty(&queue)) {
		BTNode* tmp = QueueFront(&queue);
		QueuePop(&queue);
		if (tmp) {//遇到非空节点,说明不是完全二叉树
			QueueDestroy(&queue);
			return false;
		}
	}
	QueueDestroy(&queue);
	return true;
}

我们继续使用队列来帮助我们完成该函数,如果一棵树为完全二叉树,我们使用层序遍历,将他所有节点都入队列,在我们第一次遇到空后,后续节点不会出现非空节点,否则就不是完全二叉树了

二叉树销毁

// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {
	assert(root);
	if (root == NULL) {
		return;
	}
	BinaryTreeDestory((*root)->left);
	BinaryTreeDestory((*root)->right);
	free(root);
}

二叉树的销毁我们最好选择后序遍历的顺序,最后销毁根节点,不然我们还需要对左右子树先进行存储再销毁,会变得麻烦起来

以上即为本期全部内容,希望大家可以有所收获

如有错误,还请指正

你可能感兴趣的:(数据结构,c语言,算法,二叉树,c++)