一.什么是树呢?有什么比较出名的树的应用呢?
树是一种数据结构,它由若干个节点(node)组成,并通过边(edge)互相连接。树具有以下特点:
1. 每个节点只有一个父节点,除根节点外。
2. 每个节点可以有多个子节点。
3. 根节点是唯一的,没有父节点。
4. 除了根节点之外,每个节点都有唯一的路径连接到根节点。
树广泛用于计算机科学中,例如在搜索算法、操作系统、编译器以及数据库等领域中都有应用。常见的树结构有二叉树、AVL树、B树等。
我们这篇介绍的就是二叉树
二.什么是二叉树呢?二叉树的形态?
二叉树是一种特殊的树结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。
结点的度:左右节点各为一个度
叶子结点或者终端结点:度为0的结点
树的度:结点的最大分支数
树的深度(高度)---层数
二叉树形态: 空树,根节点,根节点+左子树 根结点+右子树 根+左+右
根据树的结点和树的形状,分为几个形态:斜树,满二叉树和完全二叉树
三.C++实现中类的事件
1.节点类的设计
class Node {
public:
Node() : m_left(nullptr), m_right(nullptr) {};
Node(char v) : m_value(v), m_right(nullptr), m_left(nullptr) {};
char m_value;
Node *m_left;
Node *m_right;
};
我们在节点类中定义了两个构造方法,有参和无参构造。成员分别是存储的数据,节点的左右孩子的指针。
当然,正常情况我们使用的数据肯定不是某个类型,大概会是某个类。俺们这时写的小demo会是很简单的例子。
2.树类的设计
class Tree {
public:
Tree() : m_root(nullptr) {};//树的构造方法
void CreateTree(const char *&str) {
CreateTree(m_root, str);
}//构造树的方法,实现方法不在类中实现。代码量有点小大,不适合卸载类中。
void CreateTree(Node *&root, const char *&str);//这个是真正实现构造树的方法
void preOrder(Node *root);//树的前序递归遍历,实现也在类外。下同,不再赘述。
void preOrder_1();//树的前序非递归遍历(栈实现),类外。
void inOrder(Node *root);//树的中序递归遍历,类外。
void inOrder_1();//树的中序非递归遍历(栈实现),类外。
void PastOrder(Node *root);//树的后序递归遍历,类外。
void PastOrder_1();//树的后序非递归遍历,类外。
int Size() { return Size(m_root); }//树的节点个数,这个是调用下面的方法。
int Size(Node *root);//树的个数
int Height() { return Height(m_root); }//求树的高度,和Size()方法类似。
int Height(Node *root);//树的高度求法具体实现。
Node *Find(char v) { return Find(m_root, v); }//和上面两个方法实现类似。
Node *Find(Node *root, char v);//树的高度求法具体实现。
void LevelOrder();//树当前所在层的位置。
Node *m_root;//树的根节点指针。
};
每个函数功能在上面啰嗦过了昂,之后俺使用的时候不会就来看这个。
3.树类的函数之 -- 构造树的函数
void Tree::CreateTree(Node *&root, const char *&str) {
if (*str == '*') {
root = nullptr;
} else {
root = new Node(*str);
CreateTree(root->m_left, ++str);
CreateTree(root->m_right, ++str);
}
}
我们构造树的时候用的字符串为"ABDH**I**EJ***CF**G**",大家如果想换成其他的话,也可以比如'#',只需要把第二行哪个字符换掉就可以了。
至于传的参数,为什么是指针+const,为了减小值传递对内存的消耗,而const是为了递归深入的时候每一次共享当前位置。
4.前序遍历的两个方法 -- 递归和非递归遍历树
void Tree::preOrder(Node *root) {
if (root != nullptr) {
cout << root->m_value << " ";
preOrder(root->m_left);
preOrder(root->m_right);
}
cout<<endl;
}
void Tree::preOrder_1() const {
stack<Node*> pre;
Node* top= nullptr;
if(m_root!= nullptr){
pre.push(m_root);
while(!pre.empty()){
top = pre.top();
pre.pop();
cout<<top->m_value<<" ";
if(top->m_right!= nullptr){
pre.push(top->m_right);
}
if(top->m_left!= nullptr){
pre.push(top->m_left);
}
}
}
cout<<endl;
}
上面的是递归遍历,这个就不多说了把。递归的过程就是栈帧的开辟和回退。
说一下后面的非递归遍历把,我们需要一个栈来存储两个节点。为什么先将右孩子入栈呢?因为栈是后入先出的,我们需要先遍历左孩子┗|`O′|┛ 嗷~~。所以后入左孩子。每次都要将当前的栈顶元素保存起来,防止出栈后找不到。
5.中序遍历树 -- 递归遍历和非递归遍历(和前序遍历基本一样,只是)
void Tree::inOrder(Node *root) {
if (root != nullptr) {
inOrder(root->m_left);
cout << root->m_value << " ";
inOrder(root->m_right);
}
cout<<endl;
}
void Tree::inOrder_1() {
if (m_root != nullptr)
{
stack<Node*> ss;
Node* p = m_root;
Node* top = nullptr;
while (p != nullptr || !ss.empty())
{
while (p != nullptr)
{
ss.push(p);
p = p->m_left;
}
if (!ss.empty())
{
top = ss.top();
ss.pop();
cout << top->m_value << " ";
p = top->m_right;
}
}
}
cout << endl;
}
同样,递归的方法我们不去赘述。
非递归的方法,我们同样是定义了一个栈。此时我们的循环进入条件加了一个或栈不为空,因为开始的话。我们栈中没有元素(前序是因为可以直接将根节点入栈,我们不需要加)OK,解释结束。循环内的while循环是先找到当前节点的左孩子,直到树的叶子。而if内是进行输出,每次输出栈的一个节点。
6.后序遍历树 -- 递归遍历和非递归遍历
void Tree::PastOrder(Node *root) {
if (root != nullptr) {
PastOrder(root->m_left);
PastOrder(root->m_right);
cout << root->m_value << " ";
}
}
void Tree::PastOrder_1() {
if (m_root != nullptr)
{
stack<Node*> ss;
Node* top, * pre = nullptr;
ss.push(m_root);
while (!ss.empty())
{
top = ss.top();
if ((top->m_left == nullptr && top->m_right == nullptr) ||
(pre != nullptr && top->m_left == pre) ||
(pre != nullptr && top->m_right == pre))
{
ss.pop();
cout << top->m_value << " ";
pre = top;
}
else
{
if (top->m_right != nullptr)
ss.push(top->m_right);
if (top->m_left != nullptr)
ss.push(top->m_left);
}
}
}
cout << endl;
}
还是不说递归遍历┗|`O′|┛ 嗷~~(因为只有几行),细说一下非递归遍历吧。
首先入栈的是根节点,因为根是最后输出的,栈也是先入后出,所以先入根。我们此时定义了一个pre指针,pre此时指向的是当前栈顶元素。因为出栈完左右孩子后又回到了左右孩子的父节点,防止他出现死循环,我们需要判断他的左右孩子是否已经输出了,输出的话用pre进行标记。
7.树的高度 -- 递归遍历
int Tree::Height(Node *root) {
if (root == nullptr) {
return 0;
} else {
int lefth = Height(root->m_left);
int righth = Height(root->m_right);
return lefth > righth ? (lefth + 1) : (righth + 1);
}
}
相当于遍历了一次完整的树,每次遍历到叶子节点都得判断是否是最大的节点。
8.树的结点个数 -- 递归遍历(特别像斐波那契数列的递归求解代码写法)
int Tree::Size(Node *root) {
if (root == nullptr)
return 0;
else
return Size(root->m_left) + Size(root->m_right) + 1;
}
9.元素的查找 -- 递归查找
Node *Tree::Find(Node *root, char v) {
if (root == nullptr)
return nullptr;
if (root->m_value == v)
return root;
Node *p = Find(root->m_left, v);
if (p == nullptr)
return Find(root->m_right, v);
else
return p;
}
10.树的层级遍历 -- 每层每层遍历,非递归
void Tree::LevelOrder()
{
if (m_root != nullptr)
{
queue<Node*> qq;
qq.push(m_root);
Node* front = nullptr;
while (!qq.empty())
{
front = qq.front();
qq.pop();
cout << front->m_value << " ";
if (front->m_left != nullptr)
qq.push(front->m_left);
if (front->m_right != nullptr)
qq.push(front->m_right);
}
cout << endl;
}
}
四.附录(完整代码)
//
// Created by 18751 on 2023/6/3.
//
#include<iostream>
#include <stack>
#include<queue>
using namespace std;
class Node {
public:
Node() : m_left(nullptr), m_right(nullptr) {};
Node(char v) : m_value(v), m_right(nullptr), m_left(nullptr) {};
char m_value;
Node *m_left;
Node *m_right;
};
class Tree {
public:
Tree() : m_root(nullptr) {};
void CreateTree(const char *&str) {
CreateTree(m_root, str);
}
void CreateTree(Node *&root, const char *&str);
void preOrder(Node *root);
void preOrder_1() const;
void inOrder(Node *root);
void inOrder_1();
void PastOrder(Node *root);
void PastOrder_1();
int Size() { return Size(m_root); }
int Size(Node *root);
int Height() { return Height(m_root); }
int Height(Node *root);
Node *Find(char v) { return Find(m_root, v); }
Node *Find(Node *root, char v);
void LevelOrder();
Node *m_root;
};
void Tree::CreateTree(Node *&root, const char *&str) {
if (*str == '*') {
root = nullptr;
} else {
root = new Node(*str);
CreateTree(root->m_left, ++str);
CreateTree(root->m_right, ++str);
}
}
void Tree::preOrder(Node *root) {
if (root != nullptr) {
cout << root->m_value << " ";
preOrder(root->m_left);
preOrder(root->m_right);
}
cout<<endl;
}
void Tree::preOrder_1() const {
stack<Node*> pre;
Node* top= nullptr;
if(m_root!= nullptr){
pre.push(m_root);
while(!pre.empty()){
top = pre.top();
pre.pop();
cout<<top->m_value<<" ";
if(top->m_right!= nullptr){
pre.push(top->m_right);
}
if(top->m_left!= nullptr){
pre.push(top->m_left);
}
}
}
cout<<endl;
}
void Tree::inOrder(Node *root) {
if (root != nullptr) {
inOrder(root->m_left);
cout << root->m_value << " ";
inOrder(root->m_right);
}
cout<<endl;
}
void Tree::inOrder_1() {
if (m_root != nullptr)
{
stack<Node*> ss;
Node* p = m_root;
Node* top = nullptr;
while (p != nullptr || !ss.empty())
{
while (p != nullptr)
{
ss.push(p);
p = p->m_left;
}
if (!ss.empty())
{
top = ss.top();
ss.pop();
cout << top->m_value << " ";
p = top->m_right;
}
}
}
cout << endl;
}
void Tree::PastOrder(Node *root) {
if (root != nullptr) {
PastOrder(root->m_left);
PastOrder(root->m_right);
cout << root->m_value << " ";
}
}
void Tree::PastOrder_1() {
if (m_root != nullptr)
{
stack<Node*> ss;
Node* top, * pre = nullptr;
ss.push(m_root);
while (!ss.empty())
{
top = ss.top();
if ((top->m_left == nullptr && top->m_right == nullptr) ||
(pre != nullptr && top->m_left == pre) ||
(pre != nullptr && top->m_right == pre))
{
ss.pop();
cout << top->m_value << " ";
pre = top;
}
else
{
if (top->m_right != nullptr)
ss.push(top->m_right);
if (top->m_left != nullptr)
ss.push(top->m_left);
}
}
}
cout << endl;
}
int Tree::Height(Node *root) {
if (root == nullptr) {
return 0;
} else {
int lefth = Height(root->m_left);
int righth = Height(root->m_right);
return lefth > righth ? (lefth + 1) : (righth + 1);
}
}
int Tree::Size(Node *root) {
if (root == nullptr)
return 0;
else
return Size(root->m_left) + Size(root->m_right) + 1;
}
Node *Tree::Find(Node *root, char v) {
if (root == nullptr)
return nullptr;
if (root->m_value == v)
return root;
Node *p = Find(root->m_left, v);
if (p == nullptr)
return Find(root->m_right, v);
else
return p;
}
void Tree::LevelOrder()
{
if (m_root != nullptr)
{
queue<Node*> qq;
qq.push(m_root);
Node* front = nullptr;
while (!qq.empty())
{
front = qq.front();
qq.pop();
cout << front->m_value << " ";
if (front->m_left != nullptr)
qq.push(front->m_left);
if (front->m_right != nullptr)
qq.push(front->m_right);
}
cout << endl;
}
}
int main() {
Tree t;
const char* str = "ABDH**I**EJ***CF**G**";
t.CreateTree(str);
t.inOrder(t.m_root);
cout<<endl;
t.inOrder_1();
t.PastOrder_1();
t.LevelOrder();
t.PastOrder_1();
}
//虚负凌云万丈才