【数据结构】二叉树的基本操作

目录:

二叉树的基本操作

1. 二叉树的创建

二叉树的存储方式哦同样有两种,一种是顺序存储,一种是链式存储。

1.1. 顺序存储

#define MAXSIZE 100
struct TreeNode{
    int data;
    bool isEmpty;
}
TreeNode t[MAXSIZE];
```·
与其他数据结构类似,树的结构同样是先来一个结点,再来一个树的整体。
在树的结构体中,data表示数据域,用来存放树的节点的数据,isEmpty表示树的这个节点是否为空。

对树的节点声明以后,再创建一个以节点类型为数组元素类型的数组,这样树的结构就定义好了。

但由于顺序二叉树对于空间的浪费和查询的困难,顺序二叉树只适合于完全二叉树,所以我们一般使用链式二叉树。

### 链式存储

一个典型的存储结构如下

```c
typedef struct BTNode{
    int data;
    struct node *lchild;
    struct node *rchild;
}BTNode,*BTree;

该结构采用的是二叉树的左右链表示,即一个结构体中有三部分,一部分是二叉树节点本事的数据,其他两部分是指向二叉树下一个节点的指针,一个指向左子树,一个指向右子树。
其中data为数据域,left和right为指针域,分别指向左孩子和右孩子。

2. 二叉树的初始化

二叉树的初始化只需要创建一个二叉树,并让他等于NULL即可。

void InitBTree(BTree *root)
{
    *root=NULL;
}

如果我们要对根节点进行初始化的话,可以采取以下的方式:

(*root)=(BTree)malloc(sizeof(BTNode));
(*root)->lchild=NULL;
(*root)->rchild-NULL;
(*root)->data=2;

3. 二叉树插入节点

如果需要对一个二叉树插入一个节点,我们只需要申请内存然后赋值即可。

BTNode *p=(BTNode *)malloc(sizeof(BTNode));
p->data=2;
p->lchild=NULL;
p->rchild=NULL;
root->lchild=p;

4. 二叉树的遍历

二叉树的遍历有四种基本的方式,分别为先序遍历,中序遍历,后序遍历和层次遍历。其中每一种方式又有两种方式,分别为递归和非递归。

4.1. 递归遍历

递归遍历的方式比较简单,只需要按照先序遍历的顺序,依次递归遍历二叉树即可,并且不同遍历方式的代码相似。

void firstTree(BTree BT)//先序遍历递归
{
    if(BT!=NULL){
        printf("%c",BT->data);
        displayTree(BT->left);
        displayTree(BT->right);
    }
    else
        printf("#");
}
void middleTree(BTree BT)//中序遍历递归
{
    if(BT!=NULL){
        displayTree(BT->left);
        printf("%c",BT->data);
        displayTree(BT->right);
    }
    else
        printf("#");
}
void lastTree(BTree BT)//后序遍历递归
{
    if(BT!=NULL){
        displayTree(BT->left);
        displayTree(BT->right);
        printf("%c",BT->data);
    }
    else
        printf("#");
}

4.2. 层序遍历

层序遍历的基本原理是借助队列的形式,当一个节点被读入以后,我们先将其压入队列末尾。接下来如果队列元素不空的话,我们就将队头的节点弹出,然后访问其值,并且将其左右子树压入队列末尾。以此类推循环往复,就达到了层序遍历的目的。
具体代码实现如下

void levelOrder(BTree BT)
{
    LinkQuene Q;
    InitQuene(Q);
    BTree p;
    EnQuene(Q,BT);
    while(!isEmpty(Q))
    {
        DeQueneu(Q,p);
        visit(p);
        if(p->lchild!=NULL)
            EnQuene(Q,p->lchild);
        if(p->rchild!=NULL)
            EnQuene(Q,p->rchild);
    }
}

4.3. 非递归遍历

非递归遍历需要通过栈的操作来进行,
示例代码如下:

//前序遍历
void firstTree(BTNode* root)
{
    if (root == NULL)
        return;
    BTNode* p = root;
    stack<BTNode*> s;  //创造一个栈,用来存放树节点
    while (!s.empty() || p!=NULL)
    {
        //如果p不为空的话,酒将p压入栈中,然后让p指向左子树
        while (p!=NULL)
        {
            printf("%c",p->data);
            s.push(p);
            p = p->lchild;
        }
        //当p为空时,说明根和左子树都遍历完了,该进入右子树了
        if (!s.empty())
        {
            p = s.top();
            s.pop();
            p = p->rchild;
        }
    }
}

