二叉树_二叉链表存储_前中后遍历_栈:递归非递归遍历_队列:按层遍历

目录:

1.二叉树的存储结构之二叉链表

1.1以先序序列输入二叉树中结点的值,并构建该二叉树!

2.遍历二叉树

2.1前、中、后序的递归遍历算法

2.2前、中、后序的非递归遍历算法(栈)

2.3按层遍历二叉树(队列)

1.二叉树的存储结构之二叉链表

1.1以先序序列输入二叉树中结点的值,并构建该二叉树!

/*二叉树的结点存储结构,二叉链表存储结构*/
typedef struct BiTNode{
	char data;
	struct BiTNode *lchild, *rchild;
}BiTNode,*BiTree;
/*
BiTNode:是结构类型
BiTree:是指向结点BiTNode的指针类型
*/
bool CreateBiTree(BiTree& T)//指针引用 BiTNode*&,即我要在函数内部改变T的值!T的指向地址值
{/*按先序序列输入二叉树的中结点的值,-表示空树。构造二叉链表表示的二叉树T*/
	char ch;
	cin >> ch;
	if (ch == '-') T = nullptr;
	else
	{
		//if (!(T = (BiTNode*)malloc(sizeof(BiTNode))))	exit(0);
		T = new BiTNode;
		T->data = ch;
		CreateBiTree(T->lchild);
		CreateBiTree(T->rchild);
	}
	return true;
}

如:输入:A B C - - D E - G - - F - - -

则构造图1的二叉树:

int _tmain(int argc, _TCHAR* argv[])
{
	BiTree T=nullptr;
	//以二叉列表数据结构存储方式构造二叉树(数据元素为字符char)
	CreateBiTree(T);
	system("pause");
	return 0;
}

输入:A B C - - D E - G - - F - - -

2.遍历二叉树

2.1前、中、后序的递归遍历算法


void PreOrderTraverseRecursive(BiTree T, void(*funcPtr)(BiTree))
{//对采用二叉链表表示的二叉树的先序遍历算法
	/*先序遍历二叉树T的递归算法!*/
	if (T)
	{
		funcPtr(T);
		PreOrderTraverseRecursive(T->lchild, funcPtr);
		PreOrderTraverseRecursive(T->rchild, funcPtr);
	}
	else
	{
		return;
	}
}

void InOrderTraverseRecursive(BiTree T, void(*funcPtr)(BiTree))
{//对采用二叉链表表示的二叉树的中序遍历算法
	/*中序遍历二叉树T的递归算法!*/
	if (T)
	{
		InOrderTraverseRecursive(T->lchild, funcPtr);
		funcPtr(T);
		InOrderTraverseRecursive(T->rchild, funcPtr);
	}
	else
	{
		return;
	}
}

void PostOrderTraverseRecursive(BiTree T, void(*funcPtr)(BiTree))
{//对采用二叉链表表示的二叉树的后序遍历算法
	/*后序遍历二叉树T的递归算法!*/
	if (T)
	{
		PostOrderTraverseRecursive(T->lchild, funcPtr);
		PostOrderTraverseRecursive(T->rchild, funcPtr);
		funcPtr(T);
	}
	else
	{
		return;
	}
}

2.2前、中、后序的非递归遍历算法(栈)

2.2.1前序遍历的非递归算法:以栈模拟递归的过程:

void PreOrderTraverseLoop(BiTree T, void(*funcPtr)(BiTree))
{
	stack<BiTree> biTreeStack;
	BiTree p;
	if (T)
	{
		biTreeStack.push(T);
		while (!biTreeStack.empty())
		{
			p = biTreeStack.top();
			biTreeStack.pop();
			if (p)//因为p有可能是某个叶子结点的孩子为空指针。
			{
				funcPtr(p);
				biTreeStack.push(p->rchild);//先把右孩子压入栈
				biTreeStack.push(p->lchild);//再把左孩子压入栈
				//出栈顺序反之,先出左孩子结点
			}
		}
	}
}

2.2.2中序遍历的非递归算法:以栈模拟递归的过程:

