闭着眼学二叉树的前序,中序,后序遍历思想,

一,二叉树的链式结构

       二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是 链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面课程 学到高阶数据结构如红黑树等会用到三叉链。

d326c6103c3f450b8acba9ff51989893.jpeg

6b5416e1d4104e9990d229c68631ac5f.png

typedef int BTDataType;    //数据类型
typedef struct BinaryTreeNode
{
	BTDataType data;            //数据域
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

二,二叉树的前序遍历

         学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

1151aadce88e4bd7b13e4be1be1c6f9e.png

 前序遍历用人话说,就是,若二叉树为空,则什么都不做,不是空,就,

先访问根节点,然后访问左子树,最后再访问右子树。

之前都太抽象了,现在找个二叉树解析一下:

36d34acd37634e17aacf4d1e88f7a9f3.png

 这颗二叉树的前序遍历,是

第一步,先访问根节点1,

第二步然后访问左子树,下图,因为这是一颗二叉树,访问得先访问根节点2

0e6def41abbd4ddeaf972aad665e672d.png

第三步,访问这颗二叉树的左子树,也就是4,根节点也是4,左右子树为空

第四步,访问4的左子树,也就是

第五步,刚刚访问到空了,该到右子树了,也是

第六步,4这颗二叉树已访问完毕,该回到上一级了,以2为根的二叉树的右子树5,根节点也是5

第七步,该访问5的左子树,为

第八步,该访问5的右子树,也为

刚刚这颗子树彻底访问完毕了,又该回到上一级了,到了整颗二叉树的右子树了,

5d86a8b0ace84dd49f682ea516fd68c9.png

 废话不多说,跟上面子树一个道理,

第九步,访问的是3,

第十步,访问3的左子树6

第十一步,访问6的左子树,

第十二步,访问6的右子树,

第十三步,回到上一级,访问3的右子树7

第十四步,访问7的左子树,

第十五步,访问7的右子树,

综合所述前序:1-->2-->4-->空-->空-->5-->空-->空-->3-->6-->空-->空-->7-->空-->空

(许多人认为节点为空就不访问了? 不是这样的,他也会访问,只是你看不到罢了)

下面是代码的实现,用递归的思想去解决。用链式结构写二叉树,首选队列结构,下图是队列结构

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

typedef BTNode* QDataType;
typedef struct QueueNode            //链式结构:表示队列
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue                      //队列的结构
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;

void QueueInit(Queue* pq);        //初始化队列
                                 
void QueueDestory(Queue* pq);               //销毁队列
                                               
void QueuePush(Queue* pq,QDataType x);           //队尾入队列

void QueuePop(Queue* pq);                     //队头出队列

QDataType QueueFront(Queue* pq);              //获取队列头元素

QDataType QueueBack(Queue* pq);                 //获取队列队尾元素

bool QueueEmpty(Queue* pq);              //检测队列是否为空,如果为空返回非零结果,如果非空返回0

int QueueSize(Queue* pq);                  //获取队列中有效元素个数

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
	pq->size = 0;
}

void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* del = cur;
		cur = cur->next;
		free(del);
	}
	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode =(QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	else
	{
		newnode->data = x;
		newnode->next = NULL;
	}
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* del = pq->head;
		pq->head = pq->head->next;
		free(del);
		del = NULL;
	}
	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	
	return pq->head == NULL && pq->tail == NULL;

}

int QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	int n = 0;
	while (cur)
	{
		++n;
		cur = cur->next;
	}
	return pq->size;
}

接下来则是前序遍历代码,用递归的思想。 

void PreOrder(BTNode* root)   //二叉树前序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

看看视觉效果:就用上面那组数据,

71b4e67f14654ee997ea4fe57a456caf.png

看看结果,和上面分析的是不是一模一样,(傲娇脸)! 

fba15ca81e4141aab88bb3a464f0a9c8.png

和上述分析的是不是一模一样,

三,二叉树的中序遍历

中序遍历用人话说,就是,若二叉树为空,则什么都不做,不是空,就,

