简单二叉树

二叉树的基本结构

typedef char KeyType;
#define END '#'

typedef struct BtNode
{
	BtNode* leftchild;
	BtNode* rightchild;
	KeyType key;
}BtNode,*BinaryTree;

二叉树的建立

//购买节点
BtNode* BuyNode()
{
	BtNode* s = (BtNode*)malloc(sizeof(BtNode));
	if(s == NULL) exit(1);
	memset(s,0,sizeof(s));
	return s;
}
//释放节点
void FreeNode(BtNode* p)
{
	free(p);
}
//根据前序遍历创建二叉树
BtNode* CreateTree1()
{
	BtNode* s = NULL;
	KeyType data;
	cin>>data;
	while( data != END )
	{
		s = BuyNode();
		s->key = data;
		s->leftchild = CreateTree1();
		s->rightchild = CreateTree1();
	}
	return s;
}
//根据前序遍历传参创建二叉树
BtNode* CreateTree2(const KeyType* &str)//传引用保证每次都是访问的是同一个str
{
	BtNode *s = NULL;
	if(str != NULL && *str != END)
	{
		s = BuyNode();
		s->key = *str;
		s->leftchild = CreateTree2(++str);
		s->rightchild = CreateTree2(++str);
	}
	return s;
}
BtNode* CreateTree3(const KeyType **pstr)//传二级指针
{
	BtNode* s = NULL;
	if(**pstr != END && *pstr != NULL)
	{
		s = BuyNode();
		s->key = **pstr;
		s->leftchild = CreateTree3(&++(*pstr));//*pstr指的是指向str的指针加加 往后走 在取地址是因为参数传递的是指向指针pstr的指针
		s->rightchild = CreateTree3(&++(*pstr));
	}
	return s;
}

根据二叉树的前序和中序遍历建立二叉树

根据二叉树的中序和后序遍历建立二叉树

图如下所示

代码如下:

//根据前序中序遍历创建二叉树
/*
由于前序遍历方式为“根左右” 中学遍历方式为“左根右”
那么前序遍历的每一个值都可以在中序遍历结果中找到并且得到当前节点的左右部分数据的范围
*/
//第一步先找前序遍历的每一个值在中序遍历中的位置
int Find_Is(const KeyType* is,int n,KeyType data)
{
	int pos = -1;
	if(is == NULL ||n < 0)
		return pos;

	for(int i = 0 ;i < n ;++i)
	{
		if(is[i] == data)
		{
			pos = i;
			break;
		}
	}
	return pos;
}
BtNode* CreatePI(const KeyType* ps,const KeyType* is,int n)
{
	BtNode* s = NULL;
	if(ps != NULL && is != NULL && n > 0)
	{
		s = BuyNode();
		s->key = ps[0];
		//找到ps中第一个值在中序结果中的位置 那么就能确定此值的左右数据范围
		int pos = Find_Is(is,n,ps[0]);
		if(pos < 0)
			exit(1);
		s->leftchild = CreatePI(ps+1,is,pos);
		s->rightchild = CreatePI(ps+pos+1,is+pos+1,n-pos+1);
	}
	return s;
}
//根据中序后序遍历创建二叉树
BtNode* CreateIL(const KeyType* is,const KeyType* ls,int n)
{
	BtNode* s = NULL;
	if(is != NULL && ls != NULL && n > 0 )
	{
		s = BuyNode();
		s->key = ls[n-1];
		int pos = Find_Is(is,n,ls[n-1]);
		if(pos < 0)
			exit(1);
		s->leftchild = CreateIL(is,ls,pos);
		s->rightchild = CreateIL(is+pos+1,ls+pos,n-pos-1);
	}
	return s;
}

二叉树的遍历 递归代码

//二叉树的前序遍历 递归
void PreOrder(BtNode* ptr)
{
	if(ptr != NULL)
	{
		cout<<ptr->key<<" ";
		PreOrder(ptr->leftchild);
		PreOrder(ptr->rightchild);
	}
}


//二叉树的中序遍历 递归
void InOrder(BtNode* ptr)
{
	if(ptr != NULL)
	{
		InOrder(ptr->leftchild);
		cout<<ptr->key<<" ";
		InOrder(ptr->rightchild);
	}
}



//二叉树的后序遍历 递归
void LastOrder(BtNode* ptr)
{
	if(ptr != NULL)
	{
		LastOrder(ptr->leftchild);
		LastOrder(ptr->rightchild);
		cout<<ptr->key<<" ";
	}
}

二叉树的遍历非递归代码

//二叉树的前序遍历 非递归
/*
利用栈先进先出原则 前序遍历的顺序为根 --》 左 ---》右  
那么先入根 在出根数据打印 在入右 在入左 这样才能先出左
*/
void PreNiceOrder(BtNode* ptr)
{
	if(ptr == NULL)
		return ;
	stack<BtNode*> st;
	st.push(ptr);
	while(!st.empty())
	{
		ptr = st.top();st.pop();
		cout<<ptr->key<<" ";
		if(ptr->rightchild != NULL)
			st.push(ptr->rightchild);
		if(ptr->leftchild != NULL)
			st.push(ptr->leftchild);
	}
	cout<<endl;
}



