链式二叉树的7种遍历方式

本文介绍了链式二叉树的创建,并详细阐述了包括递归和非递归在内的7种遍历方法:前序、中序、后续、层序遍历以及非递归的前序、中序、后序遍历。通过递归和栈模拟等方式,解释了遍历过程中的节点访问逻辑。
摘要由CSDN通过智能技术生成

在说链式二叉树的遍历之前,我们先来说一下如何创建一个链式二叉树。创建一个链式二叉树,需要一个已知的字符串,这个字符串必须支持创建二叉树的规则。比如:
我们通过递归来实现二叉树的创建,开始先向左走,如果遇到#,就返回,然后向右走,直到遇到#,就返回一个NULL,这样,就完成了一个链式二叉树的创建。

这里先将除了主要函数外的其他调用到的函数放在这里,供查阅

头文件

#ifndef _BTREE_H__
#define _BTREE_H__

#include<stdio.h>
#include<stdlib.h>

typedef char BTDataType;

typedef struct BinaryTreeNode 
{
	BTDataType data;
	struct BinaryTreeNode* lchild;
	struct BinaryTreeNode* rchild;
}BTNode;

typedef BTNode* QUDataType;

typedef BTNode* STDataType;


typedef struct Stack
{
	STDataType* _a;
	int _top;
	int _capacity;

}Stack;

typedef struct QueueNode 
{ 
	struct QueueNode* _next;
	QUDataType _data;
}QueueNode;

typedef struct Queue
{
	QueueNode *head;
	QueueNode *rear;
}Queue;

BTNode* BinaryTreeCreate(BTDataType* src);

void BinaryTreePrevOrder(BTNode* root); 
void BinaryTreeInOrder(BTNode* root); 
void BinaryTreePostOrder(BTNode* root);

void BinaryTreeLevelOrder(BTNode* root);
void BinaryTreePrevOrderNonR(BTNode* root);
void BinaryTreeInOrderNonR(BTNode* root);
void BinaryTreePostOrderNonR(BTNode* root);

void BIaryTreeLevelPrint(BTNode* root, int num);

#endif//_BTREE_H__
BTNode* BinaryTreeCreate(BTDataType* src)//链式二叉树的创建
{
	static int n = 0;
	if (src[n] == '#')
	{
		n++;
		return NULL;
	}
	BTNode *cur = (BTNode*)malloc(sizeof(BTDataType));
	cur->data = src[n];
	n++;

	cur->lchild = BinaryTreeCreate(src);
	cur->rchild = BinaryTreeCreate(src);

	return cur;
}

void QueueInit(Queue* qu)//队列的初始化
{
	qu->head = qu->rear = (QueueNode *)malloc(sizeof(QueueNode));
}
void QueuePop(Queue* qu)//出对头
{
	qu->head = qu->head->_next;
}
void QueueDestory(Queue* qu)//销毁队列
{
	free(qu->head);

}
void QueuePush(Queue* qu, QUDataType x)//队列中进入元素
{
	qu->rear->_data = x;
	qu->rear->_next = (QueueNode *)malloc(sizeof(QueueNode));
	qu->rear = qu->rear->_next;   
	qu->rear->_data = NULL;
}

STDataType StackTop(Stack * st)//拿取栈顶
{
	return st->_a[st->_top - 1];
}
void StackInit(Stack *st)//栈的初始化
{
	st->_a = (STDataType)malloc(sizeof(STDataType)* 10);
	st->_capacity = 10;
	st->_top = 0;

}
void StackPop(Stack *st)//退栈
{
	st->_top--;
}
void StackPush(Stack *st,STDataType  x)//压栈
{
	if (st->_top == st->_capacity - 1)
	{
		realloc(st->_a, sizeof(STDataType)* 2 * st->_capacity);
	}
	st->_a[st->_top] = x;
	st->_top++;

}
void StackDestory(Stack *st)//销毁栈
{
	free(st->_a);
}

二叉树的链式结构的7种遍历方法

递归遍历:

  1. 前序遍历  

前序遍历的思路是,通过递归的方式,让链式二叉树,先不停地从左孩子向下,每遇到一个节点,就进行该节点打印,直到遇到“#”,然后又从右孩子开始向下走,直到遇到“#”,就返回上一个节点,然后又从这个节点开使向右孩子的位置向下。

void BinaryTreePrevOrder(BTNode* root)//递归式的前序遍历
{
	printf("%c ", root->data);
	if (root->lchild)
	{
		BinaryTreePrevOrder(root->lchild);
	}
	if (root->rchild)
	{
		BinaryTreePrevOrder(root->rchild);
	}

}
  1. 中序遍历

