版权声明:私藏源代码是违反人性的罪恶行为!博客转载无需告知,学无止境。 https://blog.csdn.net/qq_41822235/article/details/86484063
一、二叉树的创建
create binary tree
预定义类型及结构设计:
typedef char TElemType;
typedef struct BiTNode
{
TElemType data;
BiTNode*leftChild, *rightChild;
}BiTNode, *BiTree;
举例将用到的二叉树:
- 先序 A B C D E F G H
- 中序 C B E D F A G H
- 后序 C E F D B H G A
1.1 先序_带标记
以#作为空树。
BiTNode *buyNode() //申请一个结点
{
BiTNode *pNode = new BiTNode;
assert(NULL != pNode);
memset(pNode, 0, sizeof(BiTNode));
return pNode;
}
//按先序次序输入二叉树中结点的值(一个字符),#记为空树
void CreateBiTree(BiTree &T,const TElemType *&str)
{
if (*str != '#')
{
T = buyNode();
T->data = *str;
CreateBiTree(T->leftChild, ++str);
CreateBiTree(T->rightChild, ++str);
}
}
中序序列是一定要存在的,不然无法确定二叉树。
1.2 先序和中序
算法如下:
- 在先序序列中取出根结点,到中序序列中找到根结点,这一过程可封装为findPos;
- 分别对先序、中序序列进行划分,确定左右子树,缩减问题规模,进行处理。——分治法思想的精髓所在。
编程小技巧,将一个函数API实现只进行参数合法性检查,在函数体内部转调其他函数。
static int findPos(TElemType pe, const TElemType *istr, int n) //找出先序最左边的元素在中序中的位置。
{
int pos = -1;
for (int i = 0; i < n; ++i)
{
if (pe == istr[i])
{
pos = i;
break;
}
}
return pos;
}
//先序和中序事务处理,不负责参数合法性检查,只实现以PI方法创建二叉树
static BiTree CreateBiTree_PI_call(const TElemType *pstr, const TElemType *istr, int n)
{
TElemType pivotkey = pstr[0];
int pos = findPos(pivotkey, istr, n);
if (pos == -1)
return NULL;
BiTNode *s = buyNode();
s->data = pivotkey;
s->leftChild = CreateBiTree_PI_call(pstr+1,istr,pos);
s->rightChild = CreateBiTree_PI_call(pstr+pos+1,istr+pos+1,n-pos-1);
return s;
}
//先序和中序逻辑判断
BiTree CreateBiTree_PI(const TElemType *pstr, const TElemType *istr, int n)
{
if (pstr == NULL || istr == NULL || n < 1)
return NULL;
else
return CreateBiTree_PI_call(pstr, istr, n);
}
测试用例:
int main()
{
const TElemType *pstr = "ABCDEFGH";
const TElemType *istr = "CBEDFAGH";
int n = strlen(pstr);
BiTree bt = CreateBiTree_PI(pstr, istr, n);
PreOrderTraverse(bt, Visit);
return 0;
}
1.3 中序和后序
算法如下
- 从后序序列中取出当前树的根结点,到中序序列中找到对应位置。
- 根据中序序列的特点,根结点的左右分别是左子树部分和有子树部分。
虽然是递归求解,但我们应该层次分析问题 。从经验来看,递归问题我们只需要考虑一层就是了。
//按照后序序列找到根结点在中序序列中的位置。
static int findPos_IS(TElemType se, const TElemType *istr)
{
int pos = -1;
int n = strlen(istr);
for (int i = 0; i < n; ++i)
{
if (se == istr[i])
{
pos = i;
break;
}
}
return pos;
}
//istr---中序序列;sstr---后序序列;n---当前序列包含的元素个数
static BiTree CreateBinaryTree_IS_call(const TElemType *istr, const TElemType *sstr, int n)
{
BiTree root = NULL;
if (n > 0) //当前序列中的元素个数
{
root = buyNode();
root->data = sstr[n - 1];
int pos = findPos_IS(root->data, istr);
if (pos == -1) //没有找到证明中序和后序序列不匹配,不能确定二叉树,应该终止程序
exit(1);
root->leftChild = CreateBinaryTree_IS_call(istr, sstr, pos);
root->rightChild = CreateBinaryTree_IS_call(istr + pos + 1, sstr + pos, n - pos - 1);
}
return root;
}
//API:根据用户输入的中序序列和后序序列建立二叉树。
BiTree CreateBinary_IS(const TElemType *istr, const TElemType *sstr)
{
int ni = strlen(istr);
int ns = strlen(sstr);
if (istr == NULL || sstr == NULL || ni != ns)
return NULL;
else
return CreateBinaryTree_IS_call(istr, sstr, ni);
}
测试用例:
int main()
{
const TElemType* istr = "CBEDFAGH"; //中序序列CBEDFAGH
const TElemType* sstr = "CEFDBHGA"; //后序序列CEFDBHGA
BiTree root = CreateBinary_IS(istr, sstr);
PreOrderTraverse(root,Visit);
return 0;
}
二、二叉树的遍历
traversing binary tree
如何按某条搜索路径寻访树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。“访问”的含义很广,可以是对结点做各种处理,如输出结点的信息等。若限定先左后右,则只有DLR先序遍历、LDR中序遍历、LRD后序遍历三种。
2.1 先序
prefix-order
- 若二叉树为空,则空操作;否则
- 访问根结点;先序遍历左子树;先序遍历右子树。
void Visit(TElemType e) //访问元素操作
{
cout << e<<" ";
fflush(stdout);
}
//先序遍历---DLR
void PreOrderTraverse(BiTree &T, void(*Visit)(TElemType e))
{
if (T != NULL)
{
Visit(T->data);
PreOrderTraverse(T->leftChild, Visit); //L
PreOrderTraverse(T->rightChild, Visit); //R
}
}
测试用例:
int main()
{
BiTree bt;
const TElemType *str = { "ABC##DE##F##G#H##" };
CreateBiTree(bt,str); //按照先序创建二叉树
PreOrderTraverse(bt, Visit); //按照先序遍历二叉树
return 0;
}
三种遍历方式会一种触类旁通就会其余两种,中序和后序就不贴代码了。
2.2 中序
infix-order
中序遍历实际上是一个结点进栈和出栈的过程。二叉树的形态确定了其结点进栈和出栈的顺序,也确定了其结点的中序序列。
2.3 后序
sufix-order