数据结构5:二叉树(1)——基本操作与实现

91 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

基础知识

  • 二叉树
    • 定义:二叉树(Binary tree)是树形结构的一个重要类型。是指树中节点的度不大于2的有序树。
    • 递归定义:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
    • 实现:顺序存储(数组实现)和链式存储(链表实现)
    • 相关术语:节点、节点的度、叶子节点、分支节点、树的度、节点的层次、树的深度、有序树、无序树、森林
    • 应用:数据的索引与查找(二叉排序树)、堆、...
    • 常见的操作:树的创建、树的遍历、查找结点、...

在这里插入图片描述

题目

  • 二叉树的基本操作算法实现
    • (1)利用二叉树字符串“A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))”创建二叉树的二叉链式存储结构;
    • (2)输出该二叉树;
    • (3)输出‘H’节点的左、右孩子结点值;
    • (4)输出该二叉树的结点个数、叶子结点个数、二叉树的度和高度;

思路

  • ① 创建二叉树
  • ② 遍历统计结点数量、树的高度、查找结点

代码

#include <iostream>
#include<ctime>
#include<stack>
#include <cstdlib>
using namespace std;

void printstring(char* ch)
{
	for (int i = 0; i < 37; ++i)
	{
		cout << ch[i] << ' ';
	}
	cout << endl;
}

class treeNode
{
public:
	treeNode() :ch(NULL), lChild(NULL), rChild(NULL) {}
	treeNode(char m_ch, treeNode* m_lChild = NULL, treeNode* m_rChild = NULL) :ch(m_ch), lChild(m_lChild), rChild(m_rChild) {}
	~treeNode() { lChild = NULL; rChild = NULL; }
public:
	treeNode* lChild;
	treeNode* rChild;
	char ch;
};

class treeList
{
public:
	treeList() :root(new treeNode()) {}
	treeList(treeNode* m_root) :root(m_root) {}
	~treeList() { destroyTree(root); delete root; }
	void buildTree(treeNode* node, char* ch, int ch_index);
	void buildTree_update(treeNode* node, char* ch, int index);
	void createTree(treeNode* node, char* ch, int index); 
	void findChild(char ch, treeNode* node);
	void printTree(treeNode* node);
	void destroyTree(treeNode* node);  //销毁二叉树
	int getTreeDepth(treeNode* node);  //二叉树的高度
	int getTreeNodeNum(treeNode* node);  //二叉树结点个数
	int getTreeLevesNum(treeNode* node);  //二叉树叶子数
	int getTreeDegree(treeNode* node);  //二叉树的度(最大出度,不超过2)
public:
	treeNode* root;
};
//	A(B(D, E(H(J, K(L, M(, N))))), C(F, G(, I)))/*
①建立根节点,(index == 0时)赋值
②寻找左孩子:遇到'('时,判断下一个符号是字母还是',':字母:创建左孩子,之后执行③;否则,无左孩子,之后执行④;
③判断当前字母是否有左孩子,有左孩子时,递归调用,否则执行下一步
④寻找右孩子:遇到','时,判断下一个符号是字母还是')':字母:创建右孩子,之后执行⑤;否则,无左孩子,return;
	判断无右孩子的条件:找到左孩子后,下一个符号为')',此时return,解决结点E无',用于识别的问题'
