【数据结构】二叉树遍历

二叉树遍历

本篇针对链表类型的二叉树的遍历,数组类型的二叉树遍历就等于遍历数组

  • 二叉树遍历笔记:二叉树遍历· 凛音Rinne/学习笔记 - 码云 - 开源中国 (gitee.com)
  • 二叉树遍历工程文件:二叉树遍历 · 凛音Rinne/工程源码 - 码云 - 开源中国 (gitee.com)

文章目录

  • 二叉树遍历
    • 一、遍历规则
      • 1. 前序遍历(Preorder Traversal)
      • 2. 中序遍历(Inorder Traversal)
      • 3. 后序遍历(Postorder Traversal)
    • 二、普通二叉树遍历
      • 1. 二叉树节点定义
      • 2. 二叉树初始化
      • 3. 测试前序中序后序遍历
      • 4. 二叉树节点个数
      • 5. 二叉树叶子节点个数
      • 6. 二叉树第k层节点个数
      • 7. 二叉树深度/高度
      • 8. 二叉树查找值为x的节点
    • 三、层序遍历
      • 1. 创建队列
      • 2. 遍历
      • 3. 层序遍历的变形——判断是否为完全二叉树


一、遍历规则

之前是以孩子兄弟的表示方法

这是是左右孩子的表示方法

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

【数据结构】二叉树遍历_第1张图片


1. 前序遍历(Preorder Traversal)

  • 亦称先序遍历,访问根结点的操作发生在遍历其左右子树之前

遍历顺序:根节点-左子节点-右子节点

// 二叉树前序遍历 
void PreOrder(BTNode* root)
{
     
	if (root == NULL) {
     
		printf("NULL");
		return;
	}

	printf("%c ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}


2. 中序遍历(Inorder Traversal)

  • 访问根结点的操作发生在遍历其左右子树之中(间)

遍历顺序:左子节点-根节点-右子节点

// 二叉树中序遍历 
void InOrder(BTNode* root)
{
     
	if (root == NULL) {
     
		printf("NULL");
		return;
	}

	InOrder(root->left);
    printf("%c ", root->data);
	InOrder(root->right);
}


3. 后序遍历(Postorder Traversal)

  • 访问根结点的操作发生在遍历其左右子树之后

遍历顺序:左子节点-右子节点-根节点

// 二叉树后序遍历 
void PostOrder(BTNode* root)
{
     
	if (root == NULL) {
     
		printf("NULL");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
    printf("%c ", root->data);
}


二、普通二叉树遍历

1. 二叉树节点定义

链式结构且左孩子右孩子

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

2. 二叉树初始化

弄一个简单的

【数据结构】二叉树遍历_第2张图片

BTNode* BuyNode(BTDataType x)
{
     
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
     
		printf("malloc fail\n");
		exit(-1);
	}

	node->data = x;
	node->left = node->right = NULL;
	return node;
}

BTNode* CreatBinaryTree()
{
     
	BTNode* nodeA = BuyNode('A');
	BTNode* nodeB = BuyNode('B');
	BTNode* nodeC = BuyNode('C');
	BTNode* nodeD = BuyNode('D');
	BTNode* nodeE = BuyNode('E');
	BTNode* nodeF = BuyNode('F');
	BTNode* nodeG = BuyNode('G');


	nodeA->left = nodeB;
	nodeA->right = nodeC;
	nodeB->left = nodeD;
	nodeB->right = nodeE;
	nodeC->left = nodeF;
	nodeC->right = nodeG;

	return nodeA;
}


3. 测试前序中序后序遍历

int main()
{
     
	BTNode* tree = CreatBinaryTree();

	//前序
	PreOrder(tree);
	printf("\n");

	//中序
	InOrder(tree);
	printf("\n");

	//后序
	PostOrder(tree);
	printf("\n");


	return 0;
}

测试结果

【数据结构】二叉树遍历_第3张图片


4. 二叉树节点个数

递归实现,遍历每一个节点,不是空节点就+1

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
     
	/*if (root == NULL)
	{
		return 0;
	}

	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;*/
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

5. 二叉树叶子节点个数

也是递归,但注意条件

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
     
	if (root == NULL)
	{
     
		return 0;
	}
    //判断是否是叶子节点
	if (root->left == NULL && root->right == NULL)
	{
     
		return 1;
	}

	return  BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}


6. 二叉树第k层节点个数

【数据结构】二叉树遍历_第4张图片

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

	if (k == 1)
	{
     
		return 1;
	}

	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);

}

7. 二叉树深度/高度

从下面的NULL开始往上递归,到节点的倒数第一层+1

【数据结构】二叉树遍历_第5张图片

// 二叉树深度/高度
int BinaryTreeDepth(BTNode* root)
{
     
	if (root == NULL)
	{
     
		return 0;
	}

	int leftdepth = BinaryTreeDepth(root->left);
	int rightdepth = BinaryTreeDepth(root->right);
	return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
}