中序遍历和前序遍历一样,唯一的差别是,先进型向左孩子的查找,然后进行一个,遇到“#”后,就进行该节点的打印,然后再进行右孩子的查找。

void BinaryTreeInOrder(BTNode* root)//递归式的中序遍历
{
	
	if (root->lchild)
	{
		BinaryTreeInOrder(root->lchild);
	}
	printf("%c ", root->data);
	if (root->rchild)
	{
		BinaryTreeInOrder(root->rchild);
	}
}

 

  1. 后续遍历

和中序,前序一样,也仅仅只是打印的顺序发生了改变。

void BinaryTreePostOrder(BTNode* root)//递归式的后序遍历
{
	
	if (root->lchild)
	{
		BinaryTreePostOrder(root->lchild);
	}
	if (root->rchild)
	{
		BinaryTreePostOrder(root->rchild);
	}
	printf("%c ", root->data);
}

       层序遍历:

       层序遍历是通过一个队列来实现的,通过利用队列先入后出的原则,来完成层序遍历。思路如下:从根节点开始,先将根节点打印,然后如果有左孩子,就将左孩子入队列,如果有右孩子,就将右孩子入队列。然后从队列中出一个元素,将这个元素打印,然后如果有左孩子,就将左孩子入队列,如果有右孩子,就将右孩子入队列,就这样,如果有一个孩子是NULL;那就看另外一个孩子,如果都是NULL,那就只进行一个出队列的打印就好了,这样,层层循环,就完成了层序遍历。

void BinaryTreeLevelOrder(BTNode* root)//层序遍历
{
	Queue qu;
	QueueInit(&qu);
	while (root)
	{
		putchar(root->data);

		if (root->lchild)
		{
			QueuePush(&qu, root->lchild);//入队
		}
		if (root->rchild)
		{
			QueuePush(&qu,root->rchild);//入队
		}
		root = qu.head->_data;//拿取对头

		QueuePop(&qu);//出对头
	}

	//QueueDestory(&qu);

}

       非递归的遍历:

 

  1. 前序遍历

用非递归方式的前序遍历,是利用自己模拟一个栈,通过栈的先入后出的原则,进行一个前序遍历。思路如下:我们在一开始,先将根节点打印,然后将根节点的右孩子进栈(右孩子不为NULL),将根节点变为左孩子(左孩子不为NULL),然后继续执行的让根节点的右孩子进栈,打印根节点,直到遇到NULL,这个时候,我们已经将左孩子遍历了一遍,需要进入右孩子了,我们进行一个拿取栈顶的操作,然后让节点变为栈顶的那个元素,同时进行一次退栈,这个时候,拿到地栈顶就是这个叶子节点的根节点的右孩子了,这样进行一个循环,就完成了非递归的前序遍历。

void BinaryTreePrevOrderNonR(BTNode* root)//非递归式的前序遍历
{
	Stack st;
	StackInit(&st);
	while (root)
	{
		putchar(root->data);//先进行一个根节点的打印
		if (root->rchild)
		{
			StackPush(&st,root->rchild);//右孩子进栈
		}
		if (root->lchild)
		{
			root = root->lchild;//左孩子进栈
		}
		else
		{
			if (st._top == 0)//判断在退栈之前 栈是否是空
			{
				break;
			}
			root = st._a[st._top - 1];
			StackPop(&st);
		}
	}

}

 

  1. 中序遍历

思路:中序遍历,是先进行一个根节和根节点所有左孩子的进栈,然后在遇到根节点的左孩子是NULL时,然后根节点变为栈顶的元素,进行一个根节点的打印,进行一次退栈操作,然后去根节点的右孩子的位置,继续进行一个根节点的左孩子进栈操作。就这样,利用一个循环,完成这样的非递归式的中序遍历。

void BinaryTreeInOrderNonR(BTNode* root)//非递归式的中序遍历
{
	BTNode *cur = root;
	Stack st;
	StackInit(&st);

	while (1)
	{
		for (;cur;cur = cur->lchild)//所有根节点的左孩子进栈
		{
			StackPush(&st,cur);
		}
		cur = StackTop(&st);//拿取栈顶

		putchar(cur->data);
		StackPop(&st);//退栈

		cur = cur->rchild;//进入右孩子

		if (!cur)
		{
			if (st._top == 0)//判断在退栈之前 栈是否是空
			{
				break;
			}
		}
	}
	StackDestory(&st);
}
  1. 后序遍历

