数据结构——二叉树的链式存储操作集合

二叉树是一种非常重要的数据结构,它是很多算法的基础,下面给出了包含二叉树的所有基本操作:

  1. 二叉树的数据结构
  2. 二叉树的递归遍历
  3. 二叉树的非递归遍历
  4. 二叉树常用操作

1. 二叉树的数据结构

//--------数据结构------------------------------------------------
typedef char datatype;
typedef struct bintreenode
{
    datatype data;
    struct bintreenode* lchild,*rchild;
}bintree;
//---------------------------------------------------------------

2. 二叉树的递归遍历

递归实现,前序遍历,中序遍历,后序遍历

void preorder(bintree* bt){
    if (bt)
    {
        printf("%c",bt->data);
        preorder(bt->lchild);
        preorder(bt->rchild);
    }
}
void inorder(bintree* bt){
    if (bt)
    {
        inorder(bt->lchild);
        printf("%c",bt->data);
        inorder(bt->rchild);
    }
}
void postorder(bintree* bt){
    if (bt)
    {
        postorder(bt->lchild);
        postorder(bt->rchild);
        printf("%c",bt->data);
    }
}

创建二叉树

//如果使用bintree* bt,只是把实参的指针值拷贝过来了,要想改变实参指向的空间数据,必须使用指针的指针
void createtree(bintree** bt){
    char ch;
    scanf("%c",&ch);
    if (ch == '#')
    {
        *bt = NULL;
    }
    else
    {
        *bt = (bintree*)malloc(sizeof(bintree));
        (*bt)->data = ch;
        createtree(&(*bt)->lchild);
        createtree(&(*bt)->rchild);
    }
}
bintree* createtree1(bintree* bt){
    char ch;
    scanf("%c",&ch);
    if (ch == '#')
    {
        bt = NULL;
        return bt;
    }
    else
    {
        bt = (bintree*)malloc(sizeof(bintree));
        (bt)->data = ch;
        createtree(&(bt)->lchild);
        createtree(&(bt)->rchild);
        return bt;
    }
}

3. 二叉树的非递归遍历

自定义栈

//二叉树的非递归实现,递归到非递归经常需要借用栈来实现,栈定义如下
typedef struct stack{
    bintree* data[100]; //存放指针数组较好
    int tag[100]; //为栈中每个元素设置标记,只用于后序遍历
    int top;
}seqstack;
//这里设计top指向栈顶,这个栈顶是有元素的,而之前设计的栈的top指向的栈顶是空。
void push(seqstack* s,bintree* t){
    s->data[++s->top] = t;
}
bintree* pop(seqstack* s){
    assert(s->top > -1);
    if (s->top != -1)
    {
        s->top--;
        bintree* rtn = s->data[s->top+1];
        return rtn;
    }
}
//-----------------------------------------------------------------------

非递归实现前中后序遍历

//采用上述的栈来实现
void preorder1(bintree* t){
    seqstack s;
    s.top = -1;
    while(t || s.top != -1)
    {
        while(t)
        {
            printf("%c",t->data);
            push(&s,t);
            t = t->lchild;
        }
        if (s.top != -1)
        {
            t = pop(&s);
            t = t->rchild;
        }
    }
}
//直接在函数中设计一个栈,实现中序遍历
void inorder1(bintree* t)
{
    bintree* stack[100];
    int top = -1;
    while(t || top != -1)
    {
        while(t)
        {
            stack[++top] = t;
            t = t->lchild;
        }
        if (top != -1)
        {
            t = stack[top--];
            printf("%c",t->data);
            t = t->rchild;
        }
    }
}
//直接在函数中设计一个栈,实现后序遍历
void postorder1(bintree* t)
{
    bintree* stack[100];
    int flag[100] = {0};
    int top = -1;
    while(t || top != -1)
    {
        while(t)
        {
            stack[++top] = t;
            t = t->lchild;
        }
        while (flag[top] == 1 && top != -1)
        {
            t = stack[top--];
            printf("%c",t->data);
        }
        if (top != -1)
        {
            t= stack[top];
            t = t->rchild;
            flag[top] = 1;
        }
        else
        {
            t = NULL;
        }

    }
}