先访问左子树,然后访问根节点,最后再访问右子树。和前序遍历有区别的,根节点跑到第二位了

还是刚刚那个图

36d34acd37634e17aacf4d1e88f7a9f3.png

第一步:先访问左子树,然后还没完,这颗左子树的左子树4,然后4的左子树,空,

0e6def41abbd4ddeaf972aad665e672d.png

第二步:访问4这个子树的根,也就是4

第三步:访问4这个子树的右子树,

第四步:4这个子树访问完毕,返回上一级子树,访问根节点2

第五步:根节点2访问完毕,该到右子树5了,访问右子树5的左子树,

第六步:访问5的根节点,是5

第七步:访问5的右子树,

第八步:刚刚那颗小的二叉树访问结束,回到这颗大的二叉树了,访问根节点,1

36d34acd37634e17aacf4d1e88f7a9f3.png

第九步:访问大二叉树的右子树,下图,先访问这个子树的左子树6的左子树,

5d86a8b0ace84dd49f682ea516fd68c9.png

第十步:访问6的根节点,6

第十一步:访问6的右子树,

第十二步:访问这个子树的根节点,3

第十三步:访问右子树7的左子树,空

第十四步:访问7的根节点,7

第十五步:访问7的右子树,空

综合所述前序:空-->4-->空-->2-->空-->5-->空-->1-->空-->6-->空-->3-->空-->7-->空

下面是代码的实现,用递归的思想去解决。用链式结构写二叉树,依旧是刚刚的二叉树,用中序遍历,

void InOrder(BTNode* root)         //二叉树中序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

看看视觉效果:就用上面那组数据,

40786db038504031b8b82b2a56f589a7.png

 看看结果,和上面分析的是不是一模一样,(傲娇脸)! 

8084da51531b4ab982b9fe69138f7393.png

四,二叉树的后序遍历

后序遍历用人话说,就是,若二叉树为空,则什么都不做,不是空,就,

先访问左子树,再访问右子树,最后访问根节点。和前序遍历有区别的,根节点跑到第三位了

还是刚刚那个图

36d34acd37634e17aacf4d1e88f7a9f3.png

第一步:先访问左子树,然后还没完,这颗左子树的左子树4,然后4的左子树,空,

0e6def41abbd4ddeaf972aad665e672d.png

第二步:访问4这个子树的右子树,也就是

第三步:访问4这个子树的根节点,也就是4

第四步:访问这颗子树的右子树5,必须先访问5这个子树的左子树,也就是,

第五步:接着访问5这个子树的右子树,也是,

第六步:访问5这个子树的根节点,也就是,5

第七步:访问上面这个二叉树的根节点,也就是2

第八步:这个左子树访问完毕,应该到了右子树,那必须先访问左子树6的左子树,

5d86a8b0ace84dd49f682ea516fd68c9.png

第九步:接着访问6的右子树,也是,

第十步:访问6的根节点,也就是,6

第十一步:6这个子树访问完毕,应该到了,右子树7,先访问7的左子树,

第十二步:接着访问7的右子树,

第十三步:访问7的根节点,也就是,7

第十四步:访问这颗右子树的根节点,3

第十五步:这个大二叉树的左右子树全部访问完毕,最后访问根节点 ,1

综合所述前序:空-->空-->4-->空-->空-->5-->2-->空-->空-->6-->空-->空-->7-->3-->1

下面是代码的实现,用递归的思想去解决。用链式结构写二叉树,依旧是刚刚的二叉树,用后序遍历,

void PostOrder(BTNode* root)          //二叉树后序遍历
{
	{
		if (root == NULL)
		{
			printf("NULL ");
			return;
		}
		
		PostOrder(root->left);
		PostOrder(root->right);
		printf("%d ", root->data);
	}
}

看看视觉效果:就用上面那组数据,

2809126dc4fa43b9943ef12188637b96.png

 看看结果,和上面分析的是不是一模一样,(傲娇脸)!  

 c20d3068f84b42c291c2f714bfb45773.png

各位老爷,小手点个赞再走!!!!!

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值