文章目录
- 1.先序遍历
- 定义
- 递归
- 非递归(栈)
- 2.中序遍历
- 定义
- 递归
- 非递归(栈)
- 3.后序遍历
- 定义
- 递归
- 非递归(栈)
- 4.层序遍历
- 定义:
- 代码实现(队列)
二叉树的遍历是通过一定顺序来访问二叉树的所有结点。遍历的方法有四种:先序遍历,中序遍历,后序遍历和层序遍历。
其中前三种一般使用深度优先搜索(DBS)实现,而层次遍历一般用广度优先搜索(BFS)实现。
二叉树表示:
struct TreeNode{
int data;
struct TreeNode*lchild;
struct TreeNode*rchild;
};
注意:
1.访问时访问左子树一定在右子树之前。
2.前,中,后序遍历的非递归形式都是用栈来实现的。
3.层序遍历用队列来实现。
1.先序遍历
定义
对于先序遍历,其访问二叉树的结点的顺序为:
1. 先访问根节点
2. 访问左子树
3. 访问右子树
递归
为了递归实现先序遍历首先得得到两个条件:递归出口和递归式。
其中递归式可由先序遍历的定义直接得到,递归出口为二叉树为空树。
代码有:
void preorderTraversal(struct TreeNode*root){
if (root==NULL){//递归出口,二叉树为空
return;
}
else{
printf("%d\n", root->data);//访问根节点
preorderTraversal(root->lchild);//访问左子树
preorderTraversal(root->rchild);//访问右子树
}
}
递归理解:根据先序遍历的遍历顺序,树不为空时一开始进来(printf)就是访问根节点,再去访问左子树(一开始递归进来就是左子树的根节点),其次访问右子树(一开始递归进来就是右子树的根节点)。
非递归(栈)
结点一开始(根节点)进来先被访问,再走向左子树,将结点入栈(push),直到走到空子树,再将结点出栈(pop),出栈的结点就是原来不是空的结点,再走向右子树。
注意:右边如果为空,不会进入走向左边的循环,直接将结点出栈。
void preorderTraversal(struct TreeNode*root){
struct TreeNode *cur = root;
struct TreeNode *ass[100];//定义一个栈
int top = -1;//栈顶指针
while (cur || top != -1){//如果数不为空或者栈里还有节点
while (cur){//一直走向左子树
printf("%d\n",cur->data);//访问根节点
ass[++top] = cur;//入栈
cur = cur->lchild;
}
cur = ass[top--];//出栈
cur = cur->rchild;//往右走一个结点
}
}
2.中序遍历
定义
对于中序遍历,其访问二叉树的结点的顺序为:
1. 先访问左子树
2. 访问根节点
3. 访问右子树
递归
跟先序递归条件相同,只不过递归式为中序的定义。
void inorderTraversal(struct TreeNode*root){
if (root==NULL){//递归出口,二叉树为空
return;
}
else{
preorderTraversal(root->lchild);//访问左子树
printf("%d\n", root->data);//访问根节点
preorderTraversal(root->rchild);//访问右子树
}
}
递归理解:根据中序遍历的遍历顺序,树不为空时一开始进来先去遍历左子树的左子树,再访问根节点(走到最后一个不为空的叶节点,也可以理解为只有一个结点的树,所以也算是根节点)。再访问左子树的右子树。再访问右子树的左子树,再访问根节点,再访问右子树的右子树。
非递归(栈)
结点一开始走向左子树,将结点入栈(push),直到走到空子树,再将结点出栈(pop),然后访问根节点,再走向右子树,循环。与先序遍历差不多,只是访问根节点时顺序换了。
void inorderTraversal(struct TreeNode*root){
struct TreeNode *cur = root;
struct TreeNode *ass[100];//定义一个栈
int top = -1;//栈顶指针
while (cur || top != -1){//如果数不为空或者栈里还有节点
while (cur){//一直走向左子树
ass[++top] = cur;//入栈
cur = cur->lchild;
}
cur = ass[top--];//出栈
printf("%d\n",cur->data);//访问根节点
cur = cur->rchild;//往右走一个结点
}
}
3.后序遍历
定义
对于先序遍历,其访问二叉树的结点的顺序为:
1. 先访问左子树
2. 访问右子树
3. 访问根节点
递归
递归条件相同,只是递归式变为了后序递归定义
void postorderTraversal(struct TreeNode*root){
if (root==NULL){//递归出口,二叉树为空
return;
}
else{
preorderTraversal(root->lchild);//访问左子树
preorderTraversal(root->rchild);//访问右子树
printf("%d\n", root->data);//访问根节点
}
}
递归理解:根据后序递归遍历顺序,先访问左子树,到空树后,再访问左子树的右子树,再访问根节点。再访问右子树的左子树,到空树,再访问右子树的左子树。再访问根节点。
非递归(栈)
结点一开始往左子树的左子树遍历,并且每个结点入栈,直到空树后,放出栈里的一个结点,此时不访问,先判断是否有右子树。所以这里有两种情况。
1.有右子树:将该结点入栈,走到右节点出。
2.没有右节点,访问该节点,先用一变量保存该节点,将该节点置空。(置空为了不进入向左子树走的循环,直接出栈一节点。保存该节点是因为,出栈的结点的右子树是变量保存结点)。
void postorderTraversal(struct TreeNode*root){
struct TreeNode *cur = root;
struct TreeNode *ass[100];//定义一个栈
int top = -1;//栈顶指针
struct TreeNode *temp=NULL;
while (cur || top != -1){//如果数不为空或者栈里还有节点
while (cur){//一直走向左子树
ass[++top] = cur;//入栈
cur = cur->lchild;
}
cur = ass[top--];//出栈
if(cur->rchild==NULL||cur->rchild==temp){
printf("%d\n",cur->data);//访问根节点
temp=cur;//用一变量保存该节点
cur=NULL;//置空
}
else{
ass[top++]=cur;//该节点入栈
cur = cur->rchild;//往右走一个结点
}
}
}
4.层序遍历
定义:
从上往下,从左往右依次遍历。
代码实现(队列)
基本思路:
- 创建一队列
- 将根节点放入队列
- 取出队列首结点访问它
- 判断左子树是否为空,不为空入队。
- 判断右子树是否为空,不为空入队。
- 循环步骤3,直到队列为空。
typedef struct TreeNode* Elementtype;
struct Queue{
Elementtype *a;
int front;
int rear;
int capacity;
};
struct Queue *creatQueue(){//队列初始化
struct Queue *queue = (struct Queue *)malloc(sizeof(struct Queue));
queue->a = (Elementtype *)malloc(sizeof(Elementtype)*100);
queue->front = 0;
queue->rear = 0;
queue->capacity = 100;
return queue;
}
void AddQ(struct Queue *queue, struct TreeNode *cur){//将结点入队
queue->rear = (queue->rear + 1) % queue->capacity;//循环队列
queue->a[queue->rear] = cur;
}
struct TreeNone *DeleteQ(struct Queue *queue){//将结点出队
queue->front = (queue->front + 1) % queue->capacity;//循环队列
return queue->a[queue->front];
}
bool Isempty(struct Queue *queue){//判断队列是否为空
if (queue->front == queue->rear){
return true;
}
return false;
}
void levelorderTraversal(struct TreeNode *root){
struct TreeNone *cur = root;
struct TreeNone *temp = NULL;//变量保存出队结点
if (!root){
return;
}
struct Queue *queue = creatQueue();//创建一队列
AddQ(queue, cur);//将根结点入队
while (!Isempty(queue)){
temp = DeleteQ(queue);//出队
printf("%d\n", temp->data);//访问
if (!(temp->lchild)){//左子树入队
AddQ(queue, temp->lchild);
}
if (!(temp->rchild)){//右子树入队
AddQ(queue, temp->rchild);
}
}
}