【C++数据结构】二叉树的层序遍历

目录

前言:

一、问题描述 

二、创建思路及遍历方法

三、代码实现

1、二叉树的建立

(1).结点建立

 (2).新节结点建立

 (3).新增结点

(4).移除树

2.层序遍历

(1)读取数据

(2)遍历

3.主程序

四、完整代码


 

前言:

        线性结构中,每个结点只有一个后继。在有些情况下,一个结点可能有多个后继。树(tree)可以描述这类非线性结构,二叉树(Binary Tree)递归定义如下:二叉树要么为空,要么由根节点(root)左子树(left subrree)右子树(right subtree)组成,而左子树和右子树分别是一颗二叉树。注意,在计算机中,树一般是“倒置”的,即根在上,叶子在下。本文将介绍二叉树的创建及层序遍历。


提示:以下是本篇文章正文内容,代码注释较为详细,可供参考

一、问题描述 

        输入一颗二叉树,你的任务是按从上到下、从左到右的顺序输出各个结点的值。每个结点都按照从根节点到它的移动序列给出(L表示左,R表示右)。在输入中,每个结点的左括号和右括号之间没有空格,相邻结点之间用一个空格隔开。每棵树的输用一对空括号“()”结束(这对括号本身不代表一个结点),如图所示:

 

        注意,如果从根到某个叶节点的路径上有的结点没有在输入中给出,或者给出超过一次,应当输出-1。结点个数不超过256。


样例输入:

(11,LL)  (7,LLL)  (8,R)  (5,)  (4,L)  (13,RL)  (2,LLR)  (1,RRR)  (4,RR)  ()

(3,L)  (4,R)  ()

样例输出:

5  4  8  11  13  4  7  2  1

-1


二、创建思路及遍历方法

        受之前小球下落问题的启发,是否可以把树上的结点编号,然后把二叉树储存在数组中呢?很遗憾这样的方法在此是行不通的。题目中已限制结点最多有256个。如果各个结点形成一条链,最后一个结点的编号将是巨大的!

        所以,需要采用动态结构,根据需要建立新的结点,然后将其组织成一颗树。首先,先读入数据,这里采用的是string函数库里的getlinegetline的函数格式:getline(cin,string对象)。getline的作用是读取一整行,直到遇到换行符才停止读取,期间能读取像空格、Tab等的空白符。 之后使用迭代器逐个分析数据,遇到数字则使用readNum函数提取。当识别到 ‘  ,’ 时便开始读取其位置信息直到遇到 ‘  )’ 代表此结点读取结束,然后通过addNode函数创建该结点。这样一来,输入和建树部分便以完成。

        之后根据输出需求,从上往下,从左至右,选择层序遍历。此处使用一个队列来完成这个任务,在头文件中加入queue的头文件,初始时只有一个根节点入列,然后每次取出一个结点,就把它的左右子结点(如果存在)放入队列。

        对于之前示例的二叉树,层序遍历的过程应是:

        队列:5                        (5入列)

        队列:4  8                    (5出列,4、8入列)

        队列:8  11                  (4出列,11入列)

        队列:11  13  4            (8出列,13、4入列)

        队列:7  2  13  4          (11出列,7、2入列)

        队列:2  13  4              (7出列,无子节点无入列)

        队列:13  4                  (2出列,同理无入列)

        队列:4                        (13出列,无入列)

        队列:1                        (4出列,无入列,最后1出列,队列空完成遍历)


        这样遍历二叉树的方法称为宽度优先遍历(Breadth-First Search,BFS)。

        为了处理非正常输入的情况(某个叶节点值没有给出,或者给出超过一次),使用一个bool类型对象show来表示此输出的状态,每次开始时重置为true,若有上述情况则变为false,不使用BFS输出结果,而是输出-1。每次完成一次建树遍历后,便销毁该树,以防对后面再次建树遍历产生影响,清除采用的是递归的方法。

三、代码实现

1、二叉树的建立

(1)结点建立

        和之前线性结构不同,树结点一般有多个指针,二叉树结点为指向左子节点和右子节点的两个指针。

struct Node {
	int val = -1;	// 初始化结点值为-1,用来判断是否有结点值未给
	Node* left = nullptr, * right = nullptr; 
};
Node* root = nullptr;	// 定义根节点

 (2)新节结点建立

        使用new关键字时注意内存的回收,避免内存泄漏。

Node* newNode() { return new Node;}		// 创建新结点

 (3)新增结点

void addNode(int value, string loc) {
	Node* temp = root;		// 创建临时指针用于建立新结点
	for (int i = 0; i < loc.length(); i++) {
		if(loc[i]=='L'){
			if (temp->left == nullptr) {
				temp->left = newNode();		// 如果该结点的左子结点为空则新建
			}
			temp = temp->left;		// 往左子节点移动 
		}
		else if (loc[i] == 'R') {
			if (temp->right == nullptr) {
				temp->right = newNode();	// 如果该结点的右子结点为空则新建
			}
			temp = temp->right;		// 往右子节点移动 
		}
	}
	if (temp->val != -1) {
		show = false;	// 若该结点已经给过值,则返回-1并禁止遍历
	}
	else {
		temp->val = value;	// 将该节点赋值
	}
}

(4) 移除树

void remove(Node* temp) {
	if (temp == nullptr) {
		return;			// 根节点为空则返回
	}
	remove(temp->left);
	remove(temp->right);
	delete temp;
	temp = nullptr;		// 递归遍历清除
}

2.层序遍历

(1)读取数据

double readNum(string::const_iterator& it) {
	string temp;
	while (*it >= '0' && *it <= '9') {
		temp += *it++;
	}
	return stod(temp);		// 将string转化为int读取结点值
}

(2)遍历

