1 二叉树前序后序中序遍历 递归非递归解法
2 二叉树层序遍历;二叉树zigzag层序遍历
3 从中序和前序重建二叉树;从中序和后序重建二叉树
1 二叉树前序后序中序遍历
- 二叉树前序遍历,前序遍历方法是先访问根节点,然后再访问左右节点。
前序遍历的递归和非递归解法:
//前序递归遍历
void preOrderRec(TreeNode *root)
{
if(root == NULL) return;
visit(root);
preOrder(root->left);
preOrder(root->right);
}
//前序非递归遍历
void preOrder(TreeNode *root)
{
stack<TreeNode *> s;
while ((NULL != root) || !s.empty())
{
if (NULL != root)
{
visit(root);
s.push(root);
root = root->left;
}
else
{
root = s.top();
s.pop();
root = root->right;
}
}
}
- 二叉树中序遍历
中序遍历是先访问左子树,然后访问根节点,最后访问右子树
//递归中序遍历
void inorderTraversal(TreeNode *root)
{
if(root == NULL) return;
inorderTraversal(root->left);
visit(root);
inorderTraversal(root->right);
}
//非递归中序遍历
vector<int> inorderTraversal(TreeNode *root) {
vector<int> result;
vector<TreeNode*> Stack;
if(root == NULL)
return result;
TreeNode * current = root;
while(current != NULL || !Stack.empty())
{
if(current != NULL){
//左子树压入栈内
Stack.push_back(current);
current = current->left;
}
else
{
current = Stack.back();
Stack.pop_back();
result.push_back(current->val);
current = current->right;
}
}
return result;
}
- 二叉树后序遍历
按照左-右-中的顺序访问,第一次遇到右节点的时候先入栈,并不访问,等到后序先把对应的左节点访问之后,才能轮到右节点,所以使用flag进行标记。
// 后序递归遍历
void postorderTranversal(TreeNode *root)
{
if(root == NULL) return;
preOrder(root->left);
preOrder(root->right);
visit(root);
}
//后序非递归遍历:主要是要有一个标记来表示右子树是否访问过:
void postorderTranversal(TreeNode * root)
{
if(!root) return;
stack<TreeNode *> Stack;
TreeNode *lastvisit = NULL;
while(root != NULL || !Stack.empty())
{
//压入左子树
while(root!= NULL)
{
Stack.push_back(root);
root = root -> left;
}
TreeNode * current = Stack.top();
//current右子树为空说明到了最左,肯定要先访问
//lastvisited等于current右子树说明右节点已经访问过一次了,
//现在可以访问current节点了。保证的是右节点一定比根节点先被访问到。
if(current -> right && lastvisited != (current->right))
{
current = current->right;
}
else
{
int data = current ->val;
lastvisited = current;
Stack.pop();
}
}
}
2 二叉树层序遍历;二叉树zigzag层序遍历
在二叉树层序遍历的时候,第一种思想是:利用queue队列的性质,要保持一个当前level的队列,和下一level的队列。首先依次访问当前level队列的每一个节点,然后把这些节点的儿子们依次放入下一level的队列中,直到当前level队列的每一个节点都被访问到了,这时候把下一level队列作为当前level的队列,同时继续向下循环。这个属于广度优先BFS。
为了节省空间,使用一个queue,但是用两个变量来记录当前level和下一level剩余的待访问节点数。
vector<vector<int> > levelOrder(TreeNode *root) {
vector<vector<int>> result;
if (!root) return result;
vector<int> tmp;
queue<TreeNode*> nodesQueue;
int nodesInCurrentLevel = 1;
int nodesInNextLevel = 0;//分别记录当前level和下一level的节点数。
nodesQueue.push(root);
while (!nodesQueue.empty()) {
TreeNode *currNode = nodesQueue.front();
nodesQueue.pop();
nodesInCurrentLevel--;
if (currNode) {
tmp.push_back(currNode->val);
nodesQueue.push(currNode->left);
nodesQueue.push(currNode->right);
nodesInNextLevel += 2;
}
if (nodesInCurrentLevel == 0) {
result.push_back(tmp);
tmp.clear();
nodesInCurrentLevel = nodesInNextLevel;
nodesInNextLevel = 0;
}
}
result.pop_back();
return result;
}
第二种思想是深度优先DFS,先达到最深,然后对每一个深度进行递归访问:
vector<vector<int> > levelOrder(TreeNode *root) {
vector<vector<int>> result;
if(root == NULL) return result;
int depth = getMaxDepth(root,1);
for(int i = 1; i<= depth ;i++){
vector<int> tmp;
tranverseInDepth(root, i,tmp);
result.push_back(tmp);
}
return result;
}
int getMaxDepth(TreeNode * root,int depth)
{
if(root == NULL) return depth-1;
int left = getMaxDepth(root->left,depth+1);
int right = getMaxDepth(root->right,depth+1);
return left>right?left:right;
}
void tranverseInDepth(TreeNode* root, int index ,vector<int> &tmp)
{
if(!root) return;
if(1 == index)
{
tmp.push_back(root->val);
}
else{
tranverseInDepth(root->left,index-1,tmp);
tranverseInDepth(root->right,index-1,tmp);
}
}
对于zigzag访问的话则是这样子:首先访问根节点,然后从右向左访问根节点的下一层,然后自左向右访问再下一层,如此循环往复。这里使用stack来保存一层的节点,通过访问顺序的变化,实现zigzag的效果。
vector<vector<int> > zigzagLevelOrder(TreeNode *root) {
stack<TreeNode *> S ,Q;
vector<vector<int>> result;
if(root == NULL) return result;
TreeNode * cur = root;
Q.push(cur);
bool zig = true;
while(!Q.empty() || !S.empty())
{
vector<int> tmp;
if(zig)
{
while(!Q.empty())
{
TreeNode * t = Q.top();
Q.pop();
tmp.push_back(t->val);
if(t->left != NULL)
S.push(t->left);
if(t->right != NULL)
S.push(t->right);
}
result.push_back(tmp);
tmp.clear();
zig = false;
}
else
{
while(!S.empty())
{
TreeNode * s = S.top();
S.pop();
tmp.push_back(s->val);
if(s->right != NULL)
Q.push(s->right);
if(s->left != NULL)
Q.push(s->left);
}
result.push_back(tmp);
tmp.clear();
zig = true;
}
}
return result;
}
3 从中序和前序重建二叉树;从中序和后序重建二叉树
map<int,int> mapIndex;
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
buildMap(inorder);
int n = preorder.size();
return buildTreeHelper(preorder,inorder,n,0);
}
void buildMap(vector<int> inorder)
{
for(int i = 0; i < inorder.size() ; i++)
mapIndex[inorder[i]] = i;
}
TreeNode* buildTreeHelper(vector<int>& pre, vector<int> &in, int n, int offset)
{
if(n == 0) return NULL;
int val = pre[0];
int i = mapIndex[val] - offset;
TreeNode * root = new TreeNode(val);
// pre跳过第一个元素
vector<int> tmp1(pre.begin()+1,pre.end());
root -> left = buildTreeHelper(tmp1, in , i,offset);
if(i+1 > in.size() || i+1 > pre.size()) return NULL;
//分别跳过左子树和根节点 以及 根节点和左子树
vector<int> tmp2(in.begin()+i+1,in.end());
vector<int> tmp3(pre.begin()+i+1,pre.end());
root -> right = buildTreeHelper(tmp3,tmp2,n-i-1, offset +i + 1);
return root;
}
- 对于后序和中序进行重建,原理基本相同,只是根节点对于后序来说是在数组的末尾位置。
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
buildMap(inorder);
int n = postorder.size();
return buildTreeHelper(inorder,postorder,n,0);
}
TreeNode* buildTreeHelper(vector<int> &in, vector<int> &post,int n, int offset)
{
if(n == 0) return NULL;
int val = post[n-1];
int i = mapIndex[val] - offset;
TreeNode * root = new TreeNode(val);
root->left = buildTreeHelper(in,post,i,offset);
if(i+1 > in.size() || i > post.size()) return NULL;
// inorder跳过左子树和根节点
vector<int> tmp2(in.begin()+i+1,in.end());
//postorder跳过左子树
vector<int> tmp3(post.begin()+i,post.end());
root->right = buildTreeHelper(tmp2,tmp3,n-i-1,offset+i+1);
return root;
}