#include
#include
typedef int datatype;
typedef struct node {
datatype data;
struct node *left, *right;
}BitTree;
//*************************Begin*************
//添加辅助栈的结构:主要是用来保存树的节点
//简言之,这个就是用来存树节点的栈
typedef BitTree BitTreeNode;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
//-——————————修订V0.01——changed by dingst
//BitTreeNode data;那么一定是侵入式的,为什么=》逐个深拷贝每一个分量
//只要是BitTreeNode* Addr;在32位中,这个值永远是4字节
//保存的都是传入的源节点的地址值的一个副本(c/C++语言,都是指拷贝形式)
typedef struct linknode {
BitTreeNode* _NodeAddrCopy;
struct linknode* pNext;
}LinkStack;
LinkStack* InitLkStack(void) {
LinkStack* p = NULL;
p = (LinkStack*)malloc(sizeof(LinkStack));
if (p != NULL)
p->pNext = NULL;
return p;
}//建立一个带头节点的链表作为链栈
BOOL IsEmptyLkMazeStack(LinkStack* top) {
return (top->pNext == NULL);
}
LinkStack* PushLkStack(LinkStack* top, BitTreeNode* elem) {
LinkStack* p = NULL;
//深拷贝的bug修订,如果传入的是空指针,那么不能访问,修订人丁宋涛
//elem就是将当前树节点的地址值考一份:比如0x00d500578,我们现在
//栈就是把这个0x00d500578拷贝一份下来,放到我们_NodeAddrCopy
//及时栈节点出栈,此时也是我么你的_NodeAddrCopy这个变量被释放
//与原先的树节点没有关系,不会影响
//elem是这个函数的形参,它将调用者的实参拷贝一份放入到elem中
//所以elem就是这个0x00d500578
//我们用p->_NodeAddrCopy = elem;的操作
//就把0x00d500578保存下来了,此时他就是原节点地址的一个副本
if (elem != NULL)
{
p = (LinkStack*)malloc(sizeof(LinkStack));
//todo:我们用新建节点,对传入的二叉树节点做一个深拷贝
//changed by dingst,我们要做非侵入式的设计
/*p->data.data = elem->data;
p->data.left = elem->left;
p->data.right = elem->right;*/
p->_NodeAddrCopy = elem;
p->pNext = top; //p节点尾插入lkStack
top = p;//top栈顶指针上移
}
//我们思考,既然我们允许空指针进入到我们的栈结构,那么我们如何修订
//我们的出栈?
//top为空以为着?
//意味着是否和栈空条件冲突了?
//如果我们让我们的NULL进入栈空间,那么出栈也会造成空指针
//这意味着当前的节点不动
return top;
}
//出栈:pData是传入的一个“临时”空间用来接收出栈的节点信息。
LinkStack* PopLkStack(LinkStack* top, BitTreeNode *pData) {
LinkStack* p;
if (top != NULL) {
//*pData = top->data;
//----changed by dingst
/*pData->data = top->data.data;
pData->left = top->data.left;
pData->right = top->data.right;*/
pData = top->_NodeAddrCopy;
//pData实际上深拷贝了一份节点信息,所以释放栈中节点,不会影响
//节点数据
p = top;
top = p->pNext;
free(p);
}
return top;
}
//*************************End***************
//辅助队列Q,这是用来存关系的
BitTree* Q[16];//这是一个指针数组,它将缓存节点的地址,因为这个地址将以
//left域,或者right域进入二叉链表,它本身不维护i,2i,2i+1的关系
//他的关系通过front,rear来维护
//按照直观的建立,我们首先想到的是层次法
//我们根据层次,来逐一将二叉树建立
//输入的数组是按照完全二叉树的编号规则设立,即
//数组角标反映了节点位置关系(存联系)
//逐个扫描数组,直到所有的节点都存取完毕之后,就结束
//约定,0表示数组当前元素为空,最后一个节点标志是-999
BitTree* CreateBinTree(int arr[]) {
int i = 1;
//只要我们没有扫描完元素,那么这个二叉链表就没有完成
//只要扫描,我们就malloc一个节点,然后把这个节点存入left域或者right域
int front = 1, rear = 0;
BitTree* root = NULL;
BitTree* s;//暂存节点
while (arr[i] != -999) {
s = NULL;
if (arr[i] != 0) {//意味着这个不是空节点,那么我们就要分配空间
s = (BitTree*)malloc(sizeof(BitTree));
s->data = arr[i];//存数值
s->left = NULL;
s->right = NULL;
}
//要让我们新节点入队,进入缓存,等待分配双亲的left域和right域
Q[++rear] = s;
if (rear == 1)
{
root = s;
}
else {
if (s != NULL && Q[front]) {
if (rear % 2 == 0) {
Q[front]->left = s;
}
else {
Q[front]->right = s;
}
}
if (rear % 2 == 1)
front++;
}
i++;
}
return root;
}
//树遍历问题:非线性结构的输出问题:前序,中序,后序
void preorder(BitTree* t) {
if (t) {
//根左右
printf("%d ", t->data);
preorder(t->left);
preorder(t->right);
}
}
void NonRecrvPreOrder(BitTree* root) {
LinkStack* pTop = NULL;
//BitTreeNode TempNode;//用来接栈中对应的二叉树节点的
//-----change by dingst
BitTreeNode* pNode; //我要让pNode推进树遍历,而且pNode就是
//真实地址的副本
pTop = InitLkStack();
//pTop = PushLkStack(pTop, root);
//---change by dingst
pNode = root;
pTop = PushLkStack(pTop, pNode);
while (!IsEmptyLkMazeStack(pTop)) {
pNode = pTop->_NodeAddrCopy;//178行的这个赋值操作,就将真实的节点地址赋给了pNode这个函数的局部变量
pTop = PopLkStack(pTop, pNode);
if (pNode != NULL) {
printf("%d ",pNode->data);
//对右子树压栈
pTop = PushLkStack(pTop, pNode->right);
pTop = PushLkStack(pTop, pNode->left);
}
}
}
void inorder(BitTree *t) {
if (t) {
inorder(t->left);
printf("%d ", t->data);
inorder(t->right);
}
}
/*
中序就是左根右,这就意味着,当我们从根节点出发,应当首先走向左孩子
,而根的左孩子,是根的左子树的根节点,因此
又必须,继续访问其左孩子,直到左孩子为空
当这个节点访问之后,按照相同的规则,访问其右子树
我们借助的栈,就应该按照左根右来压栈
对于任意节点p
1 如果其左孩子不为空,则将p入栈,然后将p的左孩子置为当前的p,然后
再对p进行相同的处理
2 若左孩子为空,则取栈顶元素进行出栈操作,访问当前栈顶节点,然后
将当前的p置为栈顶节点的右孩子
3 直到p为NULL为止
*/
void NonRecvInorder(BitTree *root) {
LinkStack* pTop = NULL;
BitTreeNode TempNode;//用来接收栈中的对应的二叉树的节点
pTop = InitLkStack();
BitTree *p = root;
while (p != NULL || !IsEmptyLkMazeStack(pTop)) {
while (p != NULL) {
pTop = PushLkStack(pTop, p);
p = p->left;
}
if (!IsEmptyLkMazeStack(pTop)) {
//因为此时我们在输出的节点信息,就一定要从栈中
//取出元素
//这个是取出的元素的_NodeAddrCopy分量就是树中节点的地址
//因此对这个节点使用->就可以获得真实的数据
printf("%d ", pTop->_NodeAddrCopy->data);
p = pTop->_NodeAddrCopy; //因为我们设计的p就是要走完root全部节点,因此
//p必须获得的是树节点的真实地址
pTop = PopLkStack(pTop, &TempNode);//当我们把左子树的左叶子节点走完以后,
//此时栈顶一定是这个左孩子的根,所以根据左根右,这个根要出栈
//----change by dingst
//p = &TempNode; 因为tmpenode是bitnode的对象,
//所以tmpenode的地址值可以赋值给p这个指针变量,但是这个tempnode的这个地址值
//是肯定不等于真实树节点的地址值的
p = p->right;//走向根的右孩子
}
}
}
void postorder(BitTree* t) {
if (t) {
//左右根
postorder(t->left);
postorder(t->right);
printf("%d ", t->data);
}
}
/*
后序:左右根
根节点只有在左孩子和右孩子都访问以后才能访问
因此,对于任何一个节点,都将其入栈,如果p不存在左孩子和右孩子,直接访问
或者p存在左孩子或者右孩子,但是他的左孩子和右孩子都已经被访问过了
同样的,这个节点也可以直接访问.
如果,上述条件都不满足,就要将这个p的右孩子,左孩子依次入栈
这样就能保证,每次取栈顶元素的时候,左孩子都在右孩子之前被访问,
左右孩子访问之后,根才被访问
*/
void NonRecvPostorder(BitTree* root) {
BitTree* cur;//当前节点==>cur 保存的就是树节点的地址
BitTree* pre = NULL;//前一次访问的节点
LinkStack* pTop = NULL;
BitTreeNode TempNode;//注意我们就是用tempnode来接里面的内容,他是不能参与地址比较的,他的地址一定和我们栈中的地址不一样
pTop = InitLkStack();
pTop = PushLkStack(pTop, root);
while (!IsEmptyLkMazeStack(pTop)) {
cur = pTop->_NodeAddrCopy;//拿到栈中深拷贝获得的节点
//在访问中,由于我们是深拷贝树的节点,因此,pre和cur的地址不一样,我们只能比较器元素内容
//小技巧:利用短路,确保程序的可靠性
if ((cur->left == NULL && cur->right == NULL) ||
(pre != NULL && (pre == cur->left|| pre == cur->right))
) {
printf("%d ", cur->data);
pTop = PopLkStack(pTop, &TempNode);
pre = cur;
}
else {
if (cur->right != NULL)
pTop = PushLkStack(pTop, cur->right);
if (cur->left != NULL)
pTop = PushLkStack(pTop, cur->left);
}
}
}
//求叶子节点:树的遍历是一种非线性遍历,在业务编程中,从实现角度上
//用递归遍历三个方法之一,判断节点属性,就可以完成暴力搜索树
int LeafCount = 0;
void LeafCount_DLR(BitTree* t) {
if (t) {
if(t->left == NULL && t->right == NULL){
printf("%d ", t->data);
LeafCount++;
}
LeafCount_DLR(t->left);
LeafCount_DLR(t->right);
}
}
//因为我们现在能够不重复,不遗漏获取每一个节点,只有前中后序3种方法,所以,我们首先先写出
//先序递归获取树的深度
int hight = 0;//它可以在多个函数之间共享
//前序是根左右,我们把每一个树从root出发的树,可以看出左右两个子树
//无论我们如何遍历,我们前进到每一个节点都是潜在的根节点。
//我们可以认为每到一个新节点,就是到了一个新树,
//如果说,我们对新节点的发现,我们就认为到了一个新层,那么我们累加
//记录就可以了
//我用level作为当前树的高度
//当我用root,0传入的时候,就意味着已经有第一层的计数起始器
//我们用打擂台的思路,用h比对,就可以完成记录
/*
找一组数中的最大值
for(){
if(max < a[i])
max =a[i]
}
*/
int TreeDepth_DLR(BitTree *pNode, int level) {
if (pNode) {
level++;
if (level > hight)
hight = level;//这一就意味伴随着level的不断深入,我们的hight始终获得的是树中最深的层次
printf("%d ", pNode->data);
//从327开始到333行,我们认为这个函数的终止条件完成了
//根据我们前面的观点,递归函数本身就是答案
//因此hight就应该接递归函数的返回值
hight = TreeDepth_DLR(pNode->left, level);
hight = TreeDepth_DLR(pNode->right, level);//因为所有节点的遍历都是跟左右,所以和当前树节点传入的同时,它当前做在的层数我们要传入
}
return hight;
}
//根据前序、中序重建二叉树
BitTree* reConstructBinTree(int pre[], int startPre, int endPre,
int in[], int startIn, int endIn) {
//如果起始的下标大于结束的下标,那么就退出
if (startPre > endPre || startIn > endIn) {
return NULL;
}
//前序找到根节点
BitTree* pNode = (BitTree*)malloc(sizeof(BitTree));
pNode->left = NULL;
pNode->right = NULL;
pNode->data = pre[startPre];//前根当中的首元素一定是当前子树的根节点
for (int i = startIn;i <= endIn;i++) {
if (in[i] == pre[startPre]) {
//i-startIn就是左子树节点的个数
//i -1就是中序遍历左子树的终点
pNode->left = reConstructBinTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1);
//前序右子树的遍历起始值:startPre+i-startIn+1
//中序遍历右子树的起始值:i+1,endIn(i理解成你第几次切刀)
pNode->right = reConstructBinTree(pre, i - startIn + startPre + 1, endPre, in, i + 1, endIn);
}
}
return pNode;
}
//求根到所有叶子节点的路径
BitTree* Paths[10] = { 0 };
void OutPutPath(){
int i = 0;
while (Paths[i] != NULL) {
printf("%d ", Paths[i]->data);
i++;
}
printf("\n");
}
//求路径的函数
void LeavesPath(BitTree* tree, int level) {
if (tree == NULL)
return;
Paths[level] = tree;//这句话就意味着,只要走到这里,这个子树的根就被记录下来
//求路径,意味着找到叶子节点,就输出
if ((tree->left == NULL) && (tree->right == NULL)) {
//当我确定到已经从树根节点root走到了一个叶子节点,那么它以前所有的根节点都存在paths里面
Paths[level + 1] = NULL;//通过level传递了路线信息
OutPutPath();
return;
}
LeavesPath(tree->left, level + 1);
LeavesPath(tree->right, level + 1);
}
//判断两个二叉树是否相等
//提问如果两个二叉树的前序遍历相等,能否说明,这两个树相等?
//答案是否定的,只有这两个树的 A前序=B前序,A中序=B中序才可以。
BOOL isEqualTree(BitTree* t1, BitTree* t2) {
if (t1 == NULL && t2 == NULL)
return TRUE;
if (!t1 || !t2)//t1,t2一个空一个不空,肯定不相同
return FALSE;
if (t1->data == t2->data)
return isEqualTree(t1->left, t2->left) && isEqualTree(t1->right, t2->right);
else
return FALSE;
}
//使用二叉链表建立二叉搜索树:注意,我们目前这个树不支持相同元素
BitTree* insertIntoBinSearchTree(BitTree* root, int node) {
BitTree* newNode;
BitTree* currentNode;
BitTree* parentNode;
newNode = (BitTree*)malloc(sizeof(BitTree));
newNode->data = node;
newNode->left = NULL;
newNode->right = NULL;
if (root == NULL)
return newNode;
else {
currentNode = root;//然后我们对当前节点进行定位
//注意,传入root是根节点,我们要根据根节点,按照左小右大的逻辑找到节点位置
//首先通过while找到新节点的位置
while (currentNode != NULL) {
parentNode = currentNode;
if (currentNode->data > node) {
currentNode = currentNode->left;
}
else {
currentNode = currentNode->right;
}
}
//其次,我们将父节点和子节点做连接
if (parentNode->data > node) {
parentNode->left = newNode;
}
else {
parentNode->right = newNode;
}
}
return root;
}
BitTree* create_LkBinTree(int data[], int length) {
BitTree* root=NULL;
int i;
for (i = 0;i < length;i++) {
root = insertIntoBinSearchTree(root, data[i]);
}
return root;
}
//二叉树镜像:交换两个子树
void SwapTree(BitTree* tree) {
BitTree* pTemp;
pTemp = tree->left;
tree->left = tree->right;
tree->right = pTemp;
}
void ExchangeSubTree(BitTree* tree) {
if (tree == NULL)
return;
else {
SwapTree(tree);
ExchangeSubTree(tree->left);
ExchangeSubTree(tree->right);
}
}
int main(void) {
int arr[17] = { 0,6,3,8,
2,5,7,9,
0,0,4,0,
0,0,0,10,
-999
};
BitTree *root = CreateBinTree(arr);
/*printf("递归形式的先跟遍历\n");
preorder(root);
printf("\n非递归形式的先跟遍历\n");
NonRecrvPreOrder(root);*/
//NonRecvInorder(root);
/*printf("递归形式的后根遍历\n");
postorder(root);
printf("\n非递归形式的后根遍历\n");
NonRecvPostorder(root);*/
//******************BEGIN:树的节点与深度****************
/*LeafCount_DLR(root);
printf("\n当前书总共有%d个叶子节点", LeafCount);*/
//printf("\n当前树有%d层", TreeDepth_DLR(root, 0));
给定一个二叉树,我要分别得到它的左子树的深度和右子树的深度
我们用复用的思想,很容易就得到了内容
注意,虽然复用的思想是好的,当时要注意全局变量的副作用
要对全局变量清零
//hight = 0;
//printf("\n当前树的左子树有%d层\n", TreeDepth_DLR(root->left, 0));
//hight = 0;
//printf("\n当前树的右子树有%d层\n", TreeDepth_DLR(root->right, 0));
当我们在使用递归遍历中,利用全局变量进行传递的时候
函数的复用应当注意全局变量的置0
//******************END 树的节点与深度****************
//************Beign 二叉树重建
//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
/*int pre[] = { 1,2,4,7,3,5,6,8 };
int in[] = { 4,7,2,1,5,3,8,6 };
BitTree* rootNew;
rootNew = reConstructBinTree(pre, 0, 7, in, 0, 7);
preorder(rootNew);
printf("\n");
inorder(rootNew);*/
//*************End 二叉树重建
//***********Begin 获取根节点到叶子节点
//LeavesPath(root, 0);//0表示层级的传递,只要根据level延伸就能找到每一棵子树的根节点
//************END获取根节点到叶子节点
//***************Begin 判断两颗二叉树是否一致
//如果,输入的数字序列前后不一致,那么使用二叉搜索树建立的也不是同一棵树
/*int arrary[] = { 1,2,3 };
BitTree* tree1 = create_LkBinTree(arrary, 3);
int brr[] = { 1,2,3 };
BitTree* tree2 = create_LkBinTree(brr, 3);
printf("\n");
inorder(tree1);
printf("\n");
inorder(tree2);
printf("\n%d", isEqualTree(tree1, tree2));
*/
//***************End 判断两颗二叉树是否一致
//*************Begin 二叉树镜像:交换一个二叉树的左右子树
int arrary[] = { 5,4,7,2,3,6,8 };
BitTree* tree1 = create_LkBinTree(arrary, 7);
printf("\n");
inorder(tree1);
ExchangeSubTree(tree1);
printf("\n");
inorder(tree1);
//*************End 二叉树镜像:交换一个二叉树的左右子树
getchar();
return 0;
}