算法与数据结构-二叉树的基本操作C语言实现

序言

二叉树这部分当时学习C语言的时候就没有特别重视,现在遇到这类题就比较头疼,所以需要重新复习一下。


二叉树的基本操作包括哪些

  • 二叉树的建立

    • 逐个结点输入
    • 广义表方式输入
  • 广义表方式输出二叉树

  • 二叉树的销毁

  • 求二叉树的深度

  • 二叉树的遍历

  • 统计二叉树的结点数

  • 统计二叉树的叶子结点数

  • 交换左右子树

  • 复制二叉树

  • 查找某一结点

  • 删除二叉树的某一结点


1. 二叉树的定义
typedef ElemType char;     //自定义数据域类型

struct BinaryTree
{
    ElemType data;                //数据域
    struct BinaryTree *lchild;    //左子树
    struct BinaryTree *rchild;    //右子树
};

typedef struct BinaryTree TreeNode;      //给结构体struct BinaryTree取个别名叫TreeNode
typedef struct BinaryTree * BiTree;      //给结构体指针struct BinaryTree *取个别名叫BiTree


//初始化
void IniBTree(BiTree BT)
{
    BT = NULL;
    return;
}


2. 二叉树的建立
  • 方法1:逐个结点输入
/* 先序创建二叉树:根结点 -> 左子树 -> 右子树 */
void CreateBTree(BiTree BT)
{
    char ch;
    ch = getchar();        //头文件<stdio.h>,相当于scanf("%c", &ch)
    if (ch == ' ')
        BT = NULL;
    else
    {
        if(! (BT = (BiTree)malloc(sizeof(TreeNode))))
            exit(1);
        BT -> data = ch;         //生成根节点
        BT -> lchild = NULL;   //构造左子树
        BT -> rchild = NULL;   //构造右子树
        CreateBiTree(BT -> lchild);
        CreateBiTree(BT -> rchild);
    }
}
  • 方法2:利用广义表建立二叉树
    • 即根据广义表 A(B(C,D),E(,F(G)))的输入,建立二叉树
int StackMaxSize = 3;

void CreateBTreeWithGenList(BiTree BT, char *string)
{
    BiTree TempNode;
    Bitree s[StackMaxSize];    //定义数组为存储各结点的指针的栈
    int top = -1;     //栈顶指针,初始值为-1
    int branchValue;  //分支值,作为处理结点的标志,1表示处理左子树,2表示处理右子树
    int i = 0;        //数组计数元素

    //原二叉树初始化
    BT = NULL;

    //开始读入广义表字符串
    while (string[i])
    {
        switch(string[i])
        {
            case ' ': break;

            case '(':
            {
                if (top == StackMaxSize - 1)
                {
                    printf("栈空间太小,需要增大栈空间!\n");
                    exit(1);
                }
                top++;
                s[top] = TempNode;
                k = 1;                //切换到左子树
                break;              
            }

            case ')':
            {
                if (top == -1)
                {
                    printf("二叉树广义表字符串错误\n");
                    exit(1);
                }
                top--;
            }

            case ',': k = 2; break;   //切换到右子树 

            default:                  //默认情况下,即读取到字母字符,对应赋值。
            {
                TempNode = (BiTree)malloc(sizeof(TreeNode));
                TempNode -> data = string[i];                    //临时结点赋值
                TempNode -> lchild = TempNode -> rchild = NULL;
                if (BT == NULL)
                    BT = TempNode;           //根节点赋值
                else
                {
                    if (k == 1)
                        s[top] -> lchild = TempNode;
                    else
                        s[top] -> rchild = TempNode;
                }
            }
        } //switch结束
    i++;
    }  //while结束
}


3. 广义表方式输出二叉树
void PrintBTreeWithGenList(BiTree BT)
{
    if (BT)
    {
        printf("%c", BT -> data);

        if (BT -> lchild || BT -> rchild)
        {
            printf("(");
            PrintBTreeWithGenList(BT -> lchild);
            if (BT -> rchild)
            {
                printf(",");
                PrintBTreeWithGenList(BT -> rchild);
            }
            printf(")");
        }
    }
}


4. 二叉树的销毁
void DestroyBTree(BiTree BT)
{
    if (BT)
    {
        DestroyBTree(BT -> lchild);
        DestroyBTree(BT -> rchild);
        free(BT);
        BT == NULL;
    }
}


5. 检查二叉树是否为空
int BTreeEmpty(BiTree BT)
{
    if (BT == NULL)
        return 1;
    else
        return 0;
}