8. 二叉树查找值为x的节点

这里的逻辑我觉得很好理解,但写出来,写清楚不太好想

【数据结构】二叉树遍历_第6张图片

  • 首先一遇到NULL,return NULL;

  • 如果是root == x,那么return root;

重点是怎么递归下去

像这种不是空函数,有返回值的递归的话,每次需要保存之前的值

如果没找到,return NULL;

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
     
	if (root == NULL)
	{
     
		return NULL;
	}

	if (root->data == x)
	{
     
		return root;
	}
    //左孩子
	BTNode* leftRet = BinaryTreeFind(root->left, x);
	if (leftRet)//如果是NULL的话就进不去
	{
     
		return leftRet;
	}

    //z
	BTNode* rightRet = BinaryTreeFind(root->right, x);
	if (rightRet)//如果是NULL的话就进不去
	{
     
		return rightRet;
	}

	return NULL;
}

三、层序遍历

首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历

  • 先入根

【数据结构】二叉树遍历_第7张图片

  • 当前节点出来,把孩子带进去,这样上一层节点出的时候,带入下一

【数据结构】二叉树遍历_第8张图片

【数据结构】二叉树遍历_第9张图片

  • 队列为空,说明最后一层没有节点了,遍历结束

【数据结构】二叉树遍历_第10张图片


因为用队列实现

所以我们需要队列的一些接口

【数据结构】二叉树遍历_第11张图片

如果遇到无法打开源文件这种情况

在这里插入图片描述

右击你的项目找到 属性

【数据结构】二叉树遍历_第12张图片

接着按照 配置属性 - C/C++ - 附加包含目录

【数据结构】二叉树遍历_第13张图片

加添文件所在目录

【数据结构】二叉树遍历_第14张图片

【数据结构】二叉树遍历_第15张图片


1. 创建队列

//层序遍历
void BinaryTreeLevelOder(BTNode* root)
{
     
	if (root == NULL)
	{
     
		return;
	}
	//创建队列
	Queue q;
	QueueInit(&q);
	//先放入根
	QueuePush(&q, root);


	QueueDestroy(&q);
}

这里存在一个问题原来的QueuePush

//进队
void QueuePush(Queue* pq, QDataType x);

这里的QDataType的类型,在队列里面是int

typedef int HPDataType;

现在我们要改成BTNode*

但是如果直接改会出现很多错误

【数据结构】二叉树遍历_第16张图片

这些错误都是因为导致的

typedef BTNode* HPDataType;

因为文件前面没有声明BTNode*这个类型

【数据结构】二叉树遍历_第17张图片

所以需要先声明

【数据结构】二叉树遍历_第18张图片

可以右击转到定义

快速找到原来的定义的地方,然后复制去声明

在这里插入图片描述


2. 遍历

//层序遍历
void BinaryTreeLevelOder(BTNode* root)
{
     
	if (root == NULL)
	{
     
		return;
	}
	//创建队列
	Queue q;
	QueueInit(&q);
	//先放入根
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
     
		//保存后再pop用来打印
		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);
}

测试结果:

【数据结构】二叉树遍历_第19张图片


3. 层序遍历的变形——判断是否为完全二叉树

在遇到NULL时候需要注意

【数据结构】二叉树遍历_第20张图片

【数据结构】二叉树遍历_第21张图片

这里NULL没有进去,如果在push的前面没有进行什么操作的话,判断不出来是不是完全二叉树

【数据结构】二叉树遍历_第22张图片

我们可以发现

  • 如果是完全二叉树,NULL之后不会有数据

  • 如果是不完全二叉树,NULL之后还会有数据

所以我们在第一个NULL停止层序遍历,再继续进行第二个层序遍历,如果后面都是NULL,就是完全二叉树

这里我们可以把 NULL当作一个队列中元素,进行插入

【数据结构】二叉树遍历_第23张图片

因为队列节点结构体是,思路要清晰不能因为root = NULL而去想,如果root->left == NULL,会不会报错

typedef BTNode* QDataType;

typedef struct QueueNode
{
     
	struct QueueNode* next;
	QDataType data;//如果这里为NULL,那也没有关系,我们只需要它来作为一个判断
}QueueNode;

完全二叉树和不完全二叉树第一次遇到NULL时候队列情况比较

【数据结构】二叉树遍历_第24张图片

//判断是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
     
	//创建队列
	Queue q;
	QueueInit(&q);
	//先放入根
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
     
		//保存后再pop
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		//根出去,再入左右孩子
		if (front == NULL)//非空再入队列
		{
     
			break;
		}
		else
		{
     
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}

	}

	while (!QueueEmpty(&q))
	{
     
		//保存后再pop
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front != NULL)
		{
     
			return false;
		}

	}
	
	QueueDestroy(&q);
	return true;
}

测试:

【数据结构】二叉树遍历_第25张图片


你可能感兴趣的:(The,data,structure,数据结构,链表,算法,二叉树)