算法与数据结构(c语言)——BinaryTree(二)

二叉树的存储结构

  1. 顺序存储结构 

    就是用一维数组来存储二叉树的节点,并且节点存储位置就是数组下标能体系节点之间的逻辑关系。

    顺序存储结构一般只用于完全二叉树。对于一般的二叉树,尽管层序编号不能反映逻辑关系,也可以将其按完全二叉树编号,将不存在的节点位置留空。

    但是(嗯,凡事都有个但是),考虑下极端情况:一棵深度为k的右斜树,只有k个节点,却要分2^(k-1)个存储单元(可能想上天!)。这种空间浪费的毫无意义!

  2. 链式存储结构 

    二叉树每个节点最多有俩孩子。so,脑子里首先蹦出来的就是一个数据域,俩指针域。这样的链表叫做二叉链表。

    在含有n个节点的二叉链表中有n+1个空链域

    一个节点是有两个链域的,那么n个节点必然有2n个链域,有n-1个分支数(每个分支都是一个链域,描述的就是两个节点之间的关系),叶子节点两个链域都是空的。so,空的链域  = 所有的链域 - 不空的链域。即,2n - (n-1) = n+1

    接上代码

    节点定义:
    typedef char TElemType;
    typedef struct BinTNode{
        TElemType data;
        struct BinTNode *lchild,*rchild;
    }BinTNode, *BinTree;
    

     

遍历二叉树

先序遍历(先根)

中序遍历(中根)

后序遍历(后根)

层序遍历(从上而下,从左置右)

没啥好说的,不懂的出门右拐找百度,左拐也行,你开心随你。

翠花,上酸菜。呃错了,上算法实现:

// 先序遍历二叉树
void preOrderTraverse(BinTree t){
    if(t == NULL){
        return NULL;
    }
    printf("%c",t->data);
    preOrderTraverse(t->lchild);
    preOrderTraverse(t->rchild);
}
// 中序遍历二叉树
void inOrderTraverse(BinTree t){
    if(t == NULL){
        return NULL;
    }
    inOrderTraverse(t->lchild);
    printf("%c",t->data);
    inOrderTraverse(t->rchild);
}
// 后序遍历二叉树
void postOrderTraverse(BinTree t){
    if(t == NULL){
        return NULL;
    }
    postOrderTraverse(t->lchild);
    postOrderTraverse(t->rchild);
    printf("%c",t->data);
}
// 层序遍历二叉树,用到了队列来辅助实现
void levelOrderTraverse(BinTree T) {
    LinkQueue *q;
    BinTNode *p = T;
    if((*p).data != '\0') {
        q = LinkQueueInit();
        EnQueue(q,*p);

        printf("\n");
        while(!QueueEmpty(q)) {
            BinTNode temp;
            DeQueue(q,&temp);

            // 简单的访问这个节点,将data输出
            printf("%c ",temp.data);

            if(temp.lchild != NULL) {
                EnQueue(q,*(temp.lchild));
            }
            if(temp.rchild) {
                EnQueue(q, *(temp.rchild));
            }
            free(&temp);
        }
    }
}

非递归实现:

// 中序遍历,使用了栈来辅助实现
void inOrderTraverse(BinTree t){
    LinkStack *ls = InitStack();
    BinTNode *p = t,*temp;

    while(p || !StatckEmpty(ls)){
        if(p){
            Push(ls,*p);
            // 入栈后将指针指向节点的左孩子
            p = p->lchild;
        } else{
            temp = Pop(ls);
            if(temp){
                // 访问节点,我就简单的打印输出。
                printf("%c ",temp->data);
                p = temp->rchild;
                // 将栈中弹出的节点释放掉。
                free(temp);
            }
        }
    }
}
// 另一种实现方式
void inOrderTraverse(BinTree b) {
    BinTNode *stack[MAXSIZE], *p;
    int top = -1;
    if (b != NULL) {
        p = b;
        while (top > -1 || p != NULL) {
            // 扫描p的所有左节点并入栈
            while (p != NULL) {
                top++;
                stack[top] = p;
                p = p->lchild;
            }
            if (top > -1) {
                // 出栈并访问该节点
                p = stack[top];
                top--;
                printf("%c ", p->data);
                // 扫描p的右孩子
                p = p->rchild;
            }
        }
    }
}

