Detailed explanation of binary tree (concept + traversal implementation)

 

1. Basic concepts

 

1. Leftmost child node: the leftmost child node among the child nodes of a node. For example, A——B, B——E;
2. Tree height : the highest level of the tree;
3. Path length: There is a unique path between any two vertices in the tree. The number of edges passed by a path is called the path length;
4. Tree diameter: the longest path in the tree, the diameter of the tree in the figure is 6;
5. Sibling nodes : have a common parent node (B, C, D) , the right sibling node of E is F, and the left sibling node of F is E;
6. Cousin node : the parent node (grandfather node, corresponding to the descendant node) of the parent node is the same (K and L);
7 .Uncle node : C is the uncle node of E
8. Degree : The number of subtrees of a node (A-3, E-1)
 

Second, the binary tree

Binary tree is the most commonly used tree structure. Its storage structure and related operations are relatively simple. Heap, AVL tree, red-black tree, etc. all belong to binary tree.

definition:

A binary tree is a tree structure with nodes whose degree does not exceed 2.

Properties: 1. There is at most one node
on the i-th layer of the binary tree (i>=1); 2. In a binary tree of height h, there is at most one node and at least h nodes; 3. If A binary tree has n nodes, and the binary tree has n-1 edges; 4. In a binary tree, if the number of nodes (leaf nodes) with degree 0 is n0, the number of nodes with degree 2 is n2, then n0=n2+1.2^{i-1}
2^{h}-1