//中序遍历
void middleTree(BTNode* root)
{
    if (root == NULL)
        return;
    BTNode* p = root;
    stack<BTNode*> s;  //创造一个栈,用来存放树节点
    while (!s.empty() || p!=NULL)
    {
        //如果p不为空的话,酒将p压入栈中,然后让p指向左子树
        while (p!=NULL)
        {
            s.push(p);
            p = p->lchild;
        }
        //当p为空时,说明根和左子树都遍历完了,该进入右子树了
        if (!s.empty())
        {
            p = s.top();
            s.pop();
            printf("%c",p->data);
            p = p->rchild;
        }
    }
}

其中前序遍历和中序遍历的方式较为相似,只需要先建立一个栈,然后将访问到的节点存入栈中,节点一直向左指向,直到节点为空。
当节点为空时让指针指向栈顶元素并弹出,然后开始右子树的访问。
前序遍历和中序遍历的区别仅仅在于打印数据的时间不同。

而后序遍历则更加复杂一点

//后序遍历
void PostOrderWithoutRecursion(BTNode* root)
{
    if (root == NULL)
        return;
    stack<BTNode*> s;
    BTNode *p=root;//p用来表示当前访问的节点
    BTNode *q=NULL;//q用来表示上一个访问的节点
    //先把p移动到左子树最下边
    while (p!=NULL)
    {
        s.push(P);
        p = p->lchild;
    }
    while (!s.empty())
    {
        //此时让p已经为空了,先从栈中提取一个元素赋给p
        p = s.top();//现在p的值就是最左下角的节点
        s.pop();
        if (p->rchild == NULL || p->rchild == q) //如果右子树为空或者刚访问过右子树
        {
            printf("%c",p->data);
            q = p;//每次输出值以后,修改上一次访问的节点
        }
        else if(p->lchild==q)//如果左子树已经访问过了
        {
            s.push(p);//让根结点入栈
            p = p->rchild; //进入右子树
            while (p!=NULL)
            {
                s.push(p);
                p = p->lchild;
            }
        }
    }
}

后序遍历需要两个指针,其中一个指针指向的是当前访问的元素,另一个指针指向的是上次访问过的元素。
这样是为了保证左右子树都访问过了以后在访问根节点:当我们访问过左子树以后,弹出根节点,然后需要判断右子树是否访问过或者右子树是否为空,如果是的话就访问根节点,否则就将右子树的根节点压入栈中,然后再访问右子树的根节点的左子树,直到右子树为空或者右子树被访问过,就返回去访问根节点弹栈。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉树是一种重要的数据结构,由于其特殊的结构和性质,需要具备一些基本的操作来对二叉树进行处理。 首先是创建二叉树。可以通过读取用户输入或者其他方式来构建一个二叉树。创建二叉树的过程可以使用递归的方式,通过不断地输入节点的值和连接关系来构造二叉树。 其次是遍历二叉树。常见的遍历方式有前序遍历、中序遍历和后序遍历。前序遍历先访问根节点,然后遍历左子树和右子树;中序遍历按照左子树、根节点和右子树的顺序遍历;后序遍历先遍历左子树和右子树,最后访问根节点。通过递归的方式,可以实现这三种遍历方式。 另外一个常用的操作是查找二叉树中的节点。可以通过比较节点的值,逐层搜索二叉树,找到目标节点。如果目标节点不存在,可以返回一个特定的值来表示找不到。 还有一个重要的操作是插入节点。可以通过比较节点的值,找到插入的位置。如果待插入的节点小于当前节点,就插入到左子树中;如果待插入的节点大于当前节点,就插入到右子树中。插入节点后,需要调整二叉树的结构,保持二叉树的性质。 最后,删除节点也是一个常见的操作。删除节点时,需要考虑节点的左右子树。可以通过将节点的左子树的最大节点或者右子树的最小节点上移来替代被删除的节点。删除节点后,同样需要调整二叉树的结构,保持二叉树的性质。 这些是二叉树基本操作,它们在实际应用中有广泛的应用,比如在搜索、排序和图等领域。掌握这些操作,可以更好地理解和应用二叉树

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值