在上期的二叉树基础理论中提到了二叉树的前中后序遍历,这一期就简单的实现一下。下一期会实现一下层序遍历。
三种遍历的递归和迭代的相似和差别
三种遍历的递归法大差不差,可迭代法却有点差别。前序遍历和后序遍历的迭代法有点相似,可中序遍历的迭代法却不同于前两种。
迭代法其实就是借助栈来实现的,具体的注意事项和代码实现请接着看下面。
前序遍历
递归法
前序遍历的顺序的中左右。一般的递归会有三要素:1、确定递归函数的参数和返回值。2、确定终止条件。3、确定单层递归的逻辑。
以二叉树的前序遍历为例子:1、参数是数组,根结点,数组的长度(leetcode刷的多的读友们会很了解。)返回值是空,直接修改数组就好了,不需要返回值2、终止条件是结点为空。3、这里的单层逻辑就是中左右。
下面是C语言的代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
void preorder(struct TreeNode* root, int* arr, int* returnSize){
if(!root){
return;
}
arr[(*returnSize)++] = root->val;//中
preorder(root->left, arr, returnSize);//左
preorder(root->right, arr, returnSize);//右
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* arr = (int*)malloc(sizeof(int) * 101);//这里的101是树的结点数。
*returnSize = 0;
preorder(root, arr, returnSize);
return arr;
}
迭代法
前序遍历的迭代法是借助栈来实现的。
栈的特性是先进后出,前序遍历是中左右,先将右结点放入栈中,再放入左结点。出来的顺序就是左-右。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* ret = (int*)malloc(sizeof(int) * 101);
*returnSize = 0;
if(!root){
return ret;
}
struct TreeNode* stack[101];
//也可以struct TreeNode* stack = (struct TreeNode*)malloc(sizeof(struct TreeNode) * 101)
int top = 0;
stack[top++] = root;
while(top){
struct TreeNode* node = stack[--top];
ret[(*returnSize)++] = node->val;
if(node->right){
stack[top++] = node->right;//右
}
if(node->left){
stack[top++] = node->left;//左
}
}
return ret;
}
中序遍历
递归法
和前序遍历类似,就是单层逻辑变成了左中右
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
void inorder(struct TreeNode* root, int* arr, int* returnSize){
if(!root){
return;
}
inorder(root->left, arr, returnSize);//左
arr[(*returnSize)++] = root->val;//中
inorder(root->right, arr, returnSize);//右
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int* arr = (int*)malloc(sizeof(int) * 101);
*returnSize = 0;
inorder(root, arr, returnSize);
return arr;
}
迭代法
为什么说中序遍历的迭代和前后遍历不同呢,因为前后遍历的顺序都是中间结点在前,是需要先放入数组的结点。而中序是左右中,处理的结点和遍历的顺序不一致。也就是说,需要不断遍历树,直到最左边的叶子结点,然后再开始处理结点(也就是放入数组中)。所以就需要改变一下。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int* ret = (int*)malloc(sizeof(int) * 101);
*returnSize = 0;
if(!root){
return ret;
}
struct TreeNode* stack[101];
int top = 0;
struct TreeNode* node = root;
while(node || top){
if(node){
stack[top++] = node;//这里就是不断先左将结点入栈,直到到达左叶子结点
node = node->left;
}
else{
node = stack[--top];//到了左叶子结点之后就开始弹栈
ret[(*returnSize)++] = node->val;//弹出的结点就是需要处理的结点,将它存入数组中
node = node->right;//然后再访问右结点。在将右结点入栈。(执行上面的选择语句)
}
}
return ret;
}
后序遍历
递归法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
void postorder(struct TreeNode* root, int* arr, int* returnSize){
if(!root){
return;
}
postorder(root->left, arr, returnSize);//左
postorder(root->right, arr, returnSize);//右
arr[(*returnSize)++] = root->val;//中
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* arr = (int*)malloc(sizeof(int) * 200);
*returnSize = 0;
postorder(root, arr, returnSize);
return arr;
}
迭代法
后序遍历的顺序是左右中,我们只需要将顺序变为中右左,然后倒序一下数组就ok了。
而前序的顺序是中左右,所以后序遍历的迭代和前序遍历非常像。只需要将左右结点入栈的顺序改变一下就行了。前序左右结点的入栈顺序是:右-左,所以后序就是:左-右。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
void Backarr(int* arr, int* returnSize){
for(int i = 0, j = *returnSize - 1; i < j; i++, j--){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* ret = (int*)malloc(sizeof(int) * 101);
*returnSize = 0;
if(!root){
return ret;
}
struct TreeNode* stack[101];
int top = 0;
stack[top++] = root;
while(top){
struct TreeNode* node = stack[--top];
ret[(*returnSize)++] = node->val;
if(node->left){
stack[top++] = node->left;//左
}
if(node->right){
stack[top++] = node->right;//右
}
}
Backarr(ret, returnSize);
return ret;
}
总结
三种遍历的递归大差不差,但迭代却有区别,那么在下一期将会更新三种遍历的迭代统一格式。