//二叉树的中序遍历 非递归
/*
先入左子树 直至左子树为空 再取出节点打印 并检查右子树是否存在 并进入
只有当栈为空或者 ptr为空时 树就遍历完了
*/
void InNiceOrder(BtNode* ptr)
{
	if(ptr == NULL)
		return ;
	stack<BtNode*> st;
	while(!st.empty() ||  ptr != NULL)
	{
		while(ptr != NULL)
		{
			st.push(ptr);
			ptr = ptr->leftchild;
		}
		ptr = st.top();st.pop();
		cout<<ptr->key<<" ";
		ptr = ptr->rightchild;
	}
	cout<<endl;
}




//二叉树的后序遍历 非递归
/*
先入左子树 直至左子树为空
出栈后需要用一个tag标记当前节点的右子树是否被访问过 
原因是由于出栈没有父亲无法确定当前节点的右是否被访问
*/
void LastNiceOrder(BtNode* ptr)
{
	if(ptr == NULL)
		return ;
	stack<BtNode*> st;
	BtNode* tag = NULL;
	while( !st.empty() || ptr != NULL)
	{
		while(ptr != NULL)
		{
			st.push(ptr);
			ptr = ptr->leftchild;
		}

		ptr = st.top();st.pop();

		if(ptr->rightchild == NULL ||  ptr->rightchild == tag)
		//为空直接不考虑 直接打印当前节点值 并给当前节点值tag 表明当前节点的左右都已经访问了
	   //如果当前节点的右等于tag 说明当前节点的左右都已经访问过了 直接将当前节点置为tag 表明当前节点已被访问
		{
			cout<<ptr->key<<" ";
			tag = ptr;
			ptr = NULL;
		}
		//如果当前节点右不为空 并且也不等于tag 则表示还未被访问 则将当前节点入栈 继而将其右孩子入栈
		else
		{
			st.push(ptr);
			ptr = ptr->rightchild;
		}
	}
}

二叉树的中序后序遍历 根据节点出栈次数判断

/*
1.当popnum == 1时,证明只是当前节点入栈 它的左和右都没有访问过
2.当popnum == 2时,证明当前节点以及左孩子都已被访问过
3.当popnum == 3时,证明左右都已经被访问过了
*/
//借助节点入栈次数进行树的中序遍历
void StkInOrder(BtNode* ptr)
{
	if(ptr == NULL)
		return ;

	stack<StkNode> st;
	StkNode node;
	st.push(StkNode(ptr));//先入根
	
	while( !st.empty())
	{
		node = st.top(); st.pop();
		if(++node.popnum == 2) // a1 b1 c3
		{
			cout<<node.pnode->key<<" ";
            if(node.pnode->rightchild != NULL)
			{
				st.push(StkNode(node.pnode->rightchild));
			}
		}
		else
		{
			st.push(node);
			if(node.popnum == 1 && node.pnode->leftchild != NULL)
			{
				st.push(StkNode(node.pnode->leftchild));
			}
		}
	}
	cout<<endl;
}




void StkLastOrder(BtNode* ptr)
{
	if(ptr == NULL)
		return ;
	stack<StkNode> st;
	StkNode node;
	st.push(StkNode(ptr));


	while( !st.empty())
	{
		node = st.top();st.pop();
		if(++node.popnum == 3)
		{
			cout<<node.pnode->key<<" ";
		}
		else
		{
			st.push(node);
			//node.popnum==1说明只有当前节点自己入栈了 左右都没考虑 
			if(node.popnum == 1 &&  node.pnode->leftchild != NULL)
			{
				st.push(StkNode(node.pnode->leftchild));
			}
			//node.popnum == 2说明当前节点 和左节点已访问 右还没有被访问
			else if(node.popnum == 2 && node.pnode->rightchild != NULL)
			{
				st.push(StkNode(node.pnode->rightchild));
			}
		}
	}
	cout<<endl;
}
int main()
{
	const KeyType *ps = "ABCDEFGH";
	const KeyType *is = "CBEDFAGH";
	const KeyType *ls = "CEFDBHGA";
	int n = strlen(ps);
	BinaryTree root1 = NULL;
	root1 = CreateIL(is,ls,n);

	cout<<"前序遍历::"<<" ";
	//PreOrder(root1);
	PreNiceOrder(root1);
	cout<<endl;
	cout<<"中序遍历::"<<" ";
	//InOrder(root1);
	//InNiceOrder(root1);
	 StkInOrder(root1);
	cout<<endl;
	cout<<"后序遍历::"<<" ";
	//LastOrder(root1);
	// LastNiceOrder(root1);
	StkLastOrder(root1);
	cout<<endl;
	return 0;
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值