6. 求二叉树的深度
//递归方式计算二叉树的深度,每一层的深度都能够得到比较

int BTreeDepth(BiTree BT)
{
    if (BT == NULL)
        return 0;
    else
    {
        int depth1 = BTreeDepth(BT -> lchild);     //计算左子树的深度
        int depth2 = BTreeDepth(BT -> rchild);     //计算右子树的深度
        if (depth1 > depth2)
            return depth1 + 1;
        else
            return depth2 + 1;
    }
}


//或递归方式求二叉树的深度
int BTreeDepth(BiTree BT)
{
    if (BT == NULL)
        return 0;
    else
    {
        int ldepth = BTreeDepth(BT -> lchild);
        int rdepth = BTreeDepth(BT -> rchild);
    }
    return (ldepth > rdepth) ? (ldepth + 1) : (rdepth + 1);
}


7. 二叉树的遍历
/* 前序遍历:根节点 -> 左子树 -> 右子树 */
void PreOrderTraverse(const BiTree BT)
{
    if (!BT)
        return;
    //遍历
    printf("%c ", BT -> data);        //根节点
    PreOrderTraverse(BT -> lchild);
    PreOrderTraverse(BT -> rchild);
}


/* 中序遍历:左子树 -> 根节点 -> 右子树 */
void InOrderTraverse(const BiTree BT)
{
    if (!BT)
        return;
    //遍历
    InOrderTraverse(BT -> lchild);
    printf("%c ", BT -> data);        //根节点
    InOrderTraverse(BT -> rchild);
}


/* 后序遍历:左子树 -> 右子树 -> 根节点 */
void PostOrderTraverse(const BiTree BT)
{
    if (!BT)
        return;
    //遍历
    PostOrderTraverse(BT -> lchild);
    PostOrderTraverse(BT -> rchild);
    printf("%c ", BT -> data);        //根节点
}


/* 层序遍历:逐层从左到右 */
void LevelOrderTraverse(BiTree BT)
{
    BiTree temp;
    BiTree queue[QueueMaxSize];      //定义队列所使用的数组,元素类型为指向结点的指针类型
    int front = 0;
    int rear = 0;
    if (BT != NULL)                  //根结点入队
    {
        queue[rear] = BT;
        rear = (rear + 1) % QueueMaxSize;
    }
    while (front != rear)
    {
        temp = queue[front];
        front = (front + 1) % QueueMaxSize;
        printf("%c ", temp -> data);
        if (temp -> lchild != NULL)     //输出结点存在左子树
        {
            queue[rear] = temp -> lchild;
            rear = (rear + 1) % QueueMaxSize;
        }
        if (temp -> rchild != NULL)
        {
            queue[rear] = temp -> rchild;
            rear = (rear + 1) % QueueMaxSize;
        }
    }
}
//1. 层序遍历需要使用一个队列,开始把整个树的根结点入队,然后每从队列中删除一个结点并输出该结点时,都把它的非空的左右孩子入队,当队列为空时算法结束。
//2. 算法中,队列的最大长度不会超过二叉树中相邻的两层的最大结点数,所以提前在程序开始处定义最大队列长度QueueMaxSize大于队列的最大长度,就无需考虑队列溢出的问题了


8. 统计二叉树的结点数
/* 二叉树的结点数 = 左子树 + 右子树 + 1 */
int BTreeNodeCount(BiTree BT)
{
    if (BT == NULL)
        return 0;
    else
        return BTreeNodeCount(BT -> lchild) + BTreeNodeCount(BT -> rchild) + 1;
}


9. 统计二叉树的叶子结点数
/* 叶子结点:没有左右子树的结点 */
int BTreeLeafCount(BiTree BT)
{
    if (BT == NULL)
        return 0;
    if (BT -> lchild == NULL && BT -> rchild == NULL)
        return 1;
    //递归实现
    return BTreeLeafCount(BT -> lchild) + BTreeLeafCount(BT -> rchlid);
}


10. 统计二叉树度为1的结点
/* 统计二叉树的度为1的结点个数 */
//度:一个结点的子结点的个数
int Degree1Count(BiTree BT)
{
    if(!BT) 
        return 0;
    if ((!BT -> lchild && BT -> rchild) || (BT -> lchild && !BT -> rchild))
        return 1;
    else
        return Degree1Count(BT -> lchild) + Degree1Count(BT -> rchild);
}


