二叉树基本操作(三)(非递归实现前中后序遍历)

非递归实现前序遍历思路:非递归的前序遍历就比较简单,借助一个栈,将根节点入栈,然后通过循环,取栈顶元素,出栈就可访问当前栈顶元素(也就是当前树的根节点(前序遍历的顺序是VLR)),有先序遍历的顺序规则,在将其右子树入栈,在将其左子树入栈,只需要注意一点,左右子树的入栈顺序一定是先入栈右子树在入栈左子树。(因为我们先序遍历是访问了根节点之后就访问左子树在访问右子树,而栈的特点是后进先出,因此我们要想左子树的节点先于右子树的节点被访问到,左子树必须要在右子树入栈之后再入栈。)

//非递归前序遍历二叉树
void TreePreOrderByLoop(TreeNode *root)
{
    if(root == NULL)
    {
        //空树
        return;
    }
    Stack stack;
    StackInit(&stack);
    //根节点入栈
    StackPush(&stack,root);
    TreeNode *cur = NULL;
    //循环的去栈顶元素对其操作,栈为空时结束循环
    while(StackGetTop(&stack,&cur))
    {
        //出栈
        StackPop(&stack);
        //访问当前栈顶元素
        printf("%c ",cur->data);
        //将当前元素的右子树入栈
        if(cur->rchild != NULL)
        {
            StackPush(&stack,cur->rchild);
        }
        //再将当前元素的左子树入栈(一定是先右在左)
        if(cur->lchild != NULL)
        {
            StackPush(&stack,cur->lchild);
        }
    }
    return;
}

非递归实现中序遍历思路:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

//非递归中序遍历二叉树
void TreeInOrderByLoop(TreeNode *root)
{
    if(root == NULL)
    {
        //空树
        return;
    }
    Stack stack;
    StackInit(&stack);
    TreeNode *cur = root;
    while(1)
    {
        //该循环结束cur就到了一个空节点处
        //他的前一个指向的节点就是当前树的最左的节点
        while(cur != NULL)
        {
            StackPush(&stack,cur);
            cur = cur->lchild;
        }
        //此时cur为空
        //取栈顶元素
        TreeNode *tmp = NULL;
        int ret = StackGetTop(&stack,&tmp);
        if(ret == 0)
        {
            //取栈顶元素失败,说明栈空了
            //也就说明遍历完成
            return;
        }
        //访问
        printf("%c ",tmp->data);
        //出栈
        StackPop(&stack);
        if(tmp->rchild != NULL)
        {
            cur = tmp->rchild;
        }
    }
    return; 
}

非递归实现后序遍历思路:后序遍历我们都知道是LRV的顺序,所以我们在找到当前树的最左的节点之后,并不能立即进行访问,还要找到右节点,并且找到以后需要判断,该有节点是否被访问过或者是否为空,如果为空或者当前有节点被访问过,满足任意一个条件才能访问其根节点。因此我们还需要一个变量来保存我们最后访问的一个元素用于判断。

//非递归后序遍历二叉树
void TreePostOrderByLoop(TreeNode *root)
{
    if(root == NULL)
    {
        //空树
        return;
    }
    //定义一个栈并且初始化
    Stack stack;
    StackInit(&stack);
    //定义cur指针指向根节点
    TreeNode *cur = root;
    //定义一个指针保存我们最新访问过的最后一个元素
    TreeNode *pre = NULLwhile(1)
    {
        //该循环结束cur就到了一个空节点处
        //他的前一个指向的节点就是当前树的最左的节点
        while(cur != NULL)
        {
            StackPush(&stack,cur);
            cur = cur->lchild;
        }
        //此时cur为空
        //取栈顶元素
        TreeNode *tmp = NULL;
        int ret = StackGetTop(&stack,&tmp);
        if(ret == 0)
        {
            //取栈顶元素失败,说明栈空了
            //也就说明遍历完成
            return;
        }
        //对当前栈顶元素进行判定
        //1、如果当前栈顶元素的右子树为空
        //2、或者右子树已经访问过(和访问的上一个元素是同一个元素)
        //此时才能够对该栈顶元素进行访问,然后出栈
        if(tmp->rchild == NULL || tmp->rchild == pre)
        {
            printf("%c ",tmp->data);
            StackPop(&stack);
            //更新pre的指向,用于下一次的栈顶元素的判断
            pre = tmp;
        }
        //如果都不满足,则就让cur指向该栈顶元素的右子树
        //重复之前的动作
        else
        {
            cur = tmp->rchild;
        } 
    }
}
//以下为3个非递归遍历函数的测试函数
void TestTraverse()
{
    Test_Header;
    TreeNode *a = CreateNode('a');
    TreeNode *b = CreateNode('b');
    TreeNode *c = CreateNode('c');
    TreeNode *d = CreateNode('d');
    TreeNode *e = CreateNode('e');
    TreeNode *f = CreateNode('f');
    TreeNode *g = CreateNode('g');
    a->lchild = b;
    a->rchild = c;
    c->rchild = f;
    b->lchild = d;
    b->rchild = e;
    e->lchild = g;
    printf("递归先序遍历结果:");
    TreePreOrder(a);
    printf("非递归先序遍历结果:");
    TreePreOrderByLoop(a);
    printf("\n");
    printf("递归中序遍历结果:");
    TreeInOrder(a);
    printf("非递归中序遍历结果:");
    TreeInOrderByLoop(a);
    printf("\n");
    printf("递归后序遍历结果:");
    TreePostOrder(a);
    printf("非递归后序遍历结果:");
    TreePostOrderByLoop(a);
    printf("\n");
}