Create the following binary tree: (The following codes are based on this picture)

 
        The previous method of creating a binary tree is to call the create_lrpTree function multiple times. The operation is cumbersome. Here is how to use the first root sequence of the extended binary tree to create a binary tree. Extending the binary tree refers to pointing the null pointer domain of the binary tree to a special node, and assigning a special value (such as #) to this special node. In the expanded binary tree, the original node is called the internal node, and the added node is the external node. Obviously, in the expanded binary tree, the number of external nodes is 1 more than the number of internal nodes, and each internal node has two children.

        The first root sequence of the extended binary tree shown in the figure above is: ABD##EG###CF#H###.

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

/*
	左右链指针表示法,这是左右链指针表示二叉树结点的常见表示方法
	这种方法只能自顶向下遍历二叉树,通常用二叉树的根结点表示二叉树。
	由于二叉树的一个结点的左右子树结构与原二叉树具有相似的结构,
	因此对二叉树的操作通常采用递归的方法。
*/
// 孩子表示法
typedef char datatype;
typedef struct lrpNode {
	datatype data;   //数据域
	lrpNode* lc, * rc; //左右链域
	lrpNode():lc(NULL), rc(NULL) {
		data = 0;
	}
}*lrpTree;

// 孩子双亲表示法
// 在结点中添加parent之后不仅可以对二叉树进行自顶向下的操作,而且可以进行自底向上的操作
//typedef struct lrpNode {
//	datatype data;   //数据域
//	lrpNode* lc, * rc,*parent; //左右链域
//	lrpNode() :lc(NULL), rc(NULL),parent(NULL) {
//		data = 0;
//	}
//}*lrpTree;

// 创建一棵二叉树,其根节点的数据值为data,左右孩子分别为lc,rc
// 用该函数创建一棵二叉树,应该从二叉树的最底层开始,自下而上依次创建
lrpTree create_lrpTree(datatype data, lrpTree lc, lrpTree rc) {
	lrpTree tmp = new lrpNode; //为根结点动态分配存储空间
	tmp->data = data;
	tmp->lc = lc;
	tmp->rc = rc;
	return tmp;
}

//根据扩展二叉树的先根序列创建二叉树
lrpTree create_lrpTree(string str, int& idx) {
	lrpTree ret;
	if (str[idx] == '#' || idx == str.size()) {
		idx++;
		return NULL;
	}
	ret = new lrpNode;
	ret->data = str[idx++];
	//通过递归更新ret的位置
	ret->lc = create_lrpTree(str, idx);  //创建ret的左子树,直到#结束
	ret->rc = create_lrpTree(str, idx);	 //创建ret的右子树,直到#结束
	return ret;
}

/*
	二叉树的遍历
	二叉树的遍历是指按某种顺序访问二叉树中的结点,每个结点都被访问且访问一次。
	遍历也是二叉树进行其他运算的基础。
*/

// 先根遍历:即首先先访问根结点,然后先根遍历左子树,最后先跟遍历右子树。
// 顺序:ABDEGCFH
// 递归算法
void preOrder(lrpTree rt) {
	if (rt == NULL)return;
	cout << rt->data; // 访问当前结点
	preOrder(rt->lc); // 先根遍历左子树
	preOrder(rt->rc); // 后根遍历右子树
}

// 利用递归方法实现先根遍历的代码清晰易懂,但当树的高度较大时,递归的层次较高,甚至可能会造成爆栈。
// 为了避免该情况,可以采用非递归方法实现二叉树的先根遍历,非递归用栈来模拟函数递归过程
// 左右每边遍历完后,栈都为空的状态;
// 每一个结点都要考虑右结点,所以每个都会被top,top后即失去了所有价值,需pop掉
// 先根遍历的非递归算法
void preOrder1(lrpTree rt) {
	stack<lrpTree>stk;
	lrpTree t = rt;
	while (t || !stk.empty()) {
		while (t) {  //输出t,并沿t的左分支向下遍历,直到没有
			cout << t->data;
			stk.push(t);
			t = t->lc;
		}
		t = stk.top();
		stk.pop();
		t = t->rc;  //考虑栈顶元素的右分支
	}
}

//中根遍历:首先中根遍历左子树,然后访问根节点,最后中根遍历右子树
//顺序:DBGEAFHC
//递归算法
void midOrder(lrpTree rt) {
	if (rt == NULL)return;
	midOrder(rt->lc);
	cout << rt->data;
	midOrder(rt->rc);
}

//后根遍历:首先后根遍历左子树,然后后根遍历右子树,最后访问根节点
//顺序:DGEBHFCA
//递归算法
void postOrder(lrpTree rt) {
	if (rt == NULL)return;
	postOrder(rt->lc);
	postOrder(rt->rc);
	cout << rt->data;
}
 
//为了检查所构建的二叉树是否正确,可将二叉树的每个结点的数据输出,并输出其两个孩子的数据
void print_lrpTree(lrpTree rt) {
	if (rt == NULL) return;
	cout << rt->data << " ";
	if (rt->lc) cout << "lChind:" << rt->lc->data << " ";
	else cout << "lChind:NULL" << " ";
	if (rt->rc) cout << "rChind:" << rt->rc->data << " "<<endl;
	else cout << "rChind:NULL" << " ";
	print_lrpTree(rt->lc);
	print_lrpTree(rt->rc);
}

int main()
{
	//初始版创建二叉树
	lrpTree t1 = new lrpNode, t2 = new lrpNode, t = new lrpNode;
	//构建左子树
	t1 = create_lrpTree('G',NULL,NULL), t2 = create_lrpTree('E', t1,NULL);
	t1 = create_lrpTree('D',NULL,NULL), t2 = create_lrpTree('B', t1, t2);
	//构建右子树
	t1 = create_lrpTree('H', NULL, NULL), t1 = create_lrpTree('F', NULL, t1);
	t1 = create_lrpTree('C', t1, NULL), t = create_lrpTree('A', t2, t1);

	//扩展二叉树创建先根序列
	string s = "ABD##EG###CF#H###";
	lrpTree prestr = new lrpNode;
	int i = 0;
	prestr = create_lrpTree(s, i);

	//先根遍历
	preOrder(t);
	cout << endl;
	preOrder1(t);
	cout << endl;
	preOrder(prestr);
	cout << endl;
	preOrder1(prestr);
	cout << endl;

	//中根遍历
	midOrder(t);
	cout << endl;

	//后根遍历
	postOrder(prestr);
	cout << endl;

	//print_lrpTree(t);
}

Binary tree bracket notation 

 Convert the binary tree represented by the left and right chain pointers into parenthesized binary tree bracket expressions:

//将左右链指针表示的二叉树转化t为括号的二叉树的括号表达式
string convertToBracket(lrpTree t) {
	if (t == NULL)return "";
	return "(" + convertToBracket(t->lc) + t->data + convertToBracket(t->rc) + ")";
}

Output: (((D)B((G)E))A((F(H))C)) 

Analyzing the binary tree bracket notation, we can draw the following characteristics:


1. Leaf nodes are directly enclosed by a pair of brackets, such as D/G/H


2. If a node has a left subtree, the character at the left end is ')', such as node A/B/C; if there is no left subtree, the character at the left end is '(', such as node F


3. If a node has a right subtree, the character at the right end is '(', such as node A/B/C; if there is no right subtree, the character at the right end is ')', such as node C/E


4. Each left parenthesis represents the end of a subtree, and the multiplicity of nodes enclosed by parentheses is consistent with the number of layers in the binary tree. For example, A is enclosed by one layer of parentheses, which is the first layer; D/E/ F is surrounded by triple brackets, which is the third level.

 

 Note: Figure 3-7 needs to be viewed with the code, first look at the code and then look at the picture

//将左右链指针表示的二叉树转化t为括号的二叉树的括号表达式
string convertToBracket(lrpTree t) {
	if (t == NULL) return "";
	return  "(" + convertToBracket(t->lc) + t->data + convertToBracket(t->rc) + ")";
}

//s = convertToBracket(t);
//	cout << s;

//将二叉树的括号表示形式b转化为左右链指针表示形式,结果作为函数的返回值
lrpTree converToTree(string b) {
	lrpTree rt = new lrpNode, lc = new lrpNode, rc = new lrpNode;
	if (b.size() <= 2) return NULL;
	stack<lrpTree>stk;
	for (int i = 1; i < b.size(); i++) {
		if (b[i] == '(') continue; //跳过左括号
		else if (b[i] == ')') { //右括号,构建第一棵子树,并入队
			rc = stk.top(); stk.pop(); //栈中第一个元素为右子树
			rt = stk.top(); stk.pop(); //栈中第二个元素为根结点
			lc = stk.top(); stk.pop(); //栈中第三个元素为左子树
			rt->lc = lc; rt->rc = rc;
			stk.push(rt); //新的子树入队
		}
		else { //结点
			if (b[i-1] == '(') stk.push(NULL);//结点没有左子树
			rt = new lrpNode;
			rt->data = b[i], stk.push(rt);
			if (b[i + 1] == ')') stk.push(NULL);//结点没有右子树
		}
	}
	return stk.top();
}

//s = "(((D)B((G)E))A((F(H))C))";
//	t1 = converToTree(s);
//	preOrder(t1);

 According to the parenthesis notation of the binary tree, find the back root sequence of the binary tree directly: this algorithm can be used to find the reverse Polish expression of an expression.

/*
	可以由二叉树的括号形式直接求该二叉树的后根序列。
	由于二叉树的括号表示b为中根序,b[i]=')'表示一颗子树结束,将该子树输出
	如果b[i+1]为结点,则该子树为b[i+1]的左子树,将b[i+1]保存到一个栈中
	再考虑b[i+1]的右子树,当右子树输出后,再输出b[i+1]
*/
//根据二叉树的括号表示法直接求二叉树的后根序列
string postOrder(string b) {
	string ret;
	stack<char>stk;
	for (int i = 0; i < b.size(); i++){
		if (b[i] != ')')  //b[i]为左括号或结点,直接入栈
			stk.push(b[i]);
		else {  //b[i]为右括号
			while (!stk.empty() && stk.top() != '(') //将栈顶的结点加入ret,直到左括号为止
				ret += stk.top(), stk.pop();
			if (!stk.empty())
				stk.pop(); //左括号出栈
		}
	}
	return ret;
}

 Hierarchy traversal:

The hierarchical traversal of the binary tree starts from the root node and visits the binary tree layer by layer, and each layer visits each node in order from left to right. The sequence of nodes obtained by hierarchical traversal is called a hierarchical sequence. For example, the level traversal sequence of the above binary tree graph is ABCDEFGH.

The implementation of hierarchical traversal requires the use of queues, and the implementation method is similar to the breadth-first exploration algorithm. 

reference: 

1. Data structure: Tree (Tree) [Detailed Explanation]_UniqueUnit's Blog-CSDN Blog_Data Structure Tree

2.  Tree (Tree) and binary tree

Guess you like

Origin blog.csdn.net/qq_62687015/article/details/128650963