数据结构~14.二叉树的层次遍历与案例分析

二叉树的层次遍历与案例分析

本文是上一篇文章的后续,详情点击该链接~

层次遍历

       层次遍历就像图中一样,从上到下,从左到右的顺序遍历。而要实现这个遍历的话,我们可以建立一个循环队列。先将二叉树的头结点入队列。然后出队列访问这个结点,如果它有左子树,就把左子树的根节点入队。如果它有右子树,就把右子树的根节点入队。然后出队,访问。一直往复至空为止。

在这里插入图片描述

代码实现

void levelOrder(Tree *tree) {
	int front, rear;
	//定义一个循环队列,用来记录将要访问的层次上的结点
	Tree* queue[MAXSIZE];
	front = rear = 0;
	Tree* p;
	if (tree != NULL) {
		rear = (rear + 1) % MAXSIZE;
		//根结点入队
		queue[rear] = tree;
		while (front != rear) {
			front = (front + 1) % MAXSIZE;
			//队头结点出队
			p = queue[front];
			printf("%c ",p->data);
			//如果左子树不空,左子树根节点入队
			if (p->left != NULL) {
				rear = (rear + 1) % MAXSIZE;
				queue[rear] = p->left;
			}
			//如果右子树不空,则右子树的根节点入队
			if (p->right != NULL) {
				rear = (rear + 1) % MAXSIZE;
				queue[rear] = p->right;
			}
		}
	}
	printf("\n");
}

构建图中的二叉树

Tree* tree = (Tree*)malloc(sizeof(Tree));
	tree = getTree('A');
	tree->left = getTree('B');
	tree->right = getTree('C');
	tree->left->left = getTree('D');
	tree->left->right = getTree('E');
	tree->left->left->left = getTree('H');
	tree->left->left->right = getTree('I');
	tree->left->right->left = getTree('J');
	tree->left->right->right = getTree('K');
	tree->right->left = getTree('F');
	tree->right->right = getTree('G');

前序遍历和层次遍历进行对比

在这里插入图片描述


我们来做一个案例

需求:假设二叉树采用二叉链表存储结构存储,设计一个算法,求出该二叉树的宽度

       对于这个需求,我们可以先分别求出每层的结点数,然后我们再从中选出最大的那个。但是如果要达到这个目的,我们首先得要明白两件事

       第一件事呢,就是,对于那种非空的树,树根所在的层为第一层,并且从刚才的层次遍历算法中我们可以发现,有一个由当前结点找到向左右孩子结点的操作。这就意味着如果知道当前结点的层号,就可以推出其左右孩子的层号,也就是当前结点层号+1。进而就可以求出所有结点的层号

       第二件事就是在层次遍历中,用到了一个循环队列。这个队列的入队和出队用到了rear = (rear + 1) % MAXSIZE; 和 front = (front + 1) % MAXSIZE; 两个操作。假如说这个队列的数组容量足够大,可以容纳树里面的所有结点。那么,这个时候在整个遍历操作中队头和队尾指针就不会出现折回数组起始位置的情况。那么刚才那两行代码可以换成++front和++rear。出队也只是把队头后移了一位,但是并没有把数据删除。队头元素也不会被新入队的元素覆盖

代码实现

新增一个结构体
typedef struct {
	Tree* pl;				//指针结点
	int no;					//结点所在层次号
}TreeNo;
需求实现
int maxNode(Tree *brn) {
	//定义循环非空队列
	TreeNo queue[MAXSIZE];
	int front, rear;
	int treeno, i, j, n, max;
	front = rear = 0;		//将队列置空
	Tree* p;
	if (brn != NULL) {
		++rear;
		//树根入队
		queue[rear].pl = brn;
		//树根所在的层次号设置为1,这个是已知条件
		queue[rear].no = 1;
		while (front != rear) {
			++front;
			p = queue[front].pl;
			//treeno用来存取当前结点的层次号
			treeno = queue[front].no;
			if (p->left != NULL) {
				++rear;
				queue[rear].pl = p->left;
				//根据当前结点的层次好推知其孩子结点的层次号
				queue[rear].no = treeno + 1;
			}
			if (p->right != NULL) {
				++rear;
				queue[rear].pl = p->right;
				queue[rear].no = treeno + 1;
			}
		}//结束循环的时候,treeno已经保存了这棵树的最大层数
		max = 0;
		for (i = 1; i <= treeno; i++) {
			n = 0;
			for (j = 1; j <= rear; j++) {
				if (queue[j].no == i) {
					++n;
				}
				if (max < n) {
					max = n;
				}
			}
		}
		return max;
	}
	//空树直接返回 0
	else {
		return 0;
	}
}

