给定一棵二叉树,要求按层次遍历该二叉树,每一层将单独输出一行。
难点就在于每一层的结点输出一行。
本着鄙视递归的潜意识,先用迭代来做,递归的做法放在最后。
类似于广度优先遍历,故采用队列 做为辅助记忆结构。
struct TreeNode
{
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {}
};
本题的难点就在于每一层要单独一行输出。对此,叶劲峰提出了一个算法,其主要特点是在 队列中每一层的最后一个节点之后插入一个傀儡节点,当我们遇到一个傀儡节点时,就输出换行符,因为此时我们知道已经遍历了一层,要开始新的一层了。
叶劲峰的方法比较好地解决了换行判断的问题。但空间复杂度还有下降的空间。我们记k为所有层中的结点最多的那一层的结点数,则他的算法的空间复杂度为O(k+logN)。k一般都为N/2,即空间复杂度为O(N/2+logN)。若给定的N个结点的树高度为N,则其空间复杂度为O(1.5N)。
能不能换种方法,在同样简便的情况下,使空间复杂度在最坏的情况下只有O(0.5N)?
答案是该方法是存在的。
方法说起来很简单,只要仔细想,应该都能想出这种方法来。
定义两个变量curLevelCount和nextLevelCount来分别保存当前层和下一层的结点数。
显然,curLevelCount的初始值为1,因为只有一个根结点,而nextLevelCount由于未知,故置为0.
void levelOrder(TreeNode* root)
{
if(nullptr == root)
return nullptr;
int curLevelCount = 1, nextLevelCount = 0;
std::queue<int> q;
q.push(root);
TreeNode* curPtr = nullptr;
while(!q.empty())
{
curPtr = q.front();
q.pop();
std::cout << curPtr->val << " ";
curLevelCount--;
if(nullptr != curPtr->left)
{
q.push(curPtr->left);
nextLevelCount++;
}
if(nullptr != curPtr->right)
{
q.push(curPtr->right);
nextLevelCount++;
}
if(0 == curLevelCount)
{
endl(std::cout);
curLevelCount = nextLevelCount;
nextLevelCount = 0;
}
}
}
迭代方法结束。
下面开始讲解递归的方法。
递归解法就有一点无脑了。
我们用一个形参来表示当前层数,指定层数的结点才输出。
既然需要指定层数,我们就要获取给定树的高度。因为给定的二叉树并不一定是完全二叉树,所以即使给定了结点总数,也不能直接计算出树的高度。
//计算指定树的高度
int getTreeHeight(TreeNode* root)
{
if(nullptr == root)
return 0;
int leftHeight = getTreeHeight(root->left);
int rightHeight = getTreeHeight(root->right);
return std::max(leftHeight, rightHeight)+1;
}
void printNodeAtLevel(TreeNode* root,int level)
{
if(nullptr == root || level < 0 )
return;
if(0 == level)
{
std::cout << root->val << " ";
return;
}
// 左子树的 level - 1 级
printNodeAtLevel(root->left, level - 1);
// 右子树的 level - 1 级
printNodeAtLevel(root->right, level - 1);
}
void levelOrder(const TreeNode* root)
{
if(nullptr == root)
return;
int totalLevel = getTreeHeight(root);
for(int i = 0; i < totalLevel; i++)
{
printNodeAtLevel(root, i);
endl(std::cout); //打印完一层,换行
}
}