对于二叉树有两种搜索方式:深度优先搜索与广度优先搜索,而深度优先搜索分为前序,中序,后序三种遍历方式,下面给几种遍历方式一 一解释。
深度优先搜索:
- 前序遍历,访问节点顺序:根节点--->左子节点--->右子节点(根节点在最前面被访问)
- 中序遍历,访问节点顺序:左子节点--->根节点--->右子节点(根节点在最中间被访问)
- 后续遍历,访问节点顺序:左子节点--->右子节点--->根节点(根节点在最后面被访问)
对于下面二叉树的三种遍历顺序:
- 前序遍历:8、7、5、20、9、13、14
- 中序遍历:5、7、20、8、13、9、14
- 后续遍历:5、20、7、13、14、9、8
对于这三种遍历都给出递归与非递归的方式:(下面所有的举例都是使用下图二叉树)
对于递归的方式,代码如下:
前序遍历(递归)
class solution{
public:
vector<int> result;
public:
vector<int> Preorder_traversal(TreeNode *root)
{
//vector<int> result;
if(root!=null)
{
result.push_back(root->data);
if(root->left)
{
Preorder_traversal(root->left);
}
if(root->right)
{
Preorder_traversal(root->right);
}
}
return result;
}
}
中序遍历(递归)
class solution{
public:
vector<int> result;
public:
vector<int> Inorder_traversal(TreeNode *root)
{
if(root!=null)
{
if(root->left)
{
Inorder_traversal(root->left);
}
result.push_back(root);
if(root->right)
{
Inorder_traversal(root->right);
}
}
}
}
后续遍历(递归)
class solution{
public:
vector<int> result;
public:
vector<int> Subsequent_traversal(TreeNode *root)
{
if(root->left)
{
Subsequent_traversal(root->left);
}
if(root->right)
{
Subsequent_traversal(root->right);
}
result.push_back(root);
}
}
而对于非递归方式,要借助于栈来实现:
- 对于栈实现前序遍历,前面博客有所提及,并且有图解,这里再详细理论讲解一下,先把根节点压入栈中,然后弹出栈中的栈顶数据(这里弹出的数据就是我们要遍历的数据),再把栈顶数据在树中的右、左子节点依次压入栈中,再次把此时的栈顶数据弹出来(与前面括号一样),把弹出来的栈顶数据的右、左子节点依次压入栈中,再弹出栈中的栈顶数据,一直下去...,一直到栈中没有数据为止。
- 下面给出前序搜索的代码:
/*前序搜索*/
class solution{
public:
vector<int> result;
public:
vector<int> Preorder_traversal(TreeNode *root)
{
if(root==null)
return ;
stack<TreeNode*> pre_stack=new stack<>;
pre_stack.push(root);
while(!pre_stack.empty)
{
stack<TreeNode*> temp=pre_stack.front();
result.push_back(pre_stack.top()->data);
pre_stack.pop();
if(temp->right!=null)
{
pre_stack.push(temp->right);
}
if(temp->left!=null)
{
pre_stack.push(temp->left);
}
}
}
}
- 对于栈实现中序遍历,比前序遍历更复杂,首先是从根节点一直访问左子结点,左子节点的左子结点,左子结点的左子结点....,直到没有左子结点为止(在此过程中每访问一次左子结点都要把该节点压入栈中),此时把栈顶数据弹出来,看该栈顶数据有没有右子节点(每次弹出栈顶数据都要看该栈顶数据有没有右子节点,如果有的话,从该分支继续遍历下去,继续把数据压入栈中),假设没有,则再次弹出栈顶数据,再看该栈顶数据有没有有子节点(与前面括号一样),这里再次假设没有,则再次弹出数据,下图所示为上述二叉树用栈遍历的过程
步骤 | 压入数据 | 下一步操作情况 | 弹出数据 | 下一步操作情况 | 栈中数据 |
1 | 8 | 有左节点,继续压入 | 8 | ||
2 | 7 | 有左节点,继续压入 | 8、7 | ||
3 | 5 | 无左节点,停止压入 | 8、7、5 | ||
4 | 5 | 无右节点,继续弹出 | 8、7 | ||
5 | 7 | 有右节点,停止弹出 | 8 | ||
6 | 20 | 无左节点,停止压入 | 8、20 | ||
7 | 20 | 无右节点,继续弹出 | 8 | ||
8 | 8 | 有右节点,停止弹出 | 无数据 | ||
9 | 9 | 有左节点,继续压入 | 9 | ||
10 | 13 | 无左节点,停止压入 | 9、13 | ||
11 | 13 | 无右节点,继续弹出 | 9 | ||
12 | 9 | 有右节点,停止弹出 | 无 | ||
13 | 14 | 无左节点,停止压入 | 14 | ||
14 | 14 | 无右节点,且为空(两者均满足,遍历完,停止) | 无 |
-
/*中序搜索*/ class solution{ public: vector<int> result; public: vector<int> Inorder_traversal(TreeNode *root) { if(root==null) return; } stack<TreeNode*> Inorder_stack; TreeNode* p; p=root; while(p||!Inorder_stack.empty()) { if(p) { Inorder_stack.push(p); p=p->left; } else { p=Inorder_stack.top(); result.push_back(p->data); Inorder_stack.pop(); p=p->right; } } return result; }
- 对于二叉树的后续搜索方式,下面有一种很巧妙的方式,先来一张图,该图同样是基于栈的方式实现了后续搜索,首先是对于一个二叉树,先从根部遍历(此方法注意,遍历每个元素,都要遍历两次,把数据两次压入栈中),压入根部节点后,弹出栈顶数据,看此时弹出的数据和弹出数据之后的栈顶数据是否相等,如果相等则继续分别压入弹出数据的右子节点、左子结点,压入完后,弹出栈顶数据,......与前面一样,由于这里压入20、20、5、5后后面没数据了,所以在这里弹出栈的时候会出现5!=20,所以把5弹出,后面20!=7,弹出20,7!=9,弹出7......一直到栈中没有数据为止。
代码:
/*后续搜索*/
class solution
{
public:
vector<int> result;
public:
vector<int> Subsequent_traversal(TreeNode*root)
{
if(root==null)
return;
stack<TreeNode*> Subsequent_stack;
TreeNode* p=root;
Subsequent_stack.push(p);
Subsequent_stack.push(p);
while(!Subsequent_stack.empty)
{
p=Subsequent_stack.top();//获得栈顶数据的指针
Subsequent_stack.pop();//弹出栈顶数据
if(p->data==Subsequent_stack.top()->data)//判断弹出栈顶数据之前,前后两个数据是否相等
{ //如果右子节点存在,先压入右子节点,后压入左子结点
if(p->right)
{
Subsequent_stack.push(p->right);
Subsequent_stack.push(p->right);
}
if(p->left)
{
Subsequent_stack.push(p->left);
Subsequent_stack.push(p->left);
}
}
else
{
result->push_back(p->data);
}
}
return result;
}
}
未完待续.......