4.二叉树常用操作

## 1. 二叉树查找

bintree* locate(bintree* t,datatype x)
{
    bintree *p;
    if (!t)
    {
        return NULL;
    }
    else
    {
        if (t->data == x)
        {
            return t;
        }
        else
        {
            p = locate(t->lchild,x);
            if (p)
            {
                return p;
            }
            else
            {
                return locate(t->rchild,x);
            }
        }
    }
}

2. 统计二叉树中节点数

int num_of_node1(bintree* t)
{
    if (t == NULL)
    {
        return 0;
    }
    else
    {
        return num_of_node1(t->lchild)+num_of_node1(t->rchild)+1;
    }
}
int num_of_node2(bintree*t)
{
    if (t == NULL)
    {
        return 0;
    }
    else if (t->lchild == NULL && t->rchild == NULL)
    {
        return 1;
    }
    else
    {
        return num_of_node2(t->lchild)+num_of_node2(t->rchild);
    }
}

3. 判断二叉树是否等价

int isequal1(bintree* t1,bintree* t2)
{
    int isequal = 0;
    if (t1==NULL && t2 == NULL)
    {
        return 1;
    }
    else
    {
        if (t1->data == t2->data)
        {
            if (isequal1(t1->lchild,t2->lchild))
            {
                isequal = isequal1(t1->rchild,t2->rchild);
            }
        }
        return isequal;
    }
}
//无非就是遍历每一个节点,然后比较,isequal1采用前序遍历,isequal2则采用中序遍历
int isequal2(bintree* t1,bintree* t2)
{
    int isequal = 0;
    if (t1==NULL && t2 == NULL)
    {
        return 1;
    }
    else
    {
        if (isequal1(t1->lchild,t2->lchild))
        {
            if (t1->data == t2->data)
            {
                isequal = isequal1(t1->rchild,t2->rchild);
            }
        }
        return isequal;
    }
}

4. 求二叉树的深度(高度)

int depth1(bintree* t)
{
    int h,lh,rh,max;
    if (t == NULL)
    {
        return 0;
    }
    else
    {
        lh = depth1(t->lchild);
        rh = depth1(t->rchild);
        max = lh >= rh ? lh:rh;
        h = max + 1;
    }
    return h;
}

5. 层次遍历二叉树

void levelorder(bintree* t)
{
    //建立循环队列,能满足7层的满二叉树遍历
    const int maxsize = 64;
    bintree* queue[maxsize]; 
    int front,rear;
    //利用队列实现层次遍历
    //front 指向最先进入的元素,rear指向队尾(待插入的元素位置),不考虑队列满的情况(rear+1)%maxsize = front
    front = 0;rear = 1;
    queue[0] = t;  //根结点插入
    bintree* p;
    while(front != rear)
    {
        p = queue[front];
        front = (front+1)%maxsize;
        printf("%c",p->data);
        //上面出了一次队列,所以不用检查队列满的情况。
        if (p->lchild != NULL)
        {
            queue[rear] = p->lchild;
            rear = (rear+1)%maxsize;
        }
        if (p->rchild != NULL && (rear+1)%maxsize != front)
        {
            queue[rear] = p->rchild;
            rear = (rear+1)%maxsize;
        }
    }
}
void levelorder2(bintree* t)
{
    queue<bintree*> que;
    bintree* p;
    if(t)
        que.push(t);
    do 
    {
        p = que.front();
        que.pop();
        cout << p->data << " ";
        if (p->lchild)
        {
            que.push(p->lchild);
        }
        if (p->rchild)
        {
            que.push(p->rchild);
        }
    } while (!que.empty());
}
//插入一个空指针表示一层的结束
void levelorder3(bintree* t)
{
    queue<bintree*> que;
    bintree* p;
    que.push(t);
    que.push(0);

    while(!que.empty())
    {
        p = que.front();
        que.pop();
        if (p)
        {
            cout << p->data << "  ";
            if (p->lchild)
            {
                que.push(p->lchild);
            }
            if (p->rchild)
            {
                que.push(p->rchild);
            }
        }
        else if (!que.empty())
        {
            que.push(0);
            cout << endl;
        }

    }
}

