二叉树的所有的操作基本上都是跟遍历相关的,二叉树的深度遍历(先序、中序、后序)都设计栈的操作,但是二叉树的广度搜索(层次遍历)用到的就是队列的操作。
注意一点,二叉树的层次的遍历要得到的结果是把所有的信息放到一个一维的数组中,还是放到一个二维的数组中。
遍历结果存储到一维数组中
vector<int> PrintFromTopToBottom(TreeNode *root)
{
vector<int> result;
if(root == NULL)
return result;
queue<const TreeNode*> q;
q.push(root);
const TreeNode *ptop = NULL;
while(!q.empty())
{
ptop = q.front();
q.pop();
result.push_back(ptop->val);
if(ptop->left)
q.push(ptop->left);
if(ptop->right)
q.push(ptop->right);
}//while
return result;
}
上面的代码实现是把遍历的结果放到一维数组中,所以仅仅用到了一个队列。
遍历结果存放到二维数组中
如果要把最后遍历的结果保存到二维数组中,树的每一层的结点保存在一起,那么要两个队列分别遍历不同的层次:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int> > result;
if(root == NULL)
{
return result;
}
queue<const TreeNode*> current, next;
const TreeNode *ptmp = NULL;
vector<int> level;
current.push(root);
while(!current.empty())
{
while(!current.empty())
{
ptmp = current.front();
current.pop();
level.push_back(ptmp->val);
if(ptmp->left)
{
next.push(ptmp->left);
}
if(ptmp->right)
{
next.push(ptmp->right);
}
}//while
result.push_back(level);
level.clear();
swap(current, next);
}//while
}
上面的方法是利用两个队列实现的层次遍历,其实用一个队列也是可以的。
vector<vector<int>> levelOrder(TreeNode *root)
{
vector<vector<int> > result;
vector<int> line;
if(NULL == root)
return result;
queue<const TreeNode*> q;
q.push(root);
while(!q.empty())
{
int size = q.size();
for(int i = 0; i < size; i++)
{
const TreeNode *top = q.front();
q.pop();
line.push_back(top->val);
if(top->left)
{
q.push(top->left);
}
if(top->right)
{
q.push(top->right);
}
}//for
result.push_back(line);
line.clear();
}//while
return result;
}
通过获取queue的size可以知道这层的结点的个数。然后把这层遍历完,下一层的全部加入到队列的后面。
对于每层倒序的那种问题,最后把result.push_back(line)换成result.insert(result.begin(), line)就好了。
层次遍历的锯齿形遍历
先看一下遍历的样子:
具体的遍历方法是用两个栈,每个栈遍历一层,然后交替遍历。
vector<vector<int>> zigzagLevelOrder(TreeNode *root)
{
vector<vector<int> > res;
vector<int> line;
if(NULL == root)
{
return res;
}
stack<const TreeNode*> s1;
stack<const TreeNode*> s2;
s1.push(root);
while(!s1.empty() || !s2.empty())
{
while(!s1.empty())
{
const TreeNode *top = s1.top();
s1.pop();
line.push_back(top->val);
if(top->left)
s2.push(top->left);
if(top->right)
s2.push(top->right);
}//while
if(!line.empty())
res.push_back(line);
line.clear();
while(!s2.empty())
{
const TreeNode *top = s2.top();
s2.pop();
line.push_back(top->val);
if(top->right)
s1.push(top->right);
if(top->left)
s1.push(top->left);
}//while
if(!line.empty())
res.push_back(line);
line.clear();
}//while
return res;
}
二叉树层次遍历的应用
对于二叉树层次的遍历可能不会像二叉树深度遍历的应用那么广泛,但是看下面的应用:
判断一棵二叉树是否为对称的,先看一下递归的形式:
//判断对称二叉树的递归形式
bool isSymmetric(TreeNode* root)
{
if(root == NULL)
{
return true;
}
return SubTreeSym(root->left, root->right);
}
bool SubTreeSym(TreeNode *left, TreeNode *right)
{
if(left == NULL && right == NULL)
{
return true;
}
if((left == NULL && right != NULL) || (left != NULL && right == NULL) ||(left->val != right->val))
{
return false;
}
return SubTreeSym(left->left, right->right) && SubTreeSym(left->right, right->left);
}
其实迭代的形式很简单,就是从树的根开始,把一棵树分成一棵大的左子树和一棵大的右子树,然后分别层次遍历。到每队结点的时候都要比较他们的NULL的性质和val值的大小。由于是树和自己的镜像比较,所以注意左右孩子入队列的先后。
//判断对称二叉树的迭代形式
bool isSymmetric(TreeNode* root)
{
queue<const TreeNode*> q1;
queue<const TreeNode*> q2;
if(root == NULL)
{
return true;
}
q1.push(root->left);
q2.push(root->right);
while(!q1.empty() && !q2.empty())
{
const TreeNode *tmp1 = q1.front();
const TreeNode *tmp2 = q2.front();
q1.pop();
q2.pop();
if((tmp1 == NULL && tmp2 != NULL) || (tmp1 != NULL && tmp2 == NULL))
{
return false;
}
if(tmp1 != NULL)
{
if(tmp1->val != tmp2->val)//确保两个指针都不是NULL,然后才可以比较他们的值的大小
return false;
q1.push(tmp1->left);
q1.push(tmp1->right);
q2.push(tmp2->right);
q2.push(tmp2->left);
}
}//while
return true;
}