欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-08 11:59:47
序言 二叉树这部分当时学习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();        //头文件,相当于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的左子树。如下图所示
算法与数据结构-二叉树的基本操作C语言实现
//此时的二叉树结构体定义修改为

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;
}

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