6. 试分别采用递归和非递归方式编写两个函数,求一颗给定二叉树中叶子节点的个数

int leafnum1(bintree* t)
{
    int num =0;
    if (t == NULL)
    {
        return 0;
    }
    else if (t->lchild == NULL&&t->rchild == NULL)
    {
        num += 1;
    }
    else
    {
        num += leafnum1(t->lchild);
        num += leafnum1(t->rchild);
    }
    return num;
}
int leafnum2(bintree* t)
{
    int num = 0;
    bintree* stack[100];
    int top = -1;
    while(t != NULL || top != -1)
    {
        while(t)
        {
            if (t->lchild == NULL && t->rchild == NULL)
            {
                num += 1;
            }
            stack[++top] = t;
            t = t->lchild;
        }

        if (top != -1)
        {
            t = stack[top--];
            t = t->rchild;
        }
    }
    return num;
}

7. 将一颗二叉树中的所有节点的左右子女互换

void switchchild(bintree* t)
{
    bintree* p;
    if (t == NULL)
    {
        return;
    }
    if (t->lchild != NULL || t->rchild != NULL)
    {
        p = t->lchild;
        t->lchild = t->rchild;
        t->rchild = p;
        switchchild(t->lchild);
        switchchild(t->rchild);
    }
}

8. 判断一颗给定二叉树是否是完全二叉树。

int is_completetree(bintree* t)
{
    //层次遍历,最后一层,当出现空节点时,后面就不能再出现左子树,右子树了。
    if (t == NULL)
    {
        return 0;
    }
    int flag = 0;
    //建立循环队列,能满足7层的满二叉树遍历
    const int maxsize = 64;
    bintree* queue[maxsize]; 
    int front,rear;
    //利用队列实现层次遍历
    //front 指向最先进入的元素,rear指向队尾(待插入的元素位置),不考虑队列满的情况(rear+1)%maxsize = front
    front = 0;rear = 1;
    queue[0] = t;  //根结点插入
    bintree* p;
    while(front != rear)
    {
        p = queue[front];
        front = (front+1)%maxsize;
        if (p->lchild == NULL || p->rchild == NULL) //有叶子节点了
        {
            flag = 1;
        }
        //上面出了一次队列,所以不用检查队列满的情况。
        if (p->lchild != NULL)
        {
            if (flag) //如果出现叶子节点,就不能再有左右子节点了。
            {
                return 0;
            }
            queue[rear] = p->lchild;
            rear = (rear+1)%maxsize;
        }
        if (p->rchild != NULL && (rear+1)%maxsize != front)
        {
            if (flag)
            {
                return 0;
            }
            queue[rear] = p->rchild;
            rear = (rear+1)%maxsize;
        }
    }
    return 1;
}

9. 求前序遍历下的最后一个结点DLR

bintree* last_node_of_preorder(bintree* t)
{
    if (t ==NULL)
    {
        return NULL;
    }

    while(t)
    {
        if (t->rchild != NULL)
        {
            t = t->rchild;
            continue;
        }
        if (t->lchild != NULL)
        {
            t = t->lchild;
            continue;
        }
        break;
    }
    return t;
}

10. 求中序遍历下的最后一个结点LDR

bintree* last_node_of_inorder(bintree* t)
{
    if (t == NULL)
    {
        return NULL;
    }
    //假设t为根结点,若t有右子树,则只需遍历右子树中的右子树
    if (t->rchild != NULL)
    {
        while(t->rchild)
        {
            t = t->rchild;
        }
    }
    //如果没有右子树,则直接输出跟结点。
    return t;
}

11. 求后序序遍历下的最后一个结点LRD

bintree* last_node_of_postorder(bintree* t)
{
    //就是根结点。
    return t;
}

12. 求从根结点到结点p之间的路径长度