void InOrderTraverseLoopE1(BiTree T, void(*funcPtr)(BiTree))
{
	stack<BiTree> biTreeStack;
	BiTree p;
	if (T)
	{
		biTreeStack.push(T);
		while (!biTreeStack.empty())
		{
			while ((p = biTreeStack.top()) != nullptr)
			{//走到左尽头
				biTreeStack.push(p->lchild);
			}
			biTreeStack.pop();//空指针退栈,因会把叶子节点的左孩子(为空)压进去,要退栈

			if (!biTreeStack.empty())
			{
				/*弹出栈顶元素,并访问该节点,即访问子树的根结点,
				最左端的叶子结点可看出其左右子树都为空的子树根结点*/
				p = biTreeStack.top();
				biTreeStack.pop();
				funcPtr(p);
				/*然后将其右孩子进栈*/
				biTreeStack.push(p->rchild);
			}//if循环结束
		}//while循环结束
	}
	else
	{
		return;
	}
}
/*E2版本的中序遍历结构更加清晰、易懂!*/
void InOrderTraverseLoopE2(BiTree T, void(*funcPtr)(BiTree))
{/*二叉树T采用二叉链表存储结构,中序遍历T的非递归算法,对每一个元素调用funcPtr函数*/
	stack<BiTree> biTreeStack;//存储结点指针的栈
	BiTree p;
	p = T;
	while (p || !biTreeStack.empty())
	{
		if (p)
		{//根指针进栈,然后遍历左子树
			biTreeStack.push(p);
			p = p->lchild;//这里也是一直走到最下边的结点
		}
		else
		{//根指针退栈,访问根结点,然后遍历右子树
			p=biTreeStack.top();
			biTreeStack.pop();
			funcPtr(p);
			p = p->rchild;
		}
	}
}

 2.2.3后序遍历的非递归算法:以栈模拟递归的过程:
/*
二叉树的后序遍历--非递归实现
https://www.cnblogs.com/rain-lei/p/3705680.html

https://www.cnblogs.com/rain-lei/p/3705680.html
leetcode中有这么一道题,非递归来实现二叉树的后序遍历。
二叉树的后序遍历顺序为,root->left, root->right, root,
因此需要保存根节点的状态。显然使用栈来模拟递归的过程,
但是难点是怎么从root->right转换到root。

方法1:判断是否轮到栈顶p访问法,设立刚访问结点指针last
对于节点p可以分情况讨论
1. p如果是叶子节点,直接访问(输出)
2. p如果有孩子,且孩子没有被访问过,则按照右孩子,左孩子的顺序依次入栈
3. p如果有孩子,而且孩子都已经访问过,则访问p节点
如何来表示出p的孩是否都已经访问过了呢?
最暴力的方法就是对每个节点的状态进行保存,
这么做显然是可以的,但是空间复杂度太大了。
我们可以保存最后一个访问的节点last,
如果满足 (p->right==NULL && last ==p->left) || last=p->right,
那么显然p的孩子都访问过了,接下来可以访问p
*/

二叉树的后序遍历法1:区别栈顶结点p是否该访问了之设立刚访问结点指针法

void PostOrderTraverseE1(BiTree T, void(*funcPtr)(BiTree))
{
	if (!T)
		return;
	stack<BiTree> biTreeStack;//存储结点指针的栈
	BiTree p = T;
	BiTree last = T;
	biTreeStack.push(p);
	while (!biTreeStack.empty())
	{
		p = biTreeStack.top();
		/*情况1:如果p是叶子结点,其左右子树都为空,则可直接访问p;
		情况2:如果满足 (p->right==NULL && last ==p->left) || last=p->right,
		那么显然p的孩子都访问过了,接下来可以访问p
		如果p的右子树为空,并且p的左子树已经访问过了,即(p->right==NULL && last ==p->left)
		那么就可以访问p了
		如果p的右子树也访问过了即last=p->right,也可以访问p了
		
		*/
		if ((p->lchild == nullptr&&p->rchild == nullptr) || (p->rchild == nullptr&&last == p->lchild) || (last == p->rchild))
		{
			funcPtr(p);//访问p
			last = p;//将刚才访问的结点标记为p
			biTreeStack.pop();//p出栈
		}
		else
		{
			if (p->rchild)
			{//如果右子树非空,则右子树结点进栈
				biTreeStack.push(p->rchild);
			}
			if (p->lchild)
			{//如果左子树非空,则左子树结点进栈
				biTreeStack.push(p->lchild);
			}
		}
	}
}

二叉树的后序遍历法2:区别栈顶结点p是否该访问了同一个结点两次压入两次弹出法:

/*
法2:每个结点两次压入法
其实我们希望栈中保存的从顶部依次是root->left, root->right, root,
当符合上面提到的条件时,就进行出栈操作。有一种巧妙的方法可以做到,
对于每个节点,都压入两遍,在循环体中,每次弹出一个节点赋给p,
如果p仍然等于栈的头结点,说明p的孩子们还没有被操作过,
应该把它的孩子们加入栈中,否则,访问p。
也就是说,第一次弹出,将p的孩子压入栈中,第二次弹出,访问p。
*/

