数的遍历有4种,分别是前序遍历,中序遍历和后序遍历、层次遍历。
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
层次遍历:第一层节点从左到右,第二层节点从左到右,以此类推。
分享下我怎么记忆的,只需要记得第一个字表示根的位置,就不会搞混了。另外还要知道,如果只知道前序和后序,是不能唯一确定一棵树的。
对于每种遍历,又存在递归式和非递归式。递归式当然好实现,只要简单的几行代码就可以实现;而非递归式呢,需要花点功夫,另外非递归式的后序遍历还有点小麻烦。下面来直接看代码吧
树的结构体
struct BinaryTreeNode{
int value;
BinaryTreeNode *pLeft;
BinaryTreeNode *pRight;
};
递归式
递归式前序遍历
void preOrder(BinaryTreeNode* root){
if(root==NULL){
return ;
}
else{
cout<<root->value;
preOrder(root->pLeft);
preOrder(root->pRight);
}
}
递归式中序遍历
void InOrder(BinaryTreeNode* root){
if(root==NULL){
return ;
}
else{
InOrder(root->pLeft);
cout<<root->value;
InOrder(root->pRight);
}
}
递归式后序遍历
void postOrder(BinaryTreeNode* root){
if(root==NULL){
return ;
}
else{
postOrder(root->pLeft);
postOrder(root->pRight);
cout<<root->value;
}
}
总结:可以看到,我们只需要短短的几行代码就可以实现。只需要记住前序遍历、中序遍历和后序遍历的遍历方式即可。
非递归式
非递归式前序遍历
void PreOrder1(BinaryTreeNode* root){
if (root==NULL){
return;
}
BinaryTreeNode *node = root;
stack<BinaryTreeNode*>nodelist;
while(!nodelist.empty()||node!=NULL){
while(node!=NULL){
nodelist.push(node);
cout<<node->value;
node = node->pLeft;
}
node = nodelist.top();
nodelist.pop();
node = node->pRight;
}
}
非递归式中序遍历
void InOrder1(BinaryTreeNode* root){
if (root==NULL){
return;
}
BinaryTreeNode *node = root;
stack<BinaryTreeNode*>nodelist;
while(!nodelist.empty()||node!=NULL){
while(node!=NULL){
nodelist.push(node);
node = node->pLeft;
}
node = nodelist.top();
cout<<node->value;
nodelist.pop();
node = node->pRight;
}
}
总结:
非递归式的前序和中序很相像,他们的区别在于修改了输出value的位置。原因在于前序是先输出根的值,再输出左节点的值;而中序是先输出左节点的值,再输出根的值。
非递归式后序遍历
void PostOrder1(BinaryTreeNode* root){
if (root==NULL){
return;
}
BinaryTreeNode *node = root;
stack<BinaryTreeNode*>nodelist;
BinaryTreeNode *pLastVisted = NULL;
while(node!=NULL){
nodelist.push(node);
node = node->pLeft;
}//先把所有的左节点放进去
while(!nodelist.empty()){
node = nodelist.top();
nodelist.pop();//取出栈顶节点
if(node->pRight==NULL || node->pRight==pLastVisted){//如果右子树为空或者右节点已经访问过了
cout<<node->value;
pLastVisted=node;
}else{
nodelist.push(node);//先将节点放回去
node = node->pRight;//取右节点
while(node){
nodelist.push(node);
node = node->pLeft;//放入左节点
}
}
}
}
总结:非递归式后序遍历用到了两个指针,其中一个要记录上一次遍历的那个节点。由于先输出左右节点的值,再输出根的值,因此在右节点还没有遍历的时候我们先将跟节点放回去,先遍历右子树,当右子树都遍历完了,再对根节点进行遍历。
最后附上我的main函数,一个简单建树的过程。
int main(){
//initial
BinaryTreeNode *node1 = new BinaryTreeNode();
BinaryTreeNode *node2 = new BinaryTreeNode();
BinaryTreeNode *node3 = new BinaryTreeNode();
BinaryTreeNode *node4 = new BinaryTreeNode();
BinaryTreeNode *node5 = new BinaryTreeNode();
BinaryTreeNode *node6 = new BinaryTreeNode();
node1->value = 1;
node1->pLeft = node2;
node1->pRight = node3;
node2->value = 2;
node2->pLeft = node4;
node2->pRight = node5;
node3->value = 3;
node3->pLeft = node6;
node3->pRight = NULL;
node4->value = 4;
node4->pLeft = NULL;
node4->pRight = NULL;
node5->value = 5;
node5->pLeft = NULL;
node5->pRight = NULL;
node6->value = 6;
node6->pLeft = NULL;
node6->pRight = NULL;
PreOrder1(node1);
cout<<endl;
InOrder1(node1);
cout<<endl;
PostOrder1(node1);
cout<<endl;
return 0;
}
层次遍历
一般我们实现先序遍历、中序遍历和后序遍历都用的是stack这个数据结构,而在层次遍历中,我们用queue来实现。
···
vector< vector > levelOrder(TreeNode* root) {
vector< vector > result;
if(root==NULL)
return result;
queue< TreeNode* > que;
que.push(root);
while(!que.empty()){
vector<int> tmp;
int size = que.size();
for(int i=0;i< size;i++){
TreeNode* node = que.front();
que.pop();
tmp.push_back(node->val);
if(node->left){
que.push(node->left);
}
if(node->right){
que.push(node->right);
}
}
result.push_back(tmp);
}
return result;
}
···