文章目录
二叉树的遍历是二叉树操作中的一个基本且重要的操作,它指的是按照某种规则访问二叉树中的每个节点,并且每个节点仅被访问一次。常见的二叉树遍历方法包括前序遍历(Preorder Traversal)、中序遍历(Inorder Traversal)、后序遍历(Postorder Traversal)以及层次遍历(Level-Order Traversal)。
一、前序遍历
前序遍历的顺序是:先访问根节点,然后遍历左子树,最后遍历右子树。在遍历左子树和右子树时,也按照同样的前序遍历方式。
递归方法
void preorderTraversal(TreeNode* root) {
if (root == NULL) return;
// 访问根节点
printf("%d ", root->val);
// 遍历左子树
preorderTraversal(root->left);
// 遍历右子树
preorderTraversal(root->right);
}
非递归方法
// 前序遍历二叉树(非递归)
void preorderTraversalIterative(struct TreeNode* root) {
if (root == NULL) return;
// 创建栈存储节点
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 1000);
if(stack == NULL)
{
printf("fail to create satck!\n");
return;
}
// 栈中节点数量
int cnt = 0;
// 根节点入栈
stack[cnt++] = root;
while (cnt) {
// 取栈顶元素
struct TreeNode* temp = stack[cnt-1];
// 出栈
cnt--;
// 访问节点
printf("%d ", temp->val);
// 先右后左入栈,保证左子树先遍历
if(temp->right) stack[cnt++] = temp->right;
if(temp->left) stack[cnt++] = temp->left;
}
free(stack);
}
非递归方法是用堆内存去模拟一个栈(数据先入后出),用这个栈模拟前序遍历过程。具体的实现过程见http://t.csdnimg.cn/O4v1jO4v1j 。
二、中序遍历
中序遍历的顺序是:先遍历左子树,然后访问根节点,最后遍历右子树。在遍历左子树和右子树时,也按照同样的中序遍历方式。
递归方法
// 中序遍历二叉树(递归)
void inorderTraversal(struct TreeNode* root) {
if (root == NULL) return;
// 遍历左子树
inorderTraversal(root->left);
// 访问根节点
printf("%d ", root->val);
// 遍历右子树
inorderTraversal(root->right);
}
非递归方法
// 中序遍历二叉树(非递归)
void inorderTraversalIterative(struct TreeNode* root) {
if (root == NULL) return;
struct TreeNode **stack = malloc(sizeof(struct TreeNode *) * 1000);
if(stack == NULL)
{
printf("fail to create satck!\n");
return;
}
int cnt = 0;
struct TreeNode* curr = root;
while (curr != NULL || cnt != 0) {
// 遍历到最左节点
while (curr != NULL) {
stack[cnt++] = curr;
curr = curr->left;
}
// 访问节点
curr = stack[cnt-1];
cnt--;
printf("%d ", curr->val);
// 转向右子树
curr = curr->right;
}
free(stack);
}
类似前序遍历这里同样模拟栈的存储方法(先入后出)去实现中序遍历过程,具体的入栈出栈过程见http://t.csdnimg.cn/O4v1j
三、后续遍历
递归方法
// 后序遍历(递归)
void postorderTraversal(struct TreeNode* root)
{
if (root == NULL) return;
// 遍历左子树
postorderTraversal(root->left);
// 遍历右子树
postorderTraversal(root->right);
// 访问根节点
printf("%d ", root->val);
}
非递归方法
// 后序遍历(非递归)
void postorderTraversalIterative(struct TreeNode* root)
{
// 创建栈存储节点
struct TreeNode** stack1 = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 1000);
struct TreeNode** stack2 = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 1000);
if(stack1 == NULL || stack2 == NULL)
{
printf("fail to create satck!\n");
return;
}
// 栈中节点数量
int cnt1 = 0, cnt2 = 0;
// 根节点入栈
stack1[cnt1++] = root;
while (cnt1) {
// 取栈顶元素
struct TreeNode* temp = stack1[cnt1-1];
stack2[cnt2++] = temp; // 栈顶元素入栈2
// 出栈
cnt1--;
// 先右后左入栈,保证左子树先遍历
if(temp->right) stack1[cnt1++] = temp->right;
if(temp->left) stack1[cnt1++] = temp->left;
}
// 取出栈2中的元素
while(cnt2)
{
printf("%d ", stack2[--cnt2]->val);
}
free(stack1);
free(stack2);
}
后序遍历的非递归实现相对复杂,这里的思路是创建两个栈先使用非递归方式实现前序遍历,然后将结果压入另一个栈,最后依次出栈逆序输出。
四、层序遍历
层次遍历是按照二叉树的层次从上到下、从左到右遍历。
非递归实现(常用队列)
// 层次遍历二叉树
void levelOrderTraversal(struct TreeNode* root) {
if (root == NULL) return;
// 创建一个队列用于存储节点
struct TreeNode** queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 1000); // 假设队列大小足够
int front = 0, rear = 0;
// 根节点入队
queue[rear++] = root;
while (front < rear) {
// 访问队首元素
struct TreeNode* temp = queue[front++];
printf("%d ", temp->val);
// 如果左子节点存在,则左子节点入队
if (temp->left) {
queue[rear++] = temp->left;
}
// 如果右子节点存在,则右子节点入队
if (temp->right) {
queue[rear++] = temp->right;
}
}
// 释放队列内存(如果需要)
free(queue);
}
实现思路是用堆内存以及两个头尾指针去模拟队列(先入先出),再用队列模拟层次遍历。其过程可参考如下图示。