详细实现代码参考:https://download.csdn.net/download/bailang_zhizun/12670972
1、二叉树的表示法
1.1、二叉链表示法
数据结构定义:
//1 - 二叉链表示法
//节点类型为int
typedef struct BiTNode
{
int data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
//节点类型为char
typedef struct BiTNodeC
{
char data;
struct BiTNodeC *lchild, *rchild;
}BiTNodeC, *BiTreeC;
实现:
void play01_2()
{
BiTNode *pt1, *pt2, *pt3, *pt4, *pt5;
pt1 = (BiTree) malloc(sizeof(BiTNode));
pt2 = (BiTree)malloc(sizeof(BiTNode));
pt3 = (BiTree)malloc(sizeof(BiTNode));
pt4 = (BiTree)malloc(sizeof(BiTNode));
pt5 = (BiTree)malloc(sizeof(BiTNode));
pt1->data = 1; pt2->data = 2; pt3->data = 3;
pt4->data = 4; pt5->data = 5;
//表达二叉树
pt1->lchild = pt2; pt1->rchild = pt3;
pt2->lchild = pt4;
pt3->lchild = pt5;
//
free(pt1); free(pt2); free(pt3); free(pt4); free(pt5);
}
1.2、三叉链表示法
和二叉链表示法相比,多了一个指向父节点的指针。
//2 - 三叉链表示法
typedef struct TriTNode
{
int data;
struct BiTNode *lchild, *rchild;
struct BiTNode *parent; //多了一个指向父节点的指针
}TriTNode, *TriTree;
1.3、双亲链表表示法
数据结构定义:
//3 - 双亲链表表示法(子节点中保存了双亲节点的位置)
#define MAX_TREE_SIZE 100
typedef struct BPTNode
{
int data;
int parentPosition; //指向双亲的指针(双亲数组下标)
char LRTag; //左右孩子标志域 L或R
}BPTNode;
typedef struct BPTree
{
BPTNode nodes[MAX_TREE_SIZE]; //因为节点之间是分散的,需要把节点存储到数组中
int num_node; //节点的数目
int root; //根节点的位置,注意此域存储的是父亲节点在数组的下标
}BPTree;
实现:
void play01_3()
{
BPTree tree; //定义树
tree.root = 0; //根节点位置
tree.num_node = 5;
//给树的元素赋值
//根节点
tree.nodes[0].data = 1;
//
tree.nodes[1].data = 2;
tree.nodes[1].parentPosition = 0;
tree.nodes[1].LRTag = 'L';
tree.nodes[2].data = 3;
tree.nodes[2].parentPosition = 0;
tree.nodes[2].LRTag = 'R';
for (int i = 0; i < 3; i++)
{
cout << tree.nodes[i].data << endl;
}
}
2、创建树
2.1、#法创建树
# 法创建树,让树的每一个节点都变成度数为2的树,其中没有节点的补足#,如下所示:
此时,124###3##是可以确定一棵树的,因为只有一个节点后面跟了两个#(##)就说明该节点为叶子节点。
BiTreeC createTree2(const char *s)
{
BiTreeC node = nullptr;
//1## - 先序字符串
static int cur_index = 0;
char ch = s[cur_index];
cur_index++;
if (ch == '#')
{
return nullptr;
}
else
{
node = (BiTreeC)malloc(sizeof(BiTNodeC));
if (nullptr == node) return nullptr;
memset(node, 0, sizeof(BiTNodeC));
node->data = ch;
node->lchild = createTree2(s);
node->rchild = createTree2(s);
}
return node;
}
const char *s = "ABDH#K###E##CFI###G#J##"; //先序字符串
BiTreeC p = nullptr;
p = createTree2(s);
2.2、中序和先序创建树
基本思想:
中序和先序创建树 算法:
1、通过先序遍历结果中找到根节点root,再通过root在中序遍历结果中找出左子树(左侧)、右子树(右侧)
2、在root的左子树中,找出左子树的根节点(在先序结果中中找),转步骤1;
3、在root的右子树中,找出右子树的根节点(在先序结果中中找),转步骤1;
const string g_pre_str = "ADEBCF";
const string g_in_str = "DEACFB";
string split_left(const string root_str, const char& root)
{
if (root_str.size() == 1)
{
return "";
}
string in_str = root_str;
string left = in_str.substr(0, in_str.find(root));
return left;
}
string split_right(const string root_str, const char& root)
{
string in_str = root_str;
if (root_str.size() == 1 || in_str.find(root) >= in_str.length()-1)
{
return "";
}
string right = in_str.substr(in_str.find(root) + 1, in_str.length());
return right;
}
char getRoot(const string root_str)
{
size_t pre_index = g_pre_str.length();
for (size_t i = 0; i < root_str.length(); i++)
{
size_t tmp = g_pre_str.find(root_str.at(i));
pre_index < tmp ? pre_index : pre_index = tmp;
}
return g_pre_str.at(pre_index);
}
BiTreeC createTree_in_and_pre(const string root_str)
{
BiTreeC node = nullptr;
if (root_str.empty())
{
return nullptr;
}
char root = getRoot(root_str);
if ('\0' == root)
{
return nullptr;
}
node = (BiTreeC)malloc(sizeof(BiTNodeC));
memset(node, 0, sizeof(BiTNodeC));
node->data = root;
string left_str = split_left(root_str, root);
string right_str = split_right(root_str, root);
node->lchild = createTree_in_and_pre(left_str);
node->rchild = createTree_in_and_pre(right_str);
return node;
}
const char *pre = "ADEBCF";
const char *in = "DEACFB";
BiTreeC p = nullptr;
p = createTree_in_and_pre(g_in_str);
2.3、销毁树
先序创建树则后续销毁。
void freeTree(BiTreeC t)
{
if (nullptr == t)
return;
if (nullptr != t->lchild)
{
freeTree(t->lchild); t->lchild = nullptr;
}
if (nullptr != t->rchild)
{
freeTree(t->rchild); t->rchild = nullptr;
}
if(t)
{
free(t);
t = nullptr;
}
return;
}
3、二叉树的遍历
3.1、先序遍历
先序遍历:中左右
//先序遍历
void preOrder(BiTNode* root)
{
if (nullptr == root)
{
return;
}
//打印根节点
cout << root->data << " ";
//然后 遍历左子树
preOrder(root->lchild);
//最后 遍历右子树
preOrder(root->rchild);
return;
}
3.2、中序遍历
中序遍历:左中右
//中序遍历
void inOrder(BiTNode* root)
{
if (nullptr == root)
{
return;
}
//先 遍历左子树
inOrder(root->lchild);
//然后 打印根节点的值
cout << root->data << " ";
//最后 遍历右子树
inOrder(root->rchild);
return;
}
3.3、后序遍历
后序遍历:左右中
//后序遍历
void postOrder(BiTNode* root)
{
if (nullptr == root)
{
return;
}
//先 遍历左子树
postOrder(root->lchild);
//然后 遍历右子树
postOrder(root->rchild);
//最后 打印根节点的值
cout << root->data << " ";
return;
}
3.4、树的非递归遍历
树的非递归遍历(中序遍历),算法基本思想:
中序遍历的几种情况:
分析1:什么时候访问根?什么时候访问左子树?什么时候访问右子树?
当左子树为空或者左子树已经访问完毕,再访问根;
访问根之后,再访问右子树;
分析2:为什么是栈,而不是其他(比如 队列)
先走到的后访问,后走到的先访问,显示是站结构
分析3:节点所有路径情况
步骤1: 找到中序遍历的起点
如果节点 有 左子树,该节点入栈,继续找下一个左子树;
如果该节点 没有 左子树,访问该节点;
步骤2:
如果节点 有 右子树,走到右子树的根节点,重复步骤1;
如果节点 没有 右子树(节点访问完毕),根据栈顶指示访问栈顶元素,并回退;
如果栈为空,表示遍历结束;
注意:入栈的节点表示,本身没有被访问过,同时右子树也没有被访问过。
using TreeStack = stack<BiTreeC>;
//步骤①:向左走,直到找到中序遍历的起点
BiTreeC goLeft(BiTreeC root, TreeStack &stack)
{
if (nullptr == root)
{
return nullptr;
}
while (nullptr != root->lchild)
{
stack.push(root); //先压栈(节点有左子树)
root = root->lchild; //然后继续找左子树
}
cout << root->data << " "; //节点没有左子树,直接访问该节点
return root;
}
//中序 非递归 遍历
void InOrder_stack(BiTreeC root)
{
if (nullptr == root)
{
return;
}
TreeStack stack;
BiTreeC t = goLeft(root, stack);
while (t)
{
if (nullptr != t->rchild)
{//如果有右子树,则执行步骤①
t = goLeft(t->rchild, stack);
}
else if(nullptr == t->rchild && !stack.empty())
{//如果没有右子树,根据栈顶提示 回退,
t = stack.top();
stack.pop();
//打印
cout << t->data << " ";
}
else
{
t = nullptr;
}
}
}
4、树的操作
4.1、求树的高度
int treeDepth(BiTNode* root)
{
int leftDepth = 0;
int rightDepth = 0;
int depth = 0;
if (nullptr == root)
{
return depth;
}
//左子树高度
leftDepth = treeDepth(root->lchild);
//右子数高度
rightDepth = treeDepth(root->rchild);
depth = 1 + (leftDepth>rightDepth?leftDepth:rightDepth);
return depth;
}
4.2、copy树
BiTree treeCopy(BiTNode* root)
{
BiTree newNode = nullptr;
BiTree newLeft = nullptr;
BiTree newRight = nullptr;
if (nullptr == root)
return root;
newLeft = treeCopy(root->lchild);
newRight = treeCopy(root->rchild);
//效果与下面等同,因为上面已经判断了 nullptr == root 的情况
/* //该代码也可正常运行
if (root->lchild)
{
newLeft = treeCopy(root->lchild);
}
else
return nullptr;
if (root->rchild)
{
newRight = treeCopy(root->rchild);
}
else
return nullptr;
*/
newNode = (BiTree)malloc(sizeof(BiTNode));
if (!newNode)
return nullptr;
newNode->lchild = newLeft;
newNode->rchild = newRight;
newNode->data = root->data;
return newNode;
}