在这里插入图片描述

       由图可知,二叉树的宽度的确为4。所以这种算法是正确的。


最后奉上完整代码

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
typedef struct BinaryNode {
	char data;		//数据域
	struct BinaryNode* left;	//指针域 左孩子
	struct BinaryNode* right;	//指针域 右孩子
}Tree;
typedef struct {
	Tree* pl;				//指针结点
	int no;					//结点所在层次号
}TreeNo;
//赋值
Tree* getTree(char data) {
	Tree* tree = (Tree*)malloc(sizeof(Tree));
	tree->data = data;
	tree->left = NULL;
	tree->right = NULL;
	return tree;
}
//前序遍历
void preOrder(Tree* tree) {
	if (tree != NULL) {
		printf("%c ",tree->data);
		preOrder(tree->left);
		preOrder(tree->right);
	}
}
//层次遍历
void levelOrder(Tree* tree) {
	int front, rear;
	//定义一个循环队列,用来记录将要访问的层次上的结点
	Tree* queue[MAXSIZE];
	front = rear = 0;
	Tree* p;
	if (tree != NULL) {
		rear = (rear + 1) % MAXSIZE;
		//根结点入队
		queue[rear] = tree;
		while (front != rear) {
			front = (front + 1) % MAXSIZE;
			//队头结点出队
			p = queue[front];
			printf("%c ", p->data);
			//如果左子树不空,左子树根节点入队
			if (p->left != NULL) {
				rear = (rear + 1) % MAXSIZE;
				queue[rear] = p->left;
			}
			//如果右子树不空,则右子树的根节点入队
			if (p->right != NULL) {
				rear = (rear + 1) % MAXSIZE;
				queue[rear] = p->right;
			}
		}
	}
	printf("\n");
}

int maxNode(Tree* brn) {
	//定义循环非空队列
	TreeNo queue[MAXSIZE];
	int front, rear;
	int treeno, i, j, n, max;
	front = rear = 0;		//将队列置空
	Tree* p;
	if (brn != NULL) {
		++rear;
		//树根入队
		queue[rear].pl = brn;
		//树根所在的层次号设置为1,这个是已知条件
		queue[rear].no = 1;
		while (front != rear) {
			++front;
			p = queue[front].pl;
			//treeno用来存取当前结点的层次号
			treeno = queue[front].no;
			if (p->left != NULL) {
				++rear;
				queue[rear].pl = p->left;
				//根据当前结点的层次好推知其孩子结点的层次号
				queue[rear].no = treeno + 1;
			}
			if (p->right != NULL) {
				++rear;
				queue[rear].pl = p->right;
				queue[rear].no = treeno + 1;
			}
		}//结束循环的时候,treeno已经保存了这棵树的最大层数
		max = 0;
		for (i = 1; i <= treeno; i++) {
			n = 0;
			for (j = 1; j <= rear; j++) {
				if (queue[j].no == i) {
					++n;
				}
				if (max < n) {
					max = n;
				}
			}
		}
		return max;
	}
	//空树直接返回 0
	else {
		return 0;
	}
}

int main(int argc, char* argv[]) {
	//搭建图中的二叉树
	Tree* tree = (Tree*)malloc(sizeof(Tree));
	tree = getTree('A');
	tree->left = getTree('B');
	tree->right = getTree('C');
	tree->left->left = getTree('D');
	tree->left->right = getTree('E');
	tree->left->left->left = getTree('H');
	tree->left->left->right = getTree('I');
	tree->left->right->left = getTree('J');
	tree->left->right->right = getTree('K');
	tree->right->left = getTree('F');
	tree->right->right = getTree('G');

	//前序遍历和层次遍历对比
	//前序遍历
	printf("前序遍历: ");  preOrder(tree);	printf("\n\n");
	//层次遍历
	printf("层次遍历: "); levelOrder(tree);
	//求宽度
	int max = maxNode(tree);
	printf("\n二叉树tree的宽度是: %d ", max);
	getchar();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41424688/article/details/107951117