int path_length(bintree* t, datatype p)
{
    if (t == NULL)
    {
        return -1;
    }
    bintree* stack[20];
    int tag[20];
    int top = -1;
    while(t != NULL || top != -1)
    {
        while(t)
        {
            stack[++top] = t;
            tag[top] = 0;
            t = t->lchild;
        }
        while(tag[top] == 1 && top != -1)
        {
            t = stack[top];
            if (t->data == p)
            {
                return top;
            }
            top--;
        }
        if (top != -1)
        {
            t = stack[top];
            tag[top] = 1;
            t = t->rchild;
        }
        else
        {
            t = NULL;
        }
    }
}

13. 求pq结点的共同祖先

//如果数据结构中有父节点这个指针就比较容易了。
void postfind(bintree* t,bintree * stack[],int* tag,int& top,datatype x)
{
    while(t || top != -1)
    {
        while(t)
        {
            stack[++top] = t;
            tag[top] = 0;
            t = t->lchild;
        }
        while (tag[top] == 1 && top != -1)
        {
            t = stack[top--];
            if (t->data == x)
            {
                return;
            }
        }
        if (top != -1)
        {
            t= stack[top];
            t = t->rchild;
            tag[top] = 1;
        }
        else
        {
            t = NULL;
        }

    }
}
bintree* common_parent(bintree* t, datatype p,datatype q)
{
    if (t == NULL){
        return NULL;
    }
    if (t->data == p || t->data ==q){
        return t;
    }
    bintree* stack1[20]; //p
    bintree* stack2[20]; //q
    int tag1[20];
    int tag2[20];
    int top1 = -1;
    int top2 = -1;
    postfind(t,stack1,tag1,top1,p);
    postfind(t,stack2,tag2,top2,q);

    //后序序遍历下来根结点都在树当中,只需从min开始往下比较算起
    int min = top1 > top2?top1:top2;
    for (int i = min;i>=0;i--)
    {
        if (stack1[min] == stack2[min])
        {
            return stack1[min];
        }
        min--;
    }
}

14. 求指定结点所在层数

//等价于p点到结点直接的路径长度,用后序遍历,后序遍历存储的是根结点。
int nodes_level(bintree* t, datatype p)
{
    if (t == NULL)
    {
        return -1;
    }
    bintree* stack[20];
    int tag[20];
    int top = -1;
    while(t != NULL || top != -1)
    {
        while(t)
        {
            stack[++top] = t;
            tag[top] = 0;
            t = t->lchild;
        }
        while(tag[top] == 1 && top != -1)
        {
            t = stack[top];
            if (t->data == p)
            {
                return top + 1;
            }
            top--;
        }
        if (top != -1)
        {
            t = stack[top];
            tag[top] = 1;
            t = t->rchild;
        }
        else
        {
            t = NULL;
        }
    }
}

15. 求二叉树的镜像

//先交换再递归,和先递归在交换都可以
void mirro(BTree** t)
{
    if (*t == NULL)
    {
        return;
    }
    if ((*t)->lchild || (*t)->rchild)
    {
        BTree* temp = (*t)->lchild;
        (*t)->lchild = (*t)->rchild;
        (*t)->rchild = temp;
    }
    mirro(&(*t)->lchild);
    mirro(&(*t)->rchild);
}

测试代码

#include "bintree_link.h"


int _tmain(int argc, _TCHAR* argv[])
{
    //treenode* root = NULL;
    //createtree(&root);
    //preorder(root);

    bintree* root = NULL;
    createtree(&root); //ABD#E##FG###C##
    preorder1(root);
    inorder1(root);
    postorder1(root);
    levelorder(root);
    int a = leafnum1(root);
    int b = leafnum2(root);
    switchchild(root);
    preorder1(root);
    printf("%d",path_length(root, 'D'));

    printf("%c",common_parent(root,'E','B')->data);
    printf("%d",nodes_level(root,'D'));
    system("pause");
    return 0;
}

最后,二叉树的递归和非递归是其他各种操作实现的基础。而这其中,又将栈利用进来了,在层次遍历中,利用了队列,所以熟练写出二叉树常用操作时非常有必要的。源码可以在

https://code.csdn.net/snippets/817589)中找到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值