前言
思路及算法思维,指路 代码随想录。
题目来自 LeetCode。
day 14,周二,好困~
题目详情
[144] 二叉树的前序遍历
题目描述
解题思路
前提:二叉树的前序遍历,中左右
思路:递归实现 or 迭代实现。
重点:递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。
代码实现
C语言
递归法
确定递归函数的参数和返回值:void traversal(struct TreeNode *root, int *ret, int *idx);
确定终止条件:if (root == NULL) { return ; };
确定单层递归逻辑:ret[(*idx)++] = root->val;// 中 traversal(root->left, ret, idx);// 左 traversal(root->right, ret, idx);// 右。
/**
* 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 traversal(struct TreeNode *root, int *ret, int *idx)
{
if (root == NULL)
{
return;
}
ret[(*idx)++] = root->val; // 中
traversal(root->left, ret, idx); // 左
traversal(root->right, ret, idx); // 右
return ;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
*returnSize = 0;
traversal(root, ret, returnSize);
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().
*/
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
memset(ret, 0, sizeof(int) * 100);
*returnSize = 0;
// 栈
struct TreeNode *stack[100];
memset(stack, 0, sizeof(struct TreeNode *) * 100);
int idx = 0;
// 将根节点压栈
stack[idx++] = root;
while (idx > 0)
{
struct TreeNode *cur = stack[--idx];
// 判空
if (cur != NULL)
{
// 前序遍历,保存结点值
ret[(*returnSize)++] = cur->val;
// right压栈,空节点不入栈
if (cur->right != NULL)
{
stack[idx++] = cur->right;
}
// left压栈,空节点不入栈
if (cur->left != NULL)
{
stack[idx++] = cur->left;
}
}
}
return ret;
}
统一迭代法
了解前序、中序、后序遍历的迭代法实现之后,会发现,3种遍历的迭代实现,除了前序和后序的实现有点关联,中序遍历的迭代实现,稍微有些不一样。
针对这种情况,在栈中,将要处理的节点(子树的根节点)放入栈中后,紧接着放入一个null指针作为标记,这样,每次遍历到null指针的时候,就知道下一个从栈中弹出的元素,就是要输出的元素。
/**
* 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) * 100);
memset(ret, 0, sizeof(int) * 100);
*returnSize = 0;
// 栈
struct TreeNode *stack[100];
memset(stack, 0, sizeof(struct TreeNode *) * 100);
int idx = 0;
// 判断是否为空树
if (root != NULL)
{
stack[idx++] = root;
}
// 非空树时进入循环
while (idx > 0)
{
// 取栈顶元素
struct TreeNode *cur = stack[--idx];
// 当前结点不为null指针时, 由于前序遍历,依次对 右节点、左节点、根节点、null指针进行入栈
if (cur != NULL)
{
// right结点,空节点不入栈
if (cur->right != NULL)
{
stack[idx++] = cur->right;
}
// left结点,空节点不入栈
if (cur->left != NULL)
{
stack[idx++] = cur->left;
}
// 该结点压栈,并放入空指针标识已遍历的结点
stack[idx++] = cur;
stack[idx++] = NULL;
}
// 当前结点为null指针时,说明栈中下一个节点需要保存输出
else
{
// 找到标记点,直接打印后一结点的val
ret[(*returnSize)++] = (stack[--idx])->val;
}
}
return ret;
}
[94] 二叉树的中序遍历
题目描述
解题思路
前提:二叉树的中序遍历, 左中右
思路:递归实现 or 迭代实现
重点:递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。
代码实现
C语言
递归法
确定递归函数的参数和返回值:void traversal(struct TreeNode *root, int *ret, int *returnSize);
确定终止条件:if (root == NULL) { return ; };
确定单层递归逻辑:traversal(root->left, ret, returnSize);//左 ret[(*returnSize)++] = root->val;//中 traversal(root->right, ret, returnSize);//右。
/**
* 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 traversal(struct TreeNode *root, int *ret, int *returnSize)
{
if (root == NULL)
{
return ;
}
traversal(root->left, ret, returnSize); // 左
ret[(*returnSize)++] = root->val; // 中
traversal(root->right, ret, returnSize); // 右
return ;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
*returnSize = 0;
traversal(root, ret, returnSize);
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().
*/
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
*returnSize = 0;
int idx = 0;
struct TreeNode *stack[100];
struct TreeNode *cur = root;
// 判断遍历是否完成: 栈为非空 或 当前结点为指向null结点,即为遍历未完成
while ((idx > 0) || (cur != NULL))
{
// 当前结点存在,则入栈,继续遍历其左子树
if (cur != NULL)
{
stack[idx++] = cur; // 中
cur = cur->left; // 左
}
// 当前结点为null,说明左子树已经遍历完成,输出栈顶的根节点,并遍历其右子树
else
{
ret[(*returnSize)++] = (stack[--idx])->val; // 输出节点数值
cur = stack[idx]->right; // 右
}
}
return ret;
}
统一迭代法
将要处理的节点(子树的根节点)放入栈中后,紧接着放入一个null指针作为标记。
/**
* 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) * 100);
*returnSize = 0;
int idx = 0;
struct TreeNode *stack[100];
// 判断空树
if (root != NULL)
{
stack[idx++] = root;
}
// 非空树
while (idx > 0)
{
// 取栈顶节点
struct TreeNode *cur = stack[--idx];
// 当前结点不为null指针时, 由于中序遍历,依次对 右节点、根节点、null指针、左节点进行入栈
if (cur != NULL)
{
// right,空节点不入栈
if (cur->right)
{
stack[idx++] = cur->right;
}
// 压栈当前结点,并放入空指针标识已遍历的结点
stack[idx++] = cur;
stack[idx++] = NULL;
// left,空节点不入栈
if (cur->left)
{
stack[idx++] = cur->left;
}
}
// 当前结点为null指针时,说明栈中下一个节点需要保存输出
else
{
// 取栈顶的数值
ret[(*returnSize)++] = (stack[--idx])->val;
}
}
return ret;
}
[145] 二叉树的后序遍历
题目描述
解题思路
前提:二叉树的后序遍历 左右中
思路:递归实现 or 迭代实现。
重点:递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。
代码实现
C语言
递归法
确定递归函数的参数和返回值:void traversal(struct TreeNode *root, int *ret, int *returnSize);
确定终止条件:if (root == NULL) { return ; };
确定单层递归逻辑:traversal(root->left, ret, returnSize);//左 traversal(root->right, ret, returnSize);//右 ret[(*returnSize)++] = root->val;//中。
/**
* 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 traversal(struct TreeNode *root, int *ret, int *returnSize)
{
if (root == NULL)
{
return ;
}
traversal(root->left, ret, returnSize); // 左
traversal(root->right, ret, returnSize); // 右
ret[(*returnSize)++] = root->val; // 中
return ;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
*returnSize = 0;
traversal(root, ret, returnSize);
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().
*/
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
*returnSize = 0;
struct TreeNode *stack[100];
int idx = 0;
stack[idx++] = root;
while (idx > 0)
{
struct TreeNode *cur = stack[--idx];
// 判断是否为空树
if (cur != NULL)
{
// 结点值
ret[(*returnSize)++] = cur->val;
// left,空节点不入栈
if (cur->left)
{
stack[idx++] = cur->left;
}
// right,空节点不入栈
if (cur->right)
{
stack[idx++] = cur->right;
}
}
}
// 反转结果数组
for (int i = 0; i < (*returnSize / 2); i++)
{
int tmp = ret[i];
ret[i] = ret[*returnSize - 1 - i];
ret[*returnSize - 1 - i] = tmp;
}
return ret;
}
统一迭代法
将要处理的节点(子树的根节点)放入栈中后,紧接着放入一个null指针作为标记。
/**
* 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* postorderTraversal(struct TreeNode* root, int* returnSize) {
int *ret = (int *)malloc(sizeof(int) * 100);
*returnSize = 0;
struct TreeNode *stack[100];
int idx = 0;
// 判断空树
if (root != NULL)
{
stack[idx++] = root;
}
// 不为空树
while (idx > 0)
{
struct TreeNode *cur = stack[--idx];
// 当前结点不为null指针时, 由于后序遍历,依次对 根节点、null指针、右节点、左节点进行入栈
if (cur != NULL)
{
// 根节点入栈,并放入空指针标识已遍历的结点
stack[idx++] = cur;
stack[idx++] = NULL;
// right,空节点不入栈
if (cur->right)
{
stack[idx++] = cur->right;
}
// left,空节点不入栈
if (cur->left)
{
stack[idx++] = cur->left;
}
}
// 当前结点为null指针时,说明栈中下一个节点需要保存输出
else
{
ret[(*returnSize)++] = (stack[--idx])->val;
}
}
return ret;
}
今日收获
- 二叉树的前序、中序、后序遍历,分别使用 递归法、迭代法、统一风格的迭代法 实现。
- 递归实现三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归逻辑。