辅助队列:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#include "linkqueue.h"

// 队列是否为空
bool QueueEmpty(LinkQueue *lq){
    if (lq->front == NULL){
        return true;
    }
    return false;
}
// 初始化一个不带头节点链队列
LinkQueue* LinkQueueInit(){
    LinkQueue *lq;
    lq = (LinkQueue*)malloc(sizeof(QNode));
    lq->front = NULL;
    lq->rear = NULL;
    return lq;
}
// 入队列,就是单链表的尾插法,rear就是尾指针
Status EnQueue(LinkQueue *lq,BinTNode e){
    QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
    if(!p){
        return ERROR;
    }
    p->data = e;
    p->next = NULL;

    // 判断是否是一个空的队列,若是空的队列,将队头和队尾指向同一节点
    if(lq->front == NULL){
        lq->front = lq->rear = p;

    } else {
        lq->rear->next = p;
        lq->rear = p;
    }

    return OK;
}
// 出队列,从队头出,不就和出栈是一样的吗
Status DeQueue(LinkQueue *lq,BinTNode *v){
    QueuePtr *temp;

    // 当队头指向NULL,才说明这个队列是个空队列
    if(QueueEmpty(lq)){
        return ERROR;
    }
    // 当队列的队头和队尾都指向同一元素节点的时候,将这个节点出队后,把队头和队尾都置为空
    *v = lq->front->data;
    temp = lq->front;

    if(lq->front == lq->rear){
        // QueueEmpty仅仅只是判断队头和队尾是否指向同一元素节点,
        // 因为队列有一个元素的时候,队头和队尾也是指向同一元素。

        lq->rear = lq->front = NULL;
    } else {
        lq->front = lq->front->next;
    }
    // 将零食变量所指向的内存单元释放。
    free(temp);

    return OK;
}

辅助栈:

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#include "linkstack.h"


// 当前栈是否为空栈,栈内元素数量大于0,则返回false
bool StatckEmpty(LinkStack *ls){
    return ls->count > 0 ? false : true;
}
// 入栈操作
Status Push(LinkStack *ls,BinTNode v){
    LinkStackPtr e = (LinkStackPtr)malloc(sizeof(StockNode));
    e->data = v;

    // 把当前的栈顶元素值赋值给新节点的直接后继
    e->next = ls->top;
    // 将新节点赋值给栈顶指针
    ls->top = e;

    ls->count++;

    return OK;
}
// 弹栈
BinTNode *Pop(LinkStack *ls){
    if(StatckEmpty(ls)){
        return NULL;
    }

    BinTNode *v = (BinTNode*)malloc(sizeof(BinTNode));
    *v = ls->top->data;

    // 用来记录要出栈的元素节点
    LinkStackPtr temp;
    temp = ls->top;

    // 使栈顶指针下移,指向后面的节点
    ls->top = ls->top->next;

    // 释放要出栈元素所在空间
    free(temp);

    // 将栈的大小更新
    ls->count--;

    return v;
}
// 获取栈顶元素
void GetTop(LinkStack *ls,BinTNode *v){
    if(!StatckEmpty(ls)){
        if(!v) {
            *v = (BinTNode*)malloc(sizeof(BinTNode));
        }
        *v = ls->top->data;
    }
}
// 获取栈的元素个数
int StatckLength(LinkStack *ls){
    if(!StatckEmpty(ls)){
        return ls->count;
    }
    return -1;
}
// 初始化栈
LinkStack* InitStack(){
    LinkStack *ls;
    ls = (LinkStack *)malloc(sizeof(LinkStack));
    ls->top = NULL;
    ls->count = 0;
}

 main函数:

运行结果:

不足之处,还望指正,蟹蟹!

关于c实现的二叉树非递归方式遍历可以点我,上述代码中的非递归中序遍历另一种实现就是摘自此处。

end

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值