void PostOrderTraverseE2(BiTree T, void(*funcPtr)(BiTree))
{
	if (T == NULL) return;

	BiTree p = T;
	stack<BiTree> sta;
	sta.push(p);
	sta.push(p);
	while (!sta.empty())
	{
		p = sta.top(); 
		sta.pop();
		if (!sta.empty() && p == sta.top())
		{
			if (p->rchild) sta.push(p->rchild), sta.push(p->rchild);//C:逗号,运算符
			if (p->lchild) sta.push(p->lchild), sta.push(p->lchild);
		}
		else
		{
			funcPtr(p);
		}
	}
}

2.3按层遍历二叉树(队列)

参考[1]《剑指offer第三版》P172:面试题32从上到下打印二叉树

void levelOrderTraverse(BiTree pTreeRoot, void(*p)(BiTree))
{/*二叉树采用二叉列表的结构,pTreeRoot是指向跟结点的指针,p是函数指针
 按层遍历该二叉树*/
	if (!pTreeRoot)
		return;
	std::deque<BiTNode*> dequeTreeNode;
	dequeTreeNode.push_back(pTreeRoot);
	while (!dequeTreeNode.empty())
	{//队列非空时继续循环!
		BiTNode* pNode = dequeTreeNode.front();//取得队头结点
		dequeTreeNode.pop_front();//弹出队头结点
		//cout << pNode->data << " ";
		p(pNode);//调用函数指针,打印该结点的值
		if (pNode->lchild)
		{//如果有左孩子,就将左孩子结点指针进队
			dequeTreeNode.push_back(pNode->lchild);
		}
		if (pNode->rchild)
		{//如果有右孩子,就将右孩子结点指针进队
			dequeTreeNode.push_back(pNode->rchild);
		}
	}
}

以上函数的测试例子:

#include "stdafx.h"
#include<iostream>
#include<deque>
#include<stack>

using namespace std;

/*二叉树的结点存储结构,二叉链表存储结构*/
typedef struct BiTNode{
	char data;
	struct BiTNode *lchild, *rchild;
}BiTNode,*BiTree;

void printVal(BiTree T)
{
	cout << T->data << " ";
}

int _tmain(int argc, _TCHAR* argv[])
{
	BiTree T=nullptr;
	//以二叉列表数据结构存储方式构造二叉树(数据元素为字符char)
	CreateBiTree(T);

	//按层遍历二叉树
	cout << "按层遍历二叉树:" << endl;
	levelOrderTraverse(T, printVal);
	cout << endl;
	//先序遍历(递归版本)
	cout << "先序遍历(递归版本):" << endl;
	PreOrderTraverseRecursive(T, printVal);
	cout << endl;
	//中序遍历(递归版本)
	cout << "中序遍历(递归版本):" << endl;
	InOrderTraverseRecursive(T, printVal);
	cout << endl;
	//后序遍历(递归版本)
	cout << "后序遍历(递归版本):" << endl;
	PostOrderTraverseRecursive(T,printVal);
	cout << endl;

	//先序遍历(非递归版本)
	cout << "先序遍历(非递归版本):" << endl;
	PreOrderTraverseLoop(T, printVal);
	cout << endl;

	//中序遍历(非递归版本1)
	cout << "中序遍历(非递归版本1):" << endl;
	InOrderTraverseLoopE1(T, printVal);
	cout << endl;

	//中序遍历(非递归版本2)
	cout << "中序遍历(非递归版本2):" << endl;
	InOrderTraverseLoopE2(T, printVal);
	cout << endl;

	//后续遍历(非递归版本1)
	cout << "后续遍历(非递归版本1)" << endl;
	PostOrderTraverseE1(T, printVal);
	cout << endl;
	//后续遍历(非递归版本2)
	cout << "后续遍历(非递归版本2)" << endl;
	PostOrderTraverseE2(T, printVal);
	cout << endl;


	system("pause");
	return 0;
}

/*
输入:
A B C - - D E - G - - F - - -

输出:
按层遍历二叉树:
A B C D E F G
先序遍历(递归版本):
A B C D E G F
中序遍历(递归版本):
C B E G D F A
后序遍历(递归版本):
C G E F D B A
先序遍历(非递归版本):
A B C D E G F
中序遍历(非递归版本1):
C B E G D F A
中序遍历(非递归版本2):
C B E G D F A
后续遍历(非递归版本1)
C G E F D B A
后续遍历(非递归版本2)
C G E F D B A
请按任意键继续. . .

*/

二叉树_二叉链表存储_前中后遍历_栈:递归非递归遍历_队列:按层遍历

数据结构

猜你喜欢

转载自blog.csdn.net/m0_37357063/article/details/81982646