void bfs() {
	queue<Node*> temp;	// 创建队列用于广搜
	temp.push(root);	// 根节点入列
	if (temp.front()->val == -1) {
		cout << -1;		// 判断根节点是否有值,若没有则返回-1并停止遍历 
		return;
	}
	while (!temp.empty()) {
		Node* tptr = temp.front();	
		temp.pop();
		cout << tptr->val << " ";
		if (tptr->left != nullptr) temp.push(tptr->left);
		if (tptr->right != nullptr) temp.push(tptr->right);
	}		// 层序遍历
}

3.主程序

int main(){
	string line;
	while (getline(cin, line)) {
		root = new Node;	// 每次创建二叉树前新创建根节点
		show = true;		// 重置判断是否为正常输出
		string temp;		// 临时对象用于存储getline读取的内容
		double value = -1;		// 给结点值默认为-1,以便判断是否有未给的结点值
		for (auto it = line.begin(); it != line.end(); it++) {
			if (*it >= '0' && *it <= '9') {
				value=readNum(it);
			}		// 遇到结点值直接读取
			if (*it == ',' && *(it+1) == ')') {
				root->val = value;
			}		// 判断是否为(n,)根节点,是则将n赋值给根节点
			else if (*it == ',') {
				while (*(it+1) != ')') {
					it++;
					temp.push_back(*it);
				}		// 识别','并从此开始读取后面的位置信息遇到直到')'代表结束
				if (value == -1) {
					cout << -1 << endl;
					show = false;
				}		// 如果位置信息读完了但没有结点值(还是默认值-1)则说明是非正常输出,即为-1
				addNode(value, temp);		// 创建结点
				temp.clear();		// 创建完一个结点后将临时对象清空
				value = -1;			// 将重置默认值
			}
			if (*it == '(' && *(it + 1) == ')') {
				if (show) {
					bfs();
					cout << endl;
				}		// 识别'()'代表结束,判断此时若为正常输出则层序遍历并输出
			}
		}
		remove(root);		// 完成一次二叉树创建遍历后清空
	}
	return 0;
}

4.输出示例 

        此为题目所给测试数据: 

 

四、完整代码

#include <iostream>
#include <string>
#include <queue>
using namespace std;

bool show = true;

struct Node {
	int val = -1;	// 初始化结点值为-1,用来判断是否有结点值未给
	Node* left = nullptr, * right = nullptr; 
};
Node* root = nullptr;	// 定义根节点

Node* newNode() { return new Node;}		// 创建新结点

void addNode(int value, string loc) {
	Node* temp = root;		// 创建临时指针用于建立新结点
	for (int i = 0; i < loc.length(); i++) {
		if(loc[i]=='L'){
			if (temp->left == nullptr) {
				temp->left = newNode();		// 如果该结点的左子结点为空则新建
			}
			temp = temp->left;		// 往左子节点移动 
		}
		else if (loc[i] == 'R') {
			if (temp->right == nullptr) {
				temp->right = newNode();	// 如果该结点的右子结点为空则新建
			}
			temp = temp->right;		// 往右子节点移动 
		}
	}
	if (temp->val != -1) {
		show = false;	// 若该结点已经给过值,则返回-1并禁止遍历
	}
	else {
		temp->val = value;	// 将该节点赋值
	}
}

void bfs() {
	queue<Node*> temp;	// 创建队列用于广搜
	temp.push(root);	// 根节点入列
	if (temp.front()->val == -1) {
		cout << -1;		// 判断根节点是否有值,若没有则返回-1并停止遍历 
		return;
	}
	while (!temp.empty()) {
		Node* tptr = temp.front();	
		temp.pop();
		cout << tptr->val << " ";
		if (tptr->left != nullptr) temp.push(tptr->left);
		if (tptr->right != nullptr) temp.push(tptr->right);
	}		// 层序遍历
}

double readNum(string::const_iterator& it) {
	string temp;
	while (*it >= '0' && *it <= '9') {
		temp += *it++;
	}
	return stod(temp);		// 将string转化为int读取结点值
}

void remove(Node* temp) {
	if (temp == nullptr) {
		return;			// 根节点为空则返回
	}
	remove(temp->left);
	remove(temp->right);
	delete temp;
	temp = nullptr;		// 递归遍历清除
}

int main(){
	string line;
	while (getline(cin, line)) {
		root = new Node;	// 每次创建二叉树前新创建根节点
		show = true;		// 重置判断是否为正常输出
		string temp;		// 临时对象用于存储getline读取的内容
		double value = -1;		// 给结点值默认为-1,以便判断是否有未给的结点值
		for (auto it = line.begin(); it != line.end(); it++) {
			if (*it >= '0' && *it <= '9') {
				value=readNum(it);
			}		// 遇到结点值直接读取
			if (*it == ',' && *(it+1) == ')') {
				root->val = value;
			}		// 判断是否为(n,)根节点,是则将n赋值给根节点
			else if (*it == ',') {
				while (*(it+1) != ')') {
					it++;
					temp.push_back(*it);
				}		// 识别','并从此开始读取后面的位置信息遇到直到')'代表结束
				if (value == -1) {
					cout << -1 << endl;
					show = false;
				}		// 如果位置信息读完了但没有结点值(还是默认值-1)则说明是非正常输出,即为-1
				addNode(value, temp);		// 创建结点
				temp.clear();		// 创建完一个结点后将临时对象清空
				value = -1;			// 将重置默认值
			}
			if (*it == '(' && *(it + 1) == ')') {
				if (show) {
					bfs();
					cout << endl;
				}		// 识别'()'代表结束,判断此时若为正常输出则层序遍历并输出
			}
		}
		remove(root);		// 完成一次二叉树创建遍历后清空
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值