1. 树的根本结构
1.1、树的根本概念
- 树的界说 树是n(n>=0)个结点的有限集。当n = 0时,称为空树。在任意一棵非空树中应满意:
- 有且仅有一个特定的称为根的结点。
- 当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1,T2,…,Tm,其中每个集合自身又是一棵树,而且称为根的子树。
- 树的根结点没有前驱,除根结点外的一切结点有且只要一个前驱。
- 树中一切结点能够有零个或多个后继,因而n个结点的树中有n-1条边。
1.2 树的结构体
typedef struct BiTNode {
char data;
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
这个结构体它界说了一个叫做BiTNode
的结构体,以及一个指向该结构体的指针类型BiTree
。
具体来说,BiTNode
结构体包括以下三个成员:
-
data
:一个字符类型的数据成员。 -
lchild
:一个指向BiTNode
类型的左子树的指针。 -
rchild
:一个指向BiTNode
类型的右子树的指针。
lchild
和rchild
分别表明指向左子树和右子树的指针,而data
则存储每个节点的数据。经过这种方式,能够构建出一个具有左右子树结构的二叉树。
此外,该界说还运用了typedef关键字来为结构体BiTNode
界说了一个别号BiTree
,表明指向该结构体的指针类型。这样,我们就能够运用BiTree
来声明指向BiTNode
结构体的指针变量。
2. 树的创建和前序遍历
- 现在要创建一棵如下的树
ABD##E##C##
,#
表明空节点。
A
B C
D E # #
# # # #
- 递归创建树
void createtree(BiTree& t) { // 先序结构二叉树。
char ch;
// ABD##E##C##
ch = getchar(); // 输入字符。
if (ch == '#')
t = NULL;
else {
t = (BiTNode*)malloc(sizeof(BiTNode));
t->data = ch;
t->lchild = NULL;
t->rchild = NULL;
createtree(t->lchild);
createtree(t->rchild);
}
};
- 前序遍历打印树的节点
void PreOrder(BiTree T) {
if (T != NULL) {
cout << ' ' << T->data;
PreOrder(T->lchild); // 递归遍历左子树
PreOrder(T->rchild); // 递归遍历右子树
}
};
3. 递归遍历二叉树
前中后序遍历二叉树的完整可执行代码
#include <bits/stdc .h>
using namespace std;
typedef struct BiTNode {
char data;
struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
// 递归鸿沟,递归式
/*
BiTNode *p 和BiTree
p,在代码的视点上来讲是等价的,都是界说一个指针变量p,来指向二叉树的节点 BiTree
p:侧重于表明p指向的节点为根的一棵树 BiTNode *p 侧重表明一个树的节点
*/
// PreOrder 先序遍历 根左右
// InOder 中序低估遍历称号
// PostOrder 后序递归遍历
/**
A
B C
D E # #
# # # #
*/
void createtree(BiTree& t) { // 先序结构二叉树。
char ch;
// ABD##E##C##
ch = getchar(); // 输入字符。
if (ch == '#')
t = NULL;
else {
// t = new Node; // 创建一个新的 Node 对象,并将其地址赋值给根节点。
//也能够这样写 t = (BiTNode*)malloc(sizeof(BiTNode));
t = (BiTNode*)malloc(sizeof(BiTNode));
t->data = ch;
t->lchild = NULL;
t->rchild = NULL;
createtree(t->lchild);
createtree(t->rchild);
}
};
// 递归-前序遍历二叉树
void PreOrder(BiTree T) {
if (T != NULL) {
cout << " " << T->data;
PreOrder(T->lchild); // 递归遍历左子树
PreOrder(T->rchild); // 递归遍历右子树
}
};
// 递归-中序遍历二叉树
void InOder(BiTree T) {
if (T != NULL) {
InOder(T->lchild); // 递归遍历左子树
cout << " " << T->data;
InOder(T->rchild); // 递归遍历右子树
}
};
// 递归-后序遍历二叉树
void PostOrder(BiTree T) {
if (T != NULL) {
PostOrder(T->lchild); // 递归遍历左子树
cout << " " << T->data;
PostOrder(T->rchild); // 递归遍历右子树
}
};
int main() {
BiTree t;
// 输入创建ABD##E##C##
createtree(t);
cout << "递归-前序遍历二叉树:";
PreOrder(t);
cout << endl;
cout << "递归-中序遍历二叉树:";
InOder(t);
cout << endl;
cout << "递归-后序遍历二叉树:";
PostOrder(t);
cout << endl;
return 0;
}
4. 非递归遍历二叉树
树结构,代码结合图理解,非常重要!!!
tips:栈的根本运用
用栈来保存数据
在C 中,你能够运用STL库中的stack类来创建一个仓库。除了stack.size()之外,stack类还供给了以下几种常见的操作:
-
stack.empty()
:查看仓库是否为空。假如仓库为空,则回来true;否则回来false。 -
stack.top()
:回来仓库顶部的元素,但不将其从仓库中移除。 -
stack.push(x)
:将元素x推入仓库的顶部。 -
stack.pop()
:移除并回来仓库顶部的元素。 -
stack.data()
:回来一个表明仓库的容器。这个容器是一个后进先出(LIFO)的容器。
stack.size() != 0,栈不为空,成果便是 true;stack.size() == 0假如仓库为空,那么成果就为 false。
4.1 前序遍历
// 非递归-前序遍历
/*
思维:用栈来完成。首要拜访根节点,然后将根节点入栈,接着拜访当时节点的左节点,然后入栈,当左节点拜访完后,
出栈,并顺次拜访右节点
*/
void un_preTraversal(Node* root) {
stack<Node*>
stack; // 名为 stack 的仓库(stack),名为 stack 的仓库(stack)
// 当时节点
Node* p = root;
// 因而,stack.size() != 0
// 的意义是查看仓库中是否有元素,也便是查看仓库是否为空。假如仓库不为空,那么
// stack.size() != 0 的成果就为 true;假如仓库为空,那么成果就为 false。
while (p != NULL || stack.size() != 0) {
if (p != NULL) {
visit(p); // 拜访p之前一定要确保p不为空
stack.push(p); // 入栈
p = p->lchild;
} else {
p = stack.top();
stack.pop();
p = p->rchild;
}
}
}
4.2 中序遍历
// 非递归-中序遍历
/*
思维:用栈来完成。从根节点开始顺次遍历当时节点的左节点,并顺次入栈,当左节点遍历完成后,获取
栈顶元素并出栈,然后拜访该节点,并顺次遍历其右节点
*/
void un_midTraversal(Node* root) {
stack<Node*> stack;
Node* p = root;
while (p != NULL || stack.size() != 0) {
if (p != NULL) {
stack.push(p);
p = p->lchild;
} else {
p = stack.top();
stack.pop();
visit(p);
p = p->rchild;
}
}
}
4.3. 后序遍历
#include <bitsstdc .h>
typedef struct treenode {
char data;
struct treenode *lchild, *rchild;
int tag;
} treenode, *tree;
void createtree(tree& t) { // 先序结构二叉树。
char ch;
// ABD##E##C##
ch = getchar(); // 输入字符。
if (ch == '#')
t = NULL;
else {
t = (treenode*)malloc(sizeof(treenode));
t->data = ch;
t->tag = 0;
t->lchild = NULL;
t->rchild = NULL;
createtree(t->lchild);
createtree(t->rchild);
}
}
void back(tree t) {
struct treenode* stack[100]; // 直接运用结构体树立一个栈。
int top = -1;
treenode* p = t; // 树立指针指向这棵树。
treenode* x; // 保存出栈的结点。
while (p || top != -1) // p和栈不能为空。
{
if (p) // p不为空时进栈。
{
top ;
stack[top] = p;
p = p->lchild;
} else // 为空的时分。
{
p = stack[top]; // 拜访栈顶。
if (p->rchild && p->rchild->tag == 0) // 判别右孩子。
{ // 没有被拜访,指向它的右孩子。
p = p->rchild; // p有值要进行栈
} else // 现已拜访过了。
{
p = stack[top];
top--;
printf(",", p->data);
p->tag = 1;
p = NULL;//这儿p为空之后,拜访栈顶
}
}
}
}
int main() {
tree t;
createtree(t);
back(t);
return 0;
}
5. 树的层次遍历
5.1 从上至下,从左至右
tips:行列的根本运用
首要需要理解行列的运用:
- 在C 中,
queue<Node*> q;
是一个声明一个名为q
的行列(queue)的语句,其中Node*
是存储在行列中的元素的类型。在这种状况下,行列将存储指向Node
对象的指针。 - 在C 的STL库中,queue容器供给了以下几种操作:
-
push(x)
:将元素x插入行列的尾部。 -
pop()
:删去行列头部的元素。 -
front()
:回来指向行列头部的元素。 -
back()
:回来指向行列尾部的元素。 -
size()
:回来行列中元素的个数。 -
empty()
:判别行列是否为空。
q.empty() == false,行列不为空
// 树的层次遍历
// 思维:运用行列queue。先将根节点入行列,循环判别当时行列不为空时,将头元素出行列并拜访头元素,然后在将它的左节点和右节点入行列
void levelTraversal(Node* root) {
queue<Node*> q;
Node* p = root;
q.push(p); // 将元素p插入行列的尾部
// 判别条件,false行列不为空
while (q.empty() == false) {
p = q.front(); // 回来指向行列头部的元素
q.pop(); // 删去行列头部的元素
visit(p);
if (p->lchild != NULL)
q.push(p->lchild);//左子树持续入队
if (p->rchild != NULL)
q.push(p->rchild);//右子树入队
}
}
5.2 逆置层次遍历,从下至上,从左至右
思路:将正常层次遍历的成果,存入栈中,再取出来。
// 层次遍历
void leveltraversal(tree* root) {
stack<tree*> stack;
queue<tree*> q;
tree* p = root;
q.push(p);
while (q.empty() == false) {
p = q.front();
q.pop();
cout << p->data;
stack.push(p);
if (p->lchild != NULL) {
q.push(p->lchild);
};
if (p->rchild != NULL) {
q.push(p->rchild);
};
}
cout << endl;
cout << "层次遍历,逆序:";
// 不等于0阐明栈不为空
while (stack.size() != 0) {
tree* p = stack.top();
stack.pop();
cout << p->data;
}
}
6. 找二叉树的公共先人节点
找i,j的公共先人节点
顺序存储,将二叉树转换,空的地方,赋值为-1
。
-
i
,j
节点均存在,treep[i]!=-1,tree[j]!=-1
- 假如不存在更新
i
和j
的值,假如i>j
,寻觅i的父节点(i/2),更新i
。假如i<j
,寻觅j
的父节点(j/2),更新j
; -
i=j
时,回来i
的值。
#include <bits/stdc .h>
// 找i,j的公共先人节点
struct tree {
// 不存在的数值为-1
int data[12] = {-1, 1, 2, 3, -1, 4, -1, 5, -1, -1, -6, -1};
};
/*
i节点的父亲节点为2i,
左孩子是2i
右孩子是2i 1
*/
int comon(tree t, int i, int j) {
if (t.data[i] != -1 && t.data[j] != -1) // 阐明存在值
{
while (i != j) // 更新i和j的值。
{
if (i > j)
i /= 2; // 寻觅大的父节点。更新i的值
else
j /= 2;
}
return t.data[i]; // 相一起回来公共先人。
}
return -1;
};
int main() {
tree t;
int a = comon(t, 7, 10); // 找7号和10号节点的公共先人。
printf("%d", a);
return 0;
}
7. 判别是否为彻底二叉树
不满意下面任何一条就不是彻底二叉树
- 节点无左孩子,必无右孩子。
- 节点缺孩子,一切后继必无孩子。
第一个if判别状况
否则的状况
树结构
写法一:
// 判别是否是彻底二叉树
#include <bits/stdc .h>
using namespace std;
#define Max 15
typedef struct treenode {
char data;
struct treenode *lchild, *rchild;
} treenode, *tree;
void buildtree(tree& t) {
char ch;
ch = getchar();
if (ch == '#')
t = NULL;
else {
t = (treenode*)malloc(sizeof(treenode));
t->data = ch;
t->lchild = NULL;
t->rchild = NULL;
buildtree(t->lchild);
buildtree(t->rchild);
}
}
struct squeue {
struct treenode* data[Max];
int f, r, tag;
};
bool isempty(squeue s) {
if (s.f == s.r && s.tag == 0)
return true;
return false;
}
bool isfull(squeue s) {
if (s.f == s.r && s.tag == 1)
return true;
return false;
}
bool enters(squeue& s, treenode* p) {
if (isfull(s))
return false;
s.data[s.r] = p;
s.r = (s.r 1) % Max;
s.tag = 1;
return true;
}
bool outs(squeue& s, treenode*& p) {
if (s.f == s.r && s.tag == 0)
return false;
p = s.data[s.f];
s.f = (s.f 1) % Max;
s.tag = 0;
return true;
}
bool isok(tree t) {
squeue s;
s.f = s.r = s.tag = 0;
// flag 为true标志都有左右孩子,ans成果标志
bool flag = true, ans = true;
if (t == NULL) // 判别是否为空树,空树也是彻底二叉树。
ans = true;
if (!t->lchild && !t->rchild) // 存在节点,左右孩子为空,也是彻底二叉树
ans = true;
enters(s, t); // 根节点入队
treenode* p;
while (!isempty(s)) { // 遍历队
outs(s, p); // 先出队
if (!p->lchild) { // 出队元素,没有左孩子
flag = false;
if (p->rchild) // 在缺孩子的状况下,还有右孩子,那么成果直接为false,不是彻底二叉树。
ans = false;
} else // 有左孩子
{
if (flag) // 之前不存在缺孩子的节点
{
enters(s, p->lchild); // 左孩子进队
if (p->rchild) // 右孩子存在持续进队
enters(s, p->rchild);
else // 没有右孩子的状况下,便是缺孩子将flag设置成false;
flag = false;
} else // 之前存在缺孩子的节点,的后继又有孩子,成果变量直接为false,不是彻底二叉树
ans = false; //
}
}
if (ans)
return true;
return false;
}
int main() {
tree t;
buildtree(t);
if (isok(t))
cout << "yes" << endl;
else
cout << "no" << endl;
return 0;
}
// ABD##E##CF##G## 彻底
// ABD###CE##F## 非彻底
写法二(推荐)
#include <bits/stdc .h>
using namespace std;
// ABD##E##CF##G## 彻底
// ABD###CE##F## 非彻底
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
void createTree(tree*& root) {
char a;
a = getchar();
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTree(root->lchild);
createTree(root->rchild);
}
}
void preOrder(tree* root) {
if (root != NULL) {
cout << root->data;
preOrder(root->lchild);
preOrder(root->rchild);
}
}
bool isok(tree* root) {
queue<tree*> q;
tree* p;
bool flag = true, ans = true;
if (root == NULL) {
ans = true;
}
if (!root->rchild && !root->rchild) {
ans = true;
}
q.push(root);
// 判别条件,false行列不为空
while (q.empty() == false) {
p = q.front();
q.pop();
if (!p->lchild) { // 判别出队元素没有左孩子
flag = false; // 设置标记
if (p->rchild) { // 在没有左孩子的状况下,还没有右孩子,直接不是彻底二叉树,设置false。
ans = false;
}
} else { // 有左孩子
if (flag) {
q.push(p->lchild);
if (p->rchild) {
q.push(p->rchild);
} else {
flag = false;
}
} else {
ans = false;
}
}
}
if (ans)
return true;
return false;
}
int main() {
tree* p;
createTree(p);
// ABD##E##CF##G## 彻底
// ABD###CE##F## 非彻底
// AB#D##CE##F##
cout << "前序遍历二叉树:";
preOrder(p);
cout << endl;
cout << "是否是彻底二叉树:";
if (isok(p))
cout << "yes" << endl;
else
cout << "no" << endl;
return 0;
}
8 .查找第k个节点的值
假定二叉树选用二叉链存储结构存储,设计一个算法,求先序遍历序列中第k(1 <k<二叉树中结点个数)个结点的值。
#include <bits/stdc .h>
using namespace std;
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
/*
假定二叉树选用二叉链存储结构存储,设计一个算法,求先序遍历序列中第k(1
<k<二叉树中结点个数)个结点的值
ABD##E##C##
// A1
// B2 C
// D3 E4 F #
// # # # # # #
*/
// 创建树
void createTre(tree*& root) {
char a;
cin >> a;
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTre(root->lchild);
createTre(root->rchild);
}
};
void preorder(tree* root, int k, char& temp, int& index) {
// cout << index;
if (root != NULL) {
index ;
if (index == k) {
temp = root->data;
};
cout << root->data << " ";
preorder(root->lchild, k, temp, index);
preorder(root->rchild, k, temp, index);
}
};
int main() {
tree* root;
createTre(root);
char temp;
int index = 0;
cout << "递归前序遍历二叉树:";
preorder(root, 5, temp, index);
cout << endl;
cout << "第4个节点的值:" << temp << endl;
return 0;
}
9. 指针指向树的某个节点
在一棵以二叉链表为存储结构的二叉树中,查找数据域值等于 key 的结 点是否存在,假如存在,则将指针 q 指向该结点,假定结点数据域为 int 型
#include <bits/stdc .h>
using namespace std;
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
/*
在一棵以二叉链表为存储结构的二叉树中,查找数据域值等于 key 的结
点是否存在,假如存在,则将指针 q 指向该结点,假定结点数据域为 int 型。(二
叉树中结点值都不相同)
ABD##E##C##
// A1
// B2 C
// D3 E4 F #
// # # # # # #
*/
// 创建树
void createTre(tree*& root) {
char a;
cin >> a;
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTre(root->lchild);
createTre(root->rchild);
}
};
void disp(tree* root) {
if (root != NULL) {
cout << root->data;
disp(root->lchild);
disp(root->rchild);
}
}
// q为指向改节点为key的指针,key为节点的值
void search(tree* root, tree*& q, int key) {
if (root != NULL) {
if (root->data == key) {
q = root;
} else {
search(root->lchild, q, key);
search(root->rchild, q, key);
}
}
};
int main() {
tree* root;
createTre(root);
tree* q;
cout << "递归前序遍历二叉树:";
disp(root);
cout << endl;
// cout << "查找值为E的节点的值:";
search(root, q, 'E');
cout << "q个节点的值:" << q->data << endl;
return 0;
}
10. 运用递归核算二叉树中一切结点的个数。
法一:界说全局变量 n 用来计数。
#include <bits/stdc .h>
using namespace std;
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
/*
在一棵以二叉链表为存储结构的二叉树中,查找数据域值等于 key 的结
点是否存在,假如存在,则将指针 q 指向该结点,假定结点数据域为 int 型。(二
叉树中结点值都不相同)
ABD##E##C##
// A1
// B2 C
// D3 E4 F #
// # # # # # #
*/
// 创建树
void createTre(tree*& root) {
char a;
cin >> a;
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTre(root->lchild);
createTre(root->rchild);
}
};
int n = 0;
void disp(tree* root) {
if (root != NULL) {
n ;
cout << root->data;
disp(root->lchild);
disp(root->rchild);
}
}
int main() {
tree* root;
createTre(root);
tree* q;
cout << "递归前序遍历二叉树:";
disp(root);
cout << endl;
cout << "树节点的个数:" << n << endl;
return 0;
}
法二:界说 n1 和 n2 分别用于接纳左右子树结点个数。
#include <bits/stdc .h>
using namespace std;
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
/*
在一棵以二叉链表为存储结构的二叉树中,查找数据域值等于 key 的结
点是否存在,假如存在,则将指针 q 指向该结点,假定结点数据域为 int 型。(二
叉树中结点值都不相同)
ABD##E##C##
// A1
// B2 C
// D3 E4 F #
// # # # # # #
*/
// 创建树
void createTre(tree*& root) {
char a;
cin >> a;
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTre(root->lchild);
createTre(root->rchild);
}
};
int n = 0;
void disp(tree* root) {
if (root != NULL) {
n ;
cout << root->data;
disp(root->lchild);
disp(root->rchild);
}
}
int count(tree* root) {
int n1, n2;
if (root == NULL) { // 节点为空的时分,回来0
return 0;
} else {
n1 = count(root->lchild);
n2 = count(root->rchild);
return n1 n2 1; // 这儿的 1是树自身的节点。重点留意
}
}
int main() {
tree* root;
createTre(root);
tree* q;
cout << "递归前序遍历二叉树:";
disp(root);
cout << endl;
cout << "树节点的个数:" << count(root) << endl;
return 0;
}
11. 核算二叉树一切子节点个数
1.递归
#include <bits/stdc .h>
using namespace std;
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
/*
运用递归核算二叉树中一切叶子结点的个数。
ABD##E##C##
// A1
// B2 C
// D3 E4 F #
// # # # # # #
*/
// 创建树
void createTre(tree*& root) {
char a;
cin >> a;
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTre(root->lchild);
createTre(root->rchild);
}
};
int n = 0;
void disp(tree* root) {
if (root != NULL) {
if (root->lchild == NULL && root->rchild==NULL) {
n ;
};
cout << root->data;
disp(root->lchild);
disp(root->rchild);
}
}
int main() {
tree* root;
createTre(root);
tree* q;
cout << "递归前序遍历二叉树:";
disp(root);
cout << endl;
cout<<"子树个数:"<<n;
return 0;
}
12 .找双分支节点
#include <bits/stdc .h>
using namespace std;
typedef struct tree {
char data;
struct tree *lchild, *rchild;
} tree;
/*
运用递归核算二叉树中一切双分支结点个数
双分支节点:既有左子树又有右子树(如下图的:A1,B2)
ABD##E##C##
// A1
// B2 C
// D3 E4 F #
// # # # # # #
*/
// 创建树
void createTre(tree*& root) {
char a;
cin >> a;
if (a == '#') {
root = NULL;
} else {
root = new tree();
root->data = a;
root->lchild = NULL;
root->rchild = NULL;
createTre(root->lchild);
createTre(root->rchild);
}
};
int n = 0;
void disp(tree* root) {
if (root != NULL) {
if (root->lchild != NULL &&
root->rchild != NULL) { // 当节点左右孩子不为空时,节点个数
n ;
};
cout << root->data;
disp(root->lchild);
disp(root->rchild);
}
}
int count3(tree* root) {
int n1, n2;
if (root == NULL) { // 节点为空的时分,回来0
return 0;
} else if (root->lchild && root->rchild) { // 双分支节点的状况
n1 = count3(root->lchild);
n2 = count3(root->rchild);
return n1 n2 1; // 这儿的 1是树自身的节点。重点留意
} else { // 单分支和叶子节点的状况
n1 = count3(root->lchild);
n2 = count3(root->rchild);
return n1 n2;
}
};
int main() {
tree* root;
createTre(root);
tree* q;
cout << "递归前序遍历二叉树:";
disp(root);
cout << endl;
cout << "双分支节点个数:" << n;
return 0;
}