Deep traversal of binary tree-recursive & non-recursive

Traversal of the binary tree:

D: Visit the root node, L: Traverse the left subtree of the root node, R: Traverse the right subtree of the root node.

Given a preorder traversal sequence and a midorder traversal sequence of a binary tree, a binary tree can be uniquely determined.

The general practice of non-recursive depth-first traversal of a binary tree is to use a stack, and the general practice of non-recursive breadth-first traversal is to use a queue.

Depth-first traversal of the binary tree.

1. Recursive algorithm for medium order traversal (LDR):

If the binary tree is empty, the algorithm ends; otherwise:

    The middle order traverses the left subtree of the root node;

    Visit the root node;

    The middle order traverses the right subtree of the root node.

2. Recursive algorithm for preorder traversal (DLR):

If the binary tree is empty, the algorithm ends, otherwise:

    Visit the root node;

    The preorder traverses the left subtree of the root node;

    The preorder traverses the right subtree of the root node.

3. Recursive algorithm for post-order traversal (LRD):

If the binary tree is empty, the algorithm ends, otherwise:

    Afterwards traverse the left subtree of the root node;

    Traverse the right subtree of the root node in post-order;

    Visit the root node.

 

Breadth first traverses the binary tree.

Breadth-first travel binary tree (order traversal) is implemented with a queue, starting from the first layer (root node) of the binary tree, traversing layer by layer from top to bottom; in the same layer, in the order from left to right The nodes are visited one by one.

The nodes of the binary tree are accessed in order from the root node to the leaf node and from the left subtree to the right subtree. algorithm:

    1 Initialize a queue and put the root node into the queue;

    2 When the queue is not empty, perform steps 3 to 5 in a loop, otherwise execute 6;

    3 Get a node out of the queue and visit the node;

    4 If the left sub-tree of the node is not empty, the left sub-tree of the node is queued;

    5 If the right subtree of the node is not empty, the right subtree of the node is queued;

    6 End.

 

Non-recursive depth-first traversal of the binary tree.

The stack is the most commonly used structure to implement recursion. A stack is used to record the nodes or subtrees that are yet to be traversed for later access. The depth-first traversal of recursion can be changed to a non-recursive algorithm.

1. Non-recursive preorder traversal: When a node is encountered, the node is visited and pushed to the stack, and then descended to traverse its left subtree. After traversing its left subtree, the node is popped from the top of the stack, and then the right subtree structure of the node is traversed according to the address indicated by its right link.

2. 非递归中序遍历:遇到一个结点,就把它推入栈中,并去遍历它的左子树。遍历完左子树后,从栈顶托出这个结点并访问之,然后按照它的右链接指示的地址再去遍历该结点的右子树。

3. 非递归后序遍历:遇到一个结点,把它推入栈中,遍历它的左子树。遍历结束后,还不能马上访问处于栈顶的该结点,而是要再按照它的右链接结构指示的地址去遍历该结点的右子树。遍历遍右子树后才能从栈顶托出该结点并访问之。另外,需要给栈中的每个元素加上一个特征位,以便当从栈顶托出一个结点时区别是从栈顶元素左边回来的(则要继续遍历右子树),还是从右边回来的(该结点的左、右子树均已周游)。特征为Left表示已进入该结点的左子树,将从左边回来;特征为Right表示已进入该结点的右子树,将从右边回来。

4. 简洁的非递归前序遍历:遇到一个结点,就访问该结点,并把此结点的非空右结点推入栈中,然后下降去遍历它的左子树。遍历完左子树后,从栈顶托出一个结点,并按照它的右链接指示的地址再去遍历该结点的右子树结构。

----------------------------------------------------------------------

    图的深度优先搜索法是树的先根遍历的推广,它的基本思想是:从图G的某个顶点v0出发,访问v0,然后选择一个与v0相邻且没被访问过的顶点vi访问,再从vi出发选择一个与vi相邻且未被访问的顶点vj进行访问,依次继续。如果当前被访问过的顶点的所有邻接顶点都已被访问,则退回到已被访问的顶点序列中最后一个拥有未被访问的相邻顶点的顶点w,从w出发按同样的方法向前遍历,直到图中所有顶点都被访问。

    图的广度优先搜索是树的按层次遍历的推广,它的基本思想是:首先访问初始点vi,并将其标记为已访问过,接着访问vi的所有未被访问过的邻接点vi1,vi2, …, vi t,并均标记已访问过,然后再按照vi1,vi2, …, vi t的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依次类推,直到图中所有和初始点vi有路径相通的顶点都被访问过为止。