测试结果:
这里写图片描述
递归与非递归求二叉树的镜像其实就像我们日常生活中照镜子一样,我们看到的镜子里面的自己总是和现实的自己的左右手颠倒的。
这里写图片描述

//交换左右子树的辅助函数
void swap(TreeNode **a,TreeNode **b)
{
    if(a == NULL || b == NULL)
    {
        return;
    }
    TreeNode *tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}
//求二叉树的镜像(递归)
void TreeMirror(TreeNode *root)
{
    if(root == NULL)
    {
        //空树
        return;
    }
    //以先序的顺序,只是这里的访问动作是交换节点的左右子树
    swap(&root->lchild,&root->rchild);
    //调用函数本身,递归的实现每一个节点的左右子树的交换(不重不漏)
    TreeMirror(root->lchild);
    TreeMirror(root->rchild);
}
//求二叉树的镜像(非递归,借助队列层序遍历)
void TreeMirror2(TreeNode *root)
{
    if(root == NULL)
    {
        //空树
        return;
    }
    //定义一个队列并且初始化
    Queue queue;
    QueueInit(&queue);
    //将根节点入队列
    QueuePush(&queue,root);
    TreeNode *top = NULL;
    //循环的取队首元素
    while(QueueGetTop(&queue,&top))
    {
        //访问动作是交换左右子树
        swap(&top->lchild,&top->rchild);
        //将当前栈顶元素出栈
        QueuePop(&queue);
        //将当前栈顶元素的左子树入队列
        if(top->lchild != NULL)
        {
            QueuePush(&queue,top->lchild);
        }
         //将当前栈顶元素的右子树入队列
        if(top->rchild != NULL)
        {
            QueuePush(&queue,top->rchild);
        }
    }
    return;
}
//测试函数
void TestTraverse()
{
    Test_Header;
    TreeNode *a = CreateNode('a');
    TreeNode *b = CreateNode('b');
    TreeNode *c = CreateNode('c');
    TreeNode *d = CreateNode('d');
    TreeNode *e = CreateNode('e');
    TreeNode *f = CreateNode('f');
    TreeNode *g = CreateNode('g');
    a->lchild = b;
    a->rchild = c;
    c->rchild = f;
    b->lchild = d;
    b->rchild = e;
    e->lchild = g;
    TreeMirror(a);
    //TreeMirror2(a);
    printf("先序遍历结果:");
    TreePreOrder(a);
    printf("非递归先序遍历结果:");
    TreePreOrderByLoop(a);
    printf("\n");
    printf("中序遍历结果:");
    TreeInOrder(a);
    printf("非递归中序遍历结果:");
    TreeInOrderByLoop(a);
    printf("\n");
    printf("后序遍历结果:");
    TreePostOrder(a);
    printf("非递归后序遍历结果:");
    TreePostOrderByLoop(a);
    printf("\n"); 
}

测试结果:
这里写图片描述
判断一棵二叉树是否为一个完全二叉树思路:首先我们需要借助队列层序的遍历该二叉树的每一个元素,对每一个元素做相应的判断。

1、将根节点入队列
2、循环的取队首元素进行判断
(2.1)如果当前栈顶元素的左右子树都存在,则将其左右子树入栈进入下一个元素的判断。
(2.2)如果当前栈顶元素只有右子树,则该二叉树一定不是二叉树
(2.3)如果当前栈顶元素只有左子树,则需要进入第二个阶段进行更深层次的判断。并且将其左子树入队列。
(2.4)如果当前栈顶元素的左右子树都没有,则也需要进入第二个阶段进行判断。
3、进入第二阶段的判断,判断每一个元素是否都没有左右子树
(3.1)若每一个元素都没有左右子树,说明是完全二叉树
(3.2)若不满足该情况,说明不是完全二叉树