后续遍历的要求是,在左孩子和右孩子都已经进行了打印后,才打印父亲节点,这样的话,我们就要直到从左孩子回来时,父亲节点被访问的次数,因为父亲节点联系着左右两个孩子节点,二叉树通过左节点回来的根节点,需要经过一个自己的父亲节点,然后才能去到右孩子的位置,因此,我们需要一个标记,来标出,父亲节点被访问的次数。如果父亲节点没有被左孩子返回时访问,这个标记就是0,如果被左孩子返回访问了一次,就记做1。这时,我们可以定义一个数组,这个数组的下表等于栈空间中父亲节点的位置。我们通过进行最初始的根节点的入栈开始,一直向左孩子方向进行入栈操作,直到遇到NULL停止,这个时候,栈顶的下标就代表了自己在数组中的访问次数,我们进行一个拿取栈顶的操作,同时将数组中这个节点的位置,进行一个置1的操作,这样,如果下一次拿去栈顶的时候,如果发现这个栈顶在数组中的数是1的时候,就代表这个栈顶,已经被访问过一次,就进行一个栈顶的打印,这里的打印是一个循环打印,如果根节点及他的父亲节点的标记都是1的话,就依次向上进行打印,直到遇到0的位置停止,每次打印完了就进行一次退栈。最后通过一个循环,就完成了一个非递归式的后序遍历。

void BinaryTreePostOrderNonR(BTNode* root)//非递归式的后序遍历
{
	BTNode *cur = root;
	Stack st;
	int arr[100] = { 0 };//创建一个数组
	StackInit(&st);
	while (1)
	{
		for (;cur;cur = cur->lchild)//所有根节点的左孩子进栈
		{
			StackPush(&st, cur);
			arr[st._top - 1] = 0;
		}

		while (arr[st._top - 1])//关键所在,判断父亲节点是否是的数组元素是否是1,若是,则进行一个循环打印,直到遇到是0的停止
		{
			if (st._top == 0)//判断在退栈之前 栈是否是空
			{
				break;
			}
			cur = StackTop(&st);//拿取栈顶
			putchar(cur->data);
			StackPop(&st);//退栈
		}
		if (st._top == 0)//判断在退栈之前 栈是否是空
		{
			break;
		}
		cur = StackTop(&st);//拿取栈顶
		arr[st._top - 1] = 1;//将该父亲节点的数组元素置1
		cur = cur->rchild;//进入右孩子
	}
	StackDestory(&st);
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉树链式存储结构可以通过定义一个结构体来实现,结构体中包含一个数据域和两个指向左右子树的指针。具体实现如下: ``` typedef struct TreeNode{ int data; struct TreeNode *left; struct TreeNode *right; }TreeNode, *Tree; Tree createTree(){ int data; scanf("%d", &data); if(data == -1){ // -1表示空节点 return NULL; } Tree root = (Tree)malloc(sizeof(TreeNode)); root->data = data; root->left = createTree(); root->right = createTree(); return root; } ``` 以上代码实现了二叉树链式存储结构的创建,其中createTree()函数使用前序遍历方式输入二叉树的节点数据,-1表示空节点。 接下来是二叉树遍历,分别实现前序、中序和后序遍历: ``` void preOrder(Tree root){ if(root == NULL){ return; } printf("%d ", root->data); preOrder(root->left); preOrder(root->right); } void inOrder(Tree root){ if(root == NULL){ return; } inOrder(root->left); printf("%d ", root->data); inOrder(root->right); } void postOrder(Tree root){ if(root == NULL){ return; } postOrder(root->left); postOrder(root->right); printf("%d ", root->data); } ``` 以上代码分别实现了前序、中序和后序遍历,其中preOrder()函数实现了前序遍历,inOrder()函数实现了中序遍历,postOrder()函数实现了后序遍历。 最后是主函数功能菜单的创建,可以使用switch语句实现: ``` int main(){ Tree root = NULL; int choice; do{ printf("1. 创建二叉树\n"); printf("2. 前序遍历\n"); printf("3. 中序遍历\n"); printf("4. 后序遍历\n"); printf("0. 退出\n"); scanf("%d", &choice); switch(choice){ case 1: printf("请输入二叉树的节点数据,-1表示空节点:\n"); root = createTree(); break; case 2: printf("前序遍历结果为:"); preOrder(root); printf("\n"); break; case 3: printf("中序遍历结果为:"); inOrder(root); printf("\n"); break; case 4: printf("后序遍历结果为:"); postOrder(root); printf("\n"); break; case 0: printf("程序已退出!\n"); break; default: printf("输入有误,请重新输入!\n"); break; } }while(choice != 0); return 0; } ``` 以上代码实现了一个简单的二叉树遍历程序,用户可以通过菜单选择需要的功能。注意,在实际使用中,需要在程序结束时释放二叉树的内存空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值