⑤判断当前字母是否有左孩子('即下一个符号是否为'('),有左孩子时,递归调用

注:前面遍历完的符号要设为空或者其他符号,避免递归是与后面的判断条件重复,导致找不到逻辑混乱(多次创建结点,找不到右括号)
*/
void treeList::buildTree_update(treeNode* node, char* ch, int index)
{
	if (ch[index] == NULL) return;
	if (index == 0)
	{
		node->ch = ch[index];
		index++;
	}
	if (ch[index] == '(')  //寻找左孩子
	{
		//cout << index << "  " << ch[index] << endl;
		//cout <<"寻找" << node->ch << "的左孩子!" << endl;
		bool flag = 0;
		while (ch[index] != ','&&ch[index] != ')')
		{
			ch[index] = '\0';  //复制完后要把字母置空,避免递归时发生错误!!!
			index++;
			//cout << index << "  " << ch[index] << endl;
			if (ch[index] >= 'A'&&ch[index] <= 'Z')  //存在左孩子
			{
				node->lChild = new treeNode(ch[index]);
				cout << node->ch << "  创建左孩子  " << ch[index] << endl;
				ch[index] = '\0';  //把字母置空,避免递归时发生错误!!!
				flag = 1;
				index++;
				//cout << index << "  " << ch[index] << endl;
				if (ch[index] == '(') buildTree_update(node->lChild, ch, index);  //注意此条件!!!
				else break;
				//cout << "当前结点" << node->ch << endl;
			}
		}
		if (flag == 0)  //不存在左孩子
		{
			node->lChild = NULL;
			cout << node->ch << "  没有左孩子" << endl;
		}

	}
	if (ch[index] == ',' || ch[index] == ')')  //寻找右孩子   ,增加条件: || ch[index] == ')'
	{
		if (ch[index] == ',')
		{
			ch[index] = '\0';
			index++;
			//cout << index << "  " << ch[index] << endl;
			//cout << "寻找" << node->ch << "的右孩子!" << endl;
			bool flag = 0;
			while (ch[index] != ')')
			{

				//cout << index << "  " << ch[index] << endl;
				if (ch[index] >= 'A'&&ch[index] <= 'Z')  //存在右孩子
				{
					node->rChild = new treeNode(ch[index]);
					cout << node->ch << "  创建右孩子  " << ch[index] << endl;
					ch[index] = '\0';  //字母置空,避免递归时发生错误!!!
					flag = 1;
					index++;
					//cout << index << "  " << ch[index] << endl;
					if (ch[index] == '(')buildTree_update(node->rChild, ch, index);
					else if (ch[index] == ')')
					{
						ch[index] = '\0';
						return;
					}
					index++;
				}
				index++;
			}
			if (flag == 0)  //不存在右孩子
			{
				node->rChild = NULL;
				cout << node->ch << "  没有右孩子" << endl;
			}
		}
		else if (ch[index] == ')')  //增加条件:解决结点E没有右孩子,但没有','用于识别的情况
		{
			ch[index] = '\0';
			node->rChild = NULL;
			cout << node->ch << "  没有右孩子" << endl;
			return;
		}

		if (ch[index] == ')')
		{
			ch[index] = '\0';
			return;
		}
	}
	cout << "!!!" << ch[index] << endl;
	cout << "bug!!!" << endl;
}

void treeList::destroyTree(treeNode* node)
{
	if (node != NULL)
	{
		if (node->lChild != NULL)
		{
			destroyTree(node->lChild);
			cout << "删除" << node->ch << "的左孩子:" << node->lChild->ch << endl;
			delete node->lChild;
		}
		if (node->rChild != NULL)
		{
			destroyTree(node->rChild);
			cout << "删除" << node->ch << "的右孩子:" << node->rChild->ch << endl;
			delete node->rChild;
		}
	}

}

void treeList::findChild(char ch, treeNode* node)
{
	if (node != NULL)
	{
		if (node->lChild != NULL)
		{
			if (node->lChild->ch == ch)
			{
				if (node->lChild->lChild != NULL) cout << ch << "的左孩子:" << node->lChild->lChild->ch << endl;
				else cout << ch << "无左孩子:" << endl;
				if (node->lChild->rChild != NULL) cout << ch << "的右孩子:" << node->lChild->rChild->ch << endl;
				else cout << ch << "无右孩子:" << endl;
				return;
			}
			else
			{
				findChild(ch, node->lChild);
			}
		}
		if (node->rChild != NULL)
		{
			if (node->rChild->ch == ch)
			{
				if (node->rChild->lChild != NULL) cout << ch << "的左孩子:" << node->rChild->lChild->ch << endl;
				else cout << ch << "无左孩子!" << endl;
				if (node->rChild->rChild != NULL) cout << ch << "的右孩子:" << node->rChild->rChild->ch << endl;
				else cout << ch << "无右孩子!" << endl;
				return;
			}
			else
			{
				findChild(ch, node->rChild);
			}
		}
	}
}

void treeList::printTree(treeNode* node)  //中序遍历
{
	if (node != NULL)
	{
		cout << node->ch;
		if (node->lChild != NULL || node->rChild != NULL)
		{
			cout << '(';
			printTree(node->lChild);
			if (node->rChild != NULL) cout << ',';
			printTree(node->rChild);
			cout << ')';
		}
	}
}

int treeList::getTreeDepth(treeNode* node)  //二叉树的高度,初始化为0
{
	if (node == NULL) return 0;
	else
	{
		int lDepht = getTreeDepth(node->lChild);
		int rDepht = getTreeDepth(node->rChild);
		if (lDepht > rDepht) return lDepht + 1;
		else return rDepht + 1;
	}
}
int treeList::getTreeNodeNum(treeNode* node)  //二叉树结点个数,初始化为0
{
	if (node == NULL) return 0;
	else
	{
		return getTreeNodeNum(node->lChild) + getTreeNodeNum(node->rChild) + 1;  //左子树结点数+右子树结点数
	}
}
int treeList::getTreeLevesNum(treeNode* node)  //二叉树叶子数,初始化为0
{
	if (node == NULL) return 0;
	if (node->lChild == NULL && node->rChild == NULL)
	{
		return 1;
	}
	else return getTreeLevesNum(node->lChild) + getTreeLevesNum(node->rChild);

}
int treeList::getTreeDegree(treeNode* node)  //二叉树的度(最大出度,不超过2),初始化为0,达到2即可return
{
	if (node == NULL || (node->lChild == NULL && node->rChild == NULL)) return 0;
	else if ((node->lChild == NULL && node->rChild != NULL) || (node->lChild != NULL && node->rChild == NULL))
	{
		int max1 = getTreeDegree(node->lChild) > 1 ? getTreeDegree(node->lChild) : 1;
		int max2 = getTreeDegree(node->rChild) > 1 ? getTreeDegree(node->rChild) : 1;
		return max1 > max2 ? max1 : max2;
	}
	else if (node->lChild != NULL && node->rChild != NULL) return 2;
}

int main()
{
	char *cc = nullptr;
	const char*string = { "A(B(D,E(H(J,K(L,M(,N))),)),C(F,G(,I)))" };
	cc = new char[strlen(string) + 1];
	for (int i = 0; i < strlen(string); ++i)
	{
		cc[i] = string[i];
	}
	cc[strlen(string)] = '\0';

	int len = strlen(string);

	int iter = 0;
	int letter_num = 0;
	treeList* tree = new treeList();  //创建一棵空树
	while (true)
	{
		int flag = 1;
		cout << "\n选择1:利用二叉树字符串“A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))”创建二叉树的二叉链式存储结构:\n";
		cout << "选择2:输出该二叉树:\n";
		cout << "选择3:输出‘H’节点的左、右孩子结点值:\n";
		cout << "选择4:输出该二叉树的结点个数、叶子结点个数、二叉树的度和高度:\n";
		cout << "选择5:执行各种遍历操作:\n";
		cout << "选择6:构建中序二叉线索树及遍历二叉搜索树:\n";

		cout << "选择0:退出!\n";
		cout << "请输入要执行的操作:";
		cin >> flag;
		if (flag == 1)
		{
			cout << "===========test1===========\n(1)利用二叉树字符串“A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))”创建二叉树的二叉链式存储结构:" << endl;
			tree->buildTree_update(tree->root, cc, 0);
			cout << "二叉树创建成功!" << endl;
		}
		else if (flag == 2)
		{
			cout << "===========test2===========\n(2)输出该二叉树:" << endl;
			tree->printTree(tree->root);
			cout << endl;

		}
		else if (flag == 3)
		{
			cout << "===========test3===========\n(3)输出‘H’节点的左、右孩子结点值:" << endl;
			tree->findChild('H', tree->root);

		}
		else if (flag == 4)
		{
			cout << "===========test4===========\n(4)输出该二叉树的结点个数、叶子结点个数、二叉树的度和高度:" << endl;
			cout << "1.二叉树的结点个数:";
			cout << tree->getTreeNodeNum(tree->root) << endl;
			cout << "2.二叉树的叶子数:";
			cout << tree->getTreeLevesNum(tree->root) << endl;
			cout << "3.二叉树的度:";
			//tree->root->lChild = NULL;  //用于测试计算二叉树的度函数是否有效
			cout << tree->getTreeDegree(tree->root) << endl;
			cout << "4.二叉树的高度:";
			cout << tree->getTreeDepth(tree->root) << endl;

		}
}

结果

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述