#include<iostream>
#include<queue>
#include<stack>
using namespace std;
//二叉树结点的描述
typedef struct BiTNode
{
    char data;
    struct BiTNode *lchild, *rchild;      //左右孩子
}BiTNode,*BiTree;
//按先序遍历创建二叉树
//BiTree *CreateBiTree()     //返回结点指针类型
//void CreateBiTree(BiTree &root)      //引用类型的参数
void CreateBiTree(BiTNode **root)    //二级指针作为函数参数
{
    char ch; //要插入的数据
    scanf("\n%c", &ch);
    //cin>>ch;
    if(ch=='#')
        *root = NULL;
    else
    {
        *root = (BiTNode *)malloc(sizeof(BiTNode));
        (*root)->data = ch;
        printf("请输入%c的左孩子:",ch);
        CreateBiTree(&((*root)->lchild));
        printf("请输入%c的右孩子:",ch);
        CreateBiTree(&((*root)->rchild));
    }
}
//前序遍历的算法程序
void PreOrder(BiTNode *root)
{
    if(root==NULL)
        return ;
    printf("%c ", root->data); //输出数据
    PreOrder(root->lchild); //递归调用,前序遍历左子树
    PreOrder(root->rchild); //递归调用,前序遍历右子树
}
//中序遍历的算法程序
void InOrder(BiTNode *root)
{
    if(root==NULL)
        return ;
    InOrder(root->lchild); //递归调用,前序遍历左子树
    printf("%c ", root->data); //输出数据
    InOrder(root->rchild); //递归调用,前序遍历右子树
}
//后序遍历的算法程序
void PostOrder(BiTNode *root)
{
    if(root==NULL)
        return ;
    PostOrder(root->lchild);      //递归调用,前序遍历左子树
    PostOrder(root->rchild);      //递归调用,前序遍历右子树
    printf("%c ", root->data);    //输出数据  
}
/*
二叉树的非递归前序遍历,前序遍历思想:先让根进栈,只要栈不为空,就可以做弹出操作,
每次弹出一个结点,记得把它的左右结点都进栈,记得右子树先进栈,这样可以保证右子树在栈中总处于左子树的下面。
*/
void PreOrder_Nonrecursive2(BiTree T)     //先序遍历的非递归  
{
    if(!T)  
        return ;  
  
    stack<BiTree> s;
    s.push(T);
    while(!s.empty())
    {
        BiTree temp = s.top();
        cout<<temp->data<<" ";
        s.pop();
        if(temp->rchild)
            s.push(temp->rchild);
        if(temp->lchild)
            s.push(temp->lchild);
    }
}
void PreOrder_Nonrecursive(BiTree T)     //先序遍历的非递归
{
    if(!T)
        return ;
    stack<BiTree> s;
    while(T)          // 左子树上的节点全部压入到栈中
    {
        s.push(T);
        cout<<T->data<<"  ";
        T = T->lchild;
    }
    
    while(!s.empty())
    {        
        BiTree temp = s.top()->rchild;  // 栈顶元素的右子树
        s.pop();                        // 弹出栈顶元素
        while(temp)          // 栈顶元素存在右子树,则对右子树同样遍历到最下方
        {
            cout<<temp->data<<"  ";
            s.push(temp);
            temp = temp->lchild;
        }
    }
}
void InOrderTraverse(BiTree T)   // 中序遍历的非递归
{
    if(!T)
        return ;
    stack<BiTree> S;
    BiTree curr = T->lchild;    // 指向当前要检查的节点
    S.push(T);
    while(curr != NULL || !S.empty())
    {
        while(curr != NULL)    // 一直向左走
        {
            S.push(curr);
            curr = curr->lchild;
        }
        curr = S.top();
        S.pop();
        cout<<curr->data<<"  ";
        curr = curr->rchild;
    }
}
void PostOrder_Nonrecursive(BiTree T)  // 后序遍历的非递归  
{  
    stack<BiTree> S;  
    BiTree curr = T ;           // 指向当前要检查的节点
    BiTree previsited = NULL;    // 指向前一个被访问的节点
    while(curr != NULL || !S.empty())  // 栈空时结束  
    {  
        while(curr != NULL)            // 一直向左走直到为空
        {  
            S.push(curr);  
            curr = curr->lchild;  
        }  
        curr = S.top();
        // 当前节点的右孩子如果为空或者已经被访问,则访问当前节点
        if(curr->rchild == NULL || curr->rchild == previsited)  
        {  
            cout<<curr->data<<"  ";  
            previsited = curr;  
            S.pop();  
            curr = NULL;  
        }  
        else
            curr = curr->rchild;      // 否则访问右孩子
    }  

void PostOrder_Nonrecursive(BiTree T)  // 后序遍历的非递归     双栈法
{  
    stack<BiTree> s1 , s2;  
    BiTree curr ;           // 指向当前要检查的节点
    s1.push(T);
    while(!s1.empty())  // 栈空时结束  
    {
        curr = s1.top();
        s1.pop();
        s2.push(curr);
        if(curr->lchild)
            s1.push(curr->lchild);
        if(curr->rchild)
            s1.push(curr->rchild);
    }
    while(!s2.empty())
    {
        printf("%c ", s2.top()->data);
        s2.pop();
    }
}
int visit(BiTree T)
{
    if(T)
    {
        printf("%c ",T->data);
        return 1;
    }
    else
        return 0;
}
void LeverTraverse(BiTree T)   //方法一、非递归层次遍历二叉树 
{
    queue <BiTree> Q;
    BiTree p;
    p = T;
    if(visit(p)==1)
        Q.push(p);
    while(!Q.empty())
    {
        p = Q.front();
        Q.pop();
        if(visit(p->lchild) == 1) 
            Q.push(p->lchild);
        if(visit(p->rchild) == 1)
            Q.push(p->rchild);
    }
}
void LevelOrder(BiTree BT)     //方法二、非递归层次遍历二叉树 
{
    BiTNode *queue[10];//定义队列有十个空间
    if (BT==NULL)
        return;
    int front,rear;
    front=rear=0;
    queue[rear++]=BT;
    while(front!=rear)//如果队尾指针不等于对头指针时
    {
        cout<<queue[front]->data<<"  ";  //输出遍历结果
        if(queue[front]->lchild!=NULL)  //将队首结点的左孩子指针入队列
        {
            queue[rear]=queue[front]->lchild;
            rear++;    //队尾指针后移一位
        }
        if(queue[front]->rchild!=NULL)
        {
            queue[rear]=queue[front]->rchild;    //将队首结点的右孩子指针入队列
            rear++;   //队尾指针后移一位
        }
        front++;    //对头指针后移一位
    }
}
int depth(BiTNode *T)   //树的深度
{
    if(!T)
        return 0;
    int d1,d2;
    d1=depth(T->lchild);
    d2=depth(T->rchild);
    return (d1>d2?d1:d2)+1;
    //return (depth(T->lchild)>depth(T->rchild)?depth(T->lchild):depth(T->rchild))+1;
}
int CountNode(BiTNode *T)
{
    if(T == NULL)
        return 0;
    return 1+CountNode(T->lchild)+CountNode(T->rchild);
}
int main(void)
{
    BiTNode *root=NULL; //定义一个根结点
    int flag=1,k;
    printf("                     本程序实现二叉树的基本操作。\n");
    printf("可以进行建立二叉树,递归先序、中序、后序遍历,非递归先序、中序遍历及非递归层序遍历等操作。\n");
    while(flag)
    {
        printf("\n");
        printf("|--------------------------------------------------------------|\n");
        printf("|                    二叉树的基本操作如下:                     |\n");
        printf("|                        0.创建二叉树                          |\n");
        printf("|                        1.递归先序遍历                        |\n");
        printf("|                        2.递归中序遍历                        |\n");
        printf("|                        3.递归后序遍历                        |\n");
        printf("|                        4.非递归先序遍历                      |\n");
        printf("|                        5.非递归中序遍历                      |\n");
        printf("|                        6.非递归后序遍历                      |\n");
        printf("|                        7.非递归层序遍历                      |\n");
        printf("|                        8.二叉树的深度                        |\n");
        printf("|                        9.二叉树的结点个数                    |\n");
        printf("|                        10.退出程序                            |\n");
        printf("|--------------------------------------------------------------|\n");
        printf("                        请选择功能:");
        scanf("%d",&k);
        switch(k)
        {
        case 0:
            printf("请建立二叉树并输入二叉树的根节点:");
            CreateBiTree(&root);
            break;
        case 1:
            if(root)
            {
                printf("递归先序遍历二叉树的结果为:");
                PreOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 2:
            if(root)
            {
                printf("递归中序遍历二叉树的结果为:");
                InOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 3:
            if(root)
            {
                printf("递归后序遍历二叉树的结果为:");
                PostOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 4:
            if(root)
            {
                printf("非递归先序遍历二叉树:");
                PreOrder_Nonrecursive(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 5:
            if(root)
            {
                printf("非递归中序遍历二叉树:");
                InOrderTraverse(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 6:
            if(root)
            {
                printf("非递归后序遍历二叉树:");
                PostOrder_Nonrecursive(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 7:
            if(root)
            {
                printf("非递归层序遍历二叉树:");
                //LeverTraverse(root);
                LevelOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 8:
            if(root)
                printf("这棵二叉树的深度为:%d\n",depth(root));
            else
                printf("          二叉树为空!\n");
            break;
        case 9:
            if(root)
                printf("这棵二叉树的结点个数为:%d\n",CountNode(root));
            else
                printf("          二叉树为空!\n");
            break;
        default:
            flag=0;
            printf("程序运行结束,按任意键退出!\n");
        }
    }
    system("pause");
    return 0;
}

运行效果图如下:



分别输入:
1
2
4
#
#
5
#
#
3
6
#
#
7
#

就可以构造如下图所示的二叉树了。。

后序遍历非递归的另外一种写法:

复制代码代码如下:

    /* 
    后序遍历由于遍历父节点是在遍历子节点之后,而且左节点和右节点遍历后的行为不一样, 
    所以需要用变量来记录前一次访问的节点,根据前一次节点和现在的节点的关系来确定具体执行什么操作 
    */  
    void Postorder(BiTree T)  
    {  
        if(T == NULL)  
            return ;  
        stack<BiTree> s;  
        BiTree prev = NULL , curr = NULL;  
        s.push(T);  
        while(!s.empty())  
        {  
            curr = s.top();  
            if(prev == NULL  || prev->lchild == curr || prev->rchild == curr)  
            {  
                if(curr->lchild != NULL)  
                    s.push(curr->lchild);  
                else if(curr->rchild != NULL)  
                    s.push(curr->rchild);  
            }  
            else if(curr->lchild == prev)  
            {  
                if(curr->rchild != NULL)  
                    s.push(curr->rchild);  
            }  
            else  
            {  
                cout<<curr->data;  
                s.pop();  
            }  
            prev = curr;  
        }  
    }  

输入二叉树中的两个节点,输出这两个结点在数中最低的共同父节点。
思路:遍历二叉树,找到一条从根节点开始到目的节点的路径,然后在两条路径上查找共同的父节点。
复制代码代码如下:

    // 得到一条从根节点开始到目的节点的路径  
    bool GetNodePath(TreeNode *pRoot , TreeNode *pNode , vector<TreeNode *> &path)  
    {  
        if(pRoot == NULL)  
            return false;  
        if(pRoot == pNode)  
            return true;  
        else if(GetNodePath(pRoot->lchild , pNode , path) )  
        {  
            path.push_back(pRoot->lchild);  
            return true;  
        }  
        else if(GetNodePath(pRoot->rchild , pNode , path) )  
        {  
            path.push_back(pRoot->rchild);  
            return true;  
        }  
        return false;  
    }  
    TreeNode *GetLastCommonNode(const vector<TreeNode *> &path1 , const vector<TreeNode *> &path2)  
    {  
        vector<TreeNode *>::const_iterator iter1 = path1.begin();  
        vector<TreeNode *>::const_iterator iter2 = path2.begin();  
        TreeNode *pLast;  
        while(iter1 != path1.end() && iter2 != path2.end() )  
        {  
            if(*iter1 == *iter2)  
                pLast = *iter1;  
            else  
                break;  
            iter1++;  
            iter2++;  
        }  
        return pLast;  
    }  
    TreeNode *GetLastCommonParent(TreeNode *pRoot , TreeNode *pNode1 , TreeNode *pNode2)  
    {  
        if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)  
            return  NULL;  
        vector<TreeNode *> path1;  
        GetNodePath(pRoot , pNode1 , path1);  

        vector<TreeNode *> path2;  
        GetNodePath(pRoot , pNode2 , path2);  
        return GetLastCommonNode(path1 , path2);  
    }  

发布了16 篇原创文章 · 获赞 1 · 访问量 3823

Guess you like

Origin blog.csdn.net/GYJ_love/article/details/50526827