11. 交换左右子树
/* 相当于对称翻转,递归实现 */
void ExchangeChildTree(BiTree BT)
{
    if (BT)
    {
        BiTree Temp = NULL;
        if (BT -> lchild || BT -> rchild)
        {
            ExchangeChildTree(BT -> lchild);
            ExchangeChildTree(BT -> rchild);
            Temp = BT -> lchild;
            BT -> lchild = BT -> rchild;
            BT -> rchild = Temp;
        }
    }
}


12. 复制二叉树
/* 递归实现 */
BiTree CopyBTree(BiTree BT)
{
    BiTree P, lchild, rchild;
    if (BT == NULL)
        return;
    lchild = CopyBTree(BT -> lchild);
    rchild = CopyBTree(BT -> rchild);
    P = (BiTree)malloc(sizeof(struct BinaryTree));
    P -> data = BT -> data;
    P -> lchild = lchild;
    P -> rchild = rchild;
    return P;
}


13. 查找某一结点
/* 查找某一结点:查找成功返回结点位置,失败返回NULL */

BiTree* FindBTree(BiTree BT, ElemType x)
{
    if (BT == NULL)
        return NULL;
    else                       //递归查找
    {
        if (x == BT -> data)
            return BT;
        else
        {
            ElemType* p;
            if (p = FindBTree(BT -> lchild, x))
                return p;
            if (p = FindBTree(BT -> rchild, x))
                return p;
            return NULL;
        }
    }
}


14. 删除二叉树的某一结点
  • 说明:二叉树删除结点有三种情况
    • [1] 该结点没有子结点:直接删除,并修改其父结点
    • [2] 该结点只有一个子结点:将该结点的子结点直接提升至该结点处,并修改相应的指针
    • [3] 若该z结点有两个子结点:找到z结点的中序后继结点y,并用y的右子树替换y结点,y结点替换z结点,z的左子树置为y的左子树。如下图所示



//此时的二叉树结构体定义修改为

typedef ElemType char;     //自定义数据域类型

struct BinaryTree
{
    ElemType data;                //数据域
    struct BinaryTree *prev;      //前驱结点,该指针的目的是为了方便找到父结点
    struct BinaryTree *lchild;    //左子树
    struct BinaryTree *rchild;    //右子树
};

typedef struct BinaryTree *BiTree;


//删除某一结点方式:先找到后删除
int DeleteBTreeNode(BiTree BT, ElemType x)
{
    //查找到该元素所在的结点位置,返回结点指针!
    BiTree temp = FindBTree(BT, x);
    if (temp == NULL)
        return 0;
    //1. 没有子结点
    if (temp -> lchild == NULL && temp -> rchild == NULL)
    {
        if (temp -> prev -> lchild == temp)      //如果是父结点的左子树
            temp -> prev -> lchild = NULL;
        else
            temp -> prev -> rchild = NULL;
        free(temp);
    }
    //2. 只有左子树
    if (temp -> rchild == NULL)
    {
        if (temp -> prev -> lchild == temp)     //如果是父结点的左子树
            temp -> prev -> lchild = temp -> lchild;
        else
            temp -> prev -> rchild = temp -> lchild;
        free (temp);
    }
    //3. 只有右子树
    if (temp -> lchild == NULL)
    {
        if (temp -> prev -> lchild == temp)      //如果是父结点的左子树
            temp -> prev -> lchild = temp -> rchild;
        else
            temp -> prev -> rchild = temp -> rchild;
        free (temp);
    }
    //4. 既有左子树又有右子树
    else
    {
        BiTree y = temp -> rchild;    //寻找中序后继结点y,即temp结点右子树的最左子树
        while (y -> lchild != NULL)
            y = y -> lchild;
        if (y == temp -> rchild)      //y是要删结点的右子树
        {
            //判断要删结点是其父结点的左子树还是右子树
            if (temp -> prev -> lchild == temp)     //左子树
            {
                temp -> prev -> lchild = y;
                y -> lchild = temp -> lchild;
                temp -> lchild -> prev = y;
            }
            else
            {
                temp -> prev -> rchild = y;
                y -> lchild = temp -> lchild;
                temp -> lchild -> prev = y;
            }
        }
        else
        {
            y -> prev -> lchild = y -> rchild;      //注意y的右子树可能不存在
            //同上。判断要删除结点是其父结点的左子树还是右子树
            if (temp -> prev -> lchild == temp)     //左子树
            {
                temp -> prev -> lchild = y;
                y -> lchild = temp -> lchild;
                y -> rchild = temp -> rchild;
                temp -> lchild -> prev = y;
                temp -> rchild -> prev = y;
            }
            else
            {
                temp -> prev -> rchild = y;
                y -> lchild = temp -> lchild;
                y -> rchild = temp -> rchild;
                temp -> lchild -> prev = y;
                temp -> rchild -> prev = y;
            }
        }
        free (temp);
    }
    return 1;
}