//判断是否为完全二叉树
int IsCompleteTree(TreeNode *root)
{
    if(root == NULL)
    {
        //空树
        return 0;
    }
    //定义一个队列并且初始化
    Queue queue;
    QueueInit(&queue);
    //将根节点初始化
    QueuePush(&queue,root);
    //定义一个变量用来表示是否进入第二阶段
    int Is_start_step_two = 0;
    TreeNode *top = NULL;
    //循环取队首元素
    while(QueueGetTop(&queue,&top))
    {
        //将当前队首元素出栈
        QueuePop(&queue);
        //第一阶段的判定
        if(Is_start_step_two == 0)
        {
            if(top->lchild != NULL && top->rchild != NULL)
            {
                QueuePush(&queue,top->lchild);
                QueuePush(&queue,top->rchild);
            }
            else if(top->lchild == NULL && top->rchild != NULL)
            {
                //此时没有左子树,只有右子树,那么一定不是完全二叉树
                return 0;
            }
            else if(top->lchild != NULL && top->rchild == NULL)
            {
                //此时只有左子树没有右子树,就需要进入下一个阶段再进行判断
                Is_start_step_two = 1;
                QueuePush(&queue,top->lchild);
            }
            else
            {
                //此时左右子树都没有,需要进入下一阶段在进行判断
                Is_start_step_two = 1;
            }
        }
        //第二阶段的判定
        else
        {
            //节点没有子树
            if(top->lchild == NULL && top->rchild == NULL)
            {
                ;
            }
            else
            {
                //节点有任意一棵子树,或者两棵子树都有,一定不是完全二叉树
                return 0;
            }
        }
    }//此处循环结束
    //所有的节点都遍历完了,而又没有return 0从而走到这一步
    //说明该树一定是完全二叉树
    return 1;
}
//测试函数
 void TestIsCompleteTree()                
 {                                  
     Test_Header;                   
     TreeNode *a = CreateNode('a'); 
     TreeNode *b = CreateNode('b'); 
     TreeNode *c = CreateNode('c'); 
     TreeNode *d = CreateNode('d'); 
     TreeNode *e = CreateNode('e'); 
     TreeNode *f = CreateNode('f'); 
     TreeNode *g = CreateNode('g'); 
     a->lchild = b;                 
     a->rchild = c;                 
     c->rchild = f;                 
     b->lchild = d;                 
     b->rchild = e;                 
     e->lchild = g;                
     //对一棵非完全二叉树进行判断测试
     int ret = IsCompleteTree(a);   
     printf("expected ret = 0,actual ret = %d\n",ret);
     //将树修改为完全二叉树再次测试
     c->rchild = NULL;
     e->lchild = NULL;
     ret = IsCompleteTree(a);
     printf("expected ret = 1,actual ret = %d\n",ret);

 }

测试结果:
这里写图片描述
由先序和中序遍历结果创建一棵二叉树

//由前序和中序结果创建树
//辅助查找终于结果中当前根节点的左右子树区间的函数
int Find(TreeDataType to_find,TreeDataType in_order[],int left,\
         int right)
{
    int i = left;
    for(;i < right;i++)
    {
        if(to_find == in_order[i])
        {
            return i;
        }
    }
    return -1;
}

TreeNode *_TreeRebuild(TreeDataType pre_order[],\
           int *pre_order_index,int size,TreeDataType in_order[],\
           int left,int right)
{
    if(left >= right)
    {
        //区间无效,说明为一棵空树
        return NULL;
    }
    if(pre_order_index == NULL)
    {
        //非法输入
        return NULL;
    }
    if(*pre_order_index >= size)
    {
        //遍历结束
        return NULL;
    }
    //以先序结果取当前值构建一个新节点
    //相当于当前子树的根节点
    TreeNode *new_node = CreateNode(pre_order[*pre_order_index]);
    //找到当前节点在中序遍历结果中的位置
    int cur_root_in_order_index = Find(new_node->data,in_order,\
                                       left,right);
    assert(cur_root_in_order_index != -1);
    //更新index值,指向下一个即将被创建为节点的值
    ++(*pre_order_index);
    //左子树区间[left,cur_root_in_order_index)
    //递归的创建当前根节点的左子树
    new_node->lchild =  _TreeRebuild(pre_order,pre_order_index,\
                     size,in_order,left,cur_root_in_order_index);
    //右子树区间[cur_root_in_order_index+1,right)
    //递归的创建当前根节点的右子树
    new_node->rchild =  _TreeRebuild(pre_order,pre_order_index,\
                   size,in_order,cur_root_in_order_index+1,right);
    return new_node;
}
//辅助创建二叉树的函数
TreeNode *TreeRebuild(TreeDataType pre_order[],\
            TreeDataType in_order[],int size)
{
    int pre_order_index = 0;
    int in_order_left = 0;
    int in_order_right = size;
    return _TreeRebuild(pre_order,&pre_order_index,\
             size,in_order,in_order_left,in_order_right);
}
//由前序和中序结果创建树的函数测试
void TestRebuild()
{
    Test_Header;
    //先序结果
    TreeDataType pre_order[] = "abdegcf";
    //中序结果
    TreeDataType in_order[] = "dbgeacf";
    //调用创建函数
    TreeNode *new_node = TreeRebuild(pre_order,in_order,7);
    printf("递归先序遍历结果:");
    TreePreOrder(new_node);
    printf("非递归先序遍历结果:");
    TreePreOrderByLoop(new_node);
    printf("\n");
    printf("递归中序遍历结果:");
    TreeInOrder(new_node);
    printf("非递归中序遍历结果:");
    TreeInOrderByLoop(new_node);
    printf("\n");
    printf("递归后序遍历结果:");
    TreePostOrder(new_node);
    printf("非递归后序遍历结果:");
    TreePostOrderByLoop(new_node);
    printf("\n");
}

测试结果:观察遍历结果均正确,说明创建成功
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值