声明::个人原创代码,转载需附上本文链接
题目:
题目分析:
本题要求实现二叉树的锯齿形层次遍历,这是一种特殊的层次遍历方式,其特点是每一层的节点值存储顺序在从左到右和从右到左之间交替变化。这种遍历方式在处理需要交替展示层级数据的场景中非常有用,例如在图形界面中展示树形结构的层级信息。
与普通的层序遍历不同,锯齿形层次遍历需要在每一层之间改变节点的存储顺序。这增加了遍历的复杂性,因为我们需要在每一层结束时改变节点的添加顺序。
思路分析:
为了实现锯齿形层次遍历,我们需要考虑如何控制每一层的遍历方向。这里我们可以使用两个栈来模拟层次遍历的过程,并通过一个标志变量来控制每一层的遍历方向。
使用栈实现锯齿形层次遍历
层序遍历的核心思想是使用栈来存储每一层的节点。通过逐层访问节点,可以保证按照从上到下、从左到右和从右到左交替的顺序访问所有节点。
双栈法
为了实现层序遍历,可以使用两个栈交替存储当前层和下一层的节点。这样可以避免在访问当前层时修改栈,从而简化代码逻辑。具体步骤如下:
-
初始化两个空栈,
one
用于存储当前层的节点,two
用于存储下一层的节点。 -
将根节点加入
one
栈。 -
当
one
或two
栈不为空时,执行以下操作:-
从
one
栈中取出所有节点,访问它们的值,并将下一层的节点加入到two
栈中。 -
将
one
栈中的节点值存储在一个一维数组中,并将该数组添加到结果二维数组中。 -
交换
one
和two
栈,以便下一次循环时处理下一层节点。
-
逐层访问
从根节点开始,逐层访问节点,并将每一层的节点值存储在一个一维数组中。每访问完一层,将下一层的节点加入栈,直到所有层都被访问完毕。
节点左右子树添加顺序分析
在锯齿形层次遍历中,控制节点左右子树的添加顺序是实现交替遍历的关键。我们需要在每一层之间改变子节点的添加顺序,以确保每一层的遍历方向按照要求交替变化。
-
当前层从左到右遍历:在这种情况下,我们首先添加左子节点,然后添加右子节点。这样做是因为在下一层的遍历中,我们需要从右到左遍历,而栈是后进先出(LIFO)的数据结构,所以先添加的节点会先被弹出。
-
当前层从右到左遍历:在这种情况下,我们首先添加右子节点,然后添加左子节点。这与从左到右遍历的情况相反,因为我们希望在下一层能够从右到左遍历。
代码分析:
TreeNode结构体
定义了二叉树节点的结构体,包含节点值和左右子节点指针。
GetOneLevel函数
从当前层的栈中取出所有节点,访问它们的值,并将下一层的节点加入到下一层的栈中。返回当前层的节点值数组。这个函数通过一个 while
循环遍历当前层的所有节点,对于每个节点,取出其值并添加到结果数组中,然后检查是否有左右子节点,如果有则将它们加入到下一层的栈中。
zigzagLevelOrder函数
使用两个栈交替存储当前层和下一层的节点,逐层访问二叉树的节点,并将每一层的节点值存储在一个一维数组中,最终返回一个二维数组。这个函数首先检查根节点是否为空,如果为空则直接返回空数组。然后初始化两个栈和一个结果数组。使用一个 while
循环来遍历所有层,每次循环中,通过调用 GetOneLevel
函数处理一层节点,并将结果添加到结果数组中。循环结束后,返回结果数组。
源码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
#include <stack>
class Solution {
public:
/**
* 处理当前层的节点并准备下一层的节点
* @param AccessNodes 当前层的节点栈
* @param StorageNodes 用于存储下一层节点的栈
* @param flag 遍历方向标志:1表示从左到右,0表示从右到左
* @return 当前层的节点值列表
*/
vector<int> GetOneLevel(stack<TreeNode*>& AccessNodes, stack<TreeNode*>& StorageNodes,
const int& flag)
{
TreeNode* temp; // 临时节点指针
vector<int> OneLevelAns; // 存储当前层的节点值
while(!AccessNodes.empty()){ // 处理当前层所有节点
temp = AccessNodes.top(); // 获取栈顶节点
AccessNodes.pop(); // 弹出栈顶节点
OneLevelAns.push_back(temp->val); // 记录节点值
// 根据方向标志处理子节点
if(flag == 1){ // 从左到右遍历时
// 先左后右压入栈,这样下一层弹出顺序就是右到左
if(temp->left != nullptr) StorageNodes.push(temp->left);
if(temp->right != nullptr) StorageNodes.push(temp->right);
}
else{ // 从右到左遍历时
// 先右后左压入栈,这样下一层弹出顺序就是左到右
if(temp->right != nullptr) StorageNodes.push(temp->right);
if(temp->left != nullptr) StorageNodes.push(temp->left);
}
}
return std::move(OneLevelAns); // 移动语义返回结果
}
/**
* 二叉树的锯齿形层序遍历
* @param root 二叉树根节点
* @return 按层序锯齿形排列的节点值列表
*/
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
stack<TreeNode*> one, two; // 两个栈交替使用
int flag = 1; // 初始方向:从左到右
vector<vector<int>> ans; // 存储最终结果
if(root == nullptr) return ans; // 空树直接返回
one.push(root); // 根节点入栈
while(1){ // 无限循环,通过break退出
if(flag == 1){ // 当前方向为从左到右
ans.push_back(GetOneLevel(one, two, flag)); // 处理当前层
flag = 0; // 切换方向
if(two.empty()) break; // 如果下一层没有节点,结束遍历
}
else{ // 当前方向为从右到左
ans.push_back(GetOneLevel(two, one, flag)); // 处理当前层
flag = 1; // 切换方向
if(one.empty()) break; // 如果下一层没有节点,结束遍历
}
}
return ans; // 返回最终结果
}
};