//注:为避免冗长,可将上述判断左右子树及结点替换的部分单独写一个函数实现。



Acknowledgements:
http://blog.csdn.net/sysu_arui/article/details/7865876
http://blog.csdn.net/wtfmonking/article/details/17038077(推荐)
http://blog.csdn.net/lub0807/article/details/37969059
http://blog.csdn.net/nzh1234/article/details/31076401
http://blog.csdn.net/heart_love/article/details/50943089

2017.08.20

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
16进制10进制.txt 32.txt asm.txt Crctable.txt C标志符命名源程序.txt erre.txt erre2.txt ff.txt for循环的.txt list.log N皇后问题回溯算法.txt ping.txt re.txt source.txt winsock2.txt ww.txt 万年历.txt 万年历的算法 .txt 乘方函数桃子猴.txt 乘法矩阵.txt 二分查找1.txt 二分查找2.txt 二叉排序树.txt 二叉树.txt 二叉树实例.txt 二进制数.txt 二进制数2.txt 余弦曲线.txt 余弦直线.txt 傻瓜递归.txt 冒泡排序.txt 冒泡法改进.txt 动态计算网络最长最短路线.txt 十五人排序.txt 单循环链表.txt 单词倒转.txt 单链表.txt 单链表1.txt 单链表2.txt 单链表倒序.txt 单链表的处理全集.txt 双链表正排序.txt 反出字符.txt 叠代整除.txt 各种排序法.txt 哈夫曼算法.txt 哈慢树.txt 四分砝码.txt 四塔1.txt 四塔2.txt 回文.txt 图.txt 圆周率.txt 多位阶乘.txt 多位阶乘2.txt 大加数.txt 大小倍约.txt 大整数.txt 字符串查找.txt 字符编辑.txt 字符编辑技术(插入和删除) .txt 完数.txt 定长串.txt 实例1.txt 实例2.txt 实例3.txt 小写数字转换成大写数字1.txt 小写数字转换成大写数字2.txt 小写数字转换成大写数字3.txt 小字库DIY-.txt 小字库DIY.txt 小孩分糖果.txt 小明买书.txt 小白鼠钻迷宫.txt 带头结点双链循环线性表.txt 平方根.txt 建树和遍历.txt 建立链表1.txt 扫描码.txt 挽救软盘.txt 换位递归.txt 排序法.txt 推箱子.txt 数字移动.txt 数据结构.txt 数据结构2.txt 数据结构3.txt 数组完全单元.txt 数组操作.txt 数组递归退出.txt 数组递归退出2.txt 文件加密.txt 文件复制.txt 文件连接.txt 无向图.txt 时间陷阱.txt 杨辉三角形.txt 栈单元加.txt 栈操作.txt 桃子猴.txt 桶排序.txt 检出错误.txt 检测鼠标.txt 汉字字模.txt 汉诺塔.txt 汉诺塔2.txt 灯塔问题.txt 猴子和桃.txt 百鸡百钱.txt 矩阵乘法动态规划.txt 矩阵转换.txt 硬币分法.txt 神经元模型.txt 穷举搜索法.txt 符号图形.txt 简单数据库.txt 简单计算器.txt 简单逆阵.txt 线性顺序存储结构.txt 线索化二叉树.txt 绘制圆.txt 编随机数.txt 网络最短路径Dijkstra算法.txt 自我复制.txt 节点.txt 苹果分法.txt 螺旋数组1.txt 螺旋数组2.txt 试题.txt 诺汉塔画图版.txt 读写文本文件.txt 货郎担分枝限界图形演示.txt 货郎担限界算法.txt 质因子.txt 输出自已.txt 迷宫.txt 迷宫问题.txt 逆波兰计算器.txt 逆矩阵.txt 逆阵.txt 递堆法.txt 递归桃猴.txt 递归车厢.txt 递推.txt 逻辑移动.txt 链串.txt 链栈.txt 链表十五人排序.txt 链表(递归).txt 链队列.txt 队列.txt 阶乘递归.txt 阿姆斯特朗数.txt 非递归.txt 顺序栈.txt 顺序表.txt 顺序队列.txt 骑士遍历1.txt 骑士遍历2.txt 骑士遍历回逆.txt 黑白.txt

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值