数据结构之 二叉树(C语言实现)

数据结构之 二叉树(C语言实现)

1. 二叉树的定义

==二叉树(Binary Tree)是n(n ≥ 0)个节点有限集合。==当n=0时,称为空二叉树,当n>0时,该集合有一个根节点及互不可交的,分别被称为左子树和右子树的二叉树组成。

二叉树可以被理解为一下两个条件的树型结构。

  • 每个节点度不大于2。
  • 节点没棵子树时明确区分左右的,不能随意改变。

2. 二叉树的性质

  1. 在二叉树的第i层之多有 2i1 个节点(i≥1)。
  2. 深度为k的二叉树之多有 2k1 个节点(k≥1)。
  3. 对任意一个二叉树T,若终端节点为n度为2的节点数为m,则n = m + 1。
  4. 具有n个节点的完全二叉树的深度为 log2n+1
  5. 对于具有n个节点的完全二叉树,如果按照对满二叉树节点进行连续编号的方式,对所有节点从1开始顺序编号,则对于任意序号为i的节点有:
    • 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
    • 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
    • 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

3. 二叉树的链式实现

3.1二叉树的抽象数据类型

typedef int myType;

typedef struct treeNode 
{
    myType element;     //值域元素
    struct treeNode *lchild;    //左孩子
    struct treeNode *rchild;    //右孩子
}Btree;

3.2 创建一个二叉树

递归创建一个二叉树

void createTree(Btree **T)  //传入一个Btree的指针的地址
{
    myType data;
    scanf("%d", &data);

    if(data == -1) {    //-1代表终端节点值
        *T = NULL;
    } else {
        *T = (Btree *)malloc(sizeof(struct treeNode));  
        (*T)->element = data;
        printf("请输入%d的左孩子节点:", data);
        createTree(&((*T)->lchild));
        printf("请输入%d的右孩子节点:", data);
        createTree(&((*T)->rchild));
    }
}

3.3 递归先根序遍历

void preOrder(Btree *T)
{
    if(T != NULL) {
        printf("%d ", T->element);  //访问根节点
        preOrder(T->lchild);    //先根序遍历左子树
        preOrder(T->rchild);    //先根序遍历右子树
    }
}

3.4 递归中根序遍历

void inOrder(Btree *T)
{
    if(T != NULL) {
        inOrder(T->lchild);     //中根序遍历左子树
        printf("%d ", T->element);  //访问根节点 
        inOrder(T->rchild); //中根序遍历右子树
    }
}

3.5 递归后根序遍历

void postOrder(Btree *T)
{
    if(T != NULL) {
        postOrder(T->lchild);   //后根序遍历左子树
        postOrder(T->rchild);   //后根序遍历右子树
        printf("%d ", T->element);  //访问根节点
    }
}

==对于二叉树递归遍历的时间复杂度均为O(n)。==

3.6 层次遍历

层次遍历的特点是:先访问的节点其孩子也将先访问,后访问的节点其孩子也将后访问,这与队列操作的特点类似,因此应用队列进行节点的访问次序控制。下面层次遍历的代码会引用到这篇文章中的队列数据结构的相关接口:队列的实现

利用队列遍历的层次思想是:
首先根节点入队,当队列非空时,重复如下两部操作:

  1. 队头节点出队,并访问出队的节点。
  2. 出队节点的非空左、右孩子依次入队。
void levelOrder(Btree *T)
{
    QUEUE *q = createQueue(100);    //创建一个容量为100的队列

    while(T != NULL) {
        printf("%d ", T->element);  //访问根节点
        if(T->lchild != NULL)
            enQueue(T->lchild, q);  //如果左孩子存在,左孩子入队
        if(T->rchild != NULL)
            enQueue(T->rchild, q);  //如果左孩子存在,左孩子入队
        if(!isEmpty(q))     //当队列非空时
            T = frontAndDequeue(q); //队头元素出队,并且访问出队元素
        else
            T = NULL;   //队列为空,遍历完成
    }

    disposeQueue(q);    //销毁队列
}

3.7 销毁二叉树

void distroyTree(Btree **T)
{
    Btree *pl, *pr;
    if((*T) == NULL) {
        return ;
    } else {
        pl = (*T)->lchild;  //保存左孩子的地址
        pr = (*T)->rchild;  //保存右孩子的地址
        (*T)->lchild = NULL;
        (*T)->rchild = NULL;
        free(*T);   //释放根节点
        (*T) = NULL;
        distroyTree(&pl);   //递归销毁
        distroyTree(&pr);
    }
}

3.8 二叉树的深度

int tree_deep(Btree *T)
{
    int deep = 0;
    if(T) {
        int leftdeep = tree_deep(T->lchild);    
        int rightdeep = tree_deep(T->rchild);
        deep = leftdeep > rightdeep ? leftdeep+1 : rightdeep+1;
    }

    return deep;
}

3.9 终端节点数目

int leafnode_count(Btree *T, int num)
{
    if(T) {
        if(T->lchild == NULL && T->rchild == NULL) 
            num++;
        leafnode_count(T->lchild, num);
        leafnode_count(T->rchild, num);
    }   

    return num;
}
  • 23
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当然,下面是一个简单的平衡二叉树的C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> // 定义二叉树节点结构 typedef struct Node { int data; // 节点值 struct Node* left; // 左子节点指针 struct Node* right; // 右子节点指针 int height; // 节点高度 } Node; // 计算节点的高度 int height(Node* node) { if (node == NULL) { return 0; } return node->height; } // 获取两个数中较大的值 int max(int a, int b) { return (a > b) ? a : b; } // 创建一个新节点 Node* createNode(int data) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; newNode->height = 1; return newNode; } // 执行右旋操作 Node* rightRotate(Node* y) { Node* x = y->left; Node* T2 = x->right; x->right = y; y->left = T2; y->height = max(height(y->left), height(y->right)) + 1; x->height = max(height(x->left), height(x->right)) + 1; return x; } // 执行左旋操作 Node* leftRotate(Node* x) { Node* y = x->right; Node* T2 = y->left; y->left = x; x->right = T2; x->height = max(height(x->left), height(x->right)) + 1; y->height = max(height(y->left), height(y->right)) + 1; return y; } // 获取节点的平衡因子 int getBalanceFactor(Node* node) { if (node == NULL) { return 0; } return height(node->left) - height(node->right); } // 插入节点 Node* insertNode(Node* node, int data) { if (node == NULL) { return createNode(data); } if (data < node->data) { node->left = insertNode(node->left, data); } else if (data > node->data) { node->right = insertNode(node->right, data); } else { // 忽略插入相同的节点 return node; } node->height = 1 + max(height(node->left), height(node->right)); int balanceFactor = getBalanceFactor(node); // 左-左情况,执行右旋操作 if (balanceFactor > 1 && data < node->left->data) { return rightRotate(node); } // 右-右情况,执行左旋操作 if (balanceFactor < -1 && data > node->right->data) { return leftRotate(node); } // 左-右情况,先对左子节点进行左旋,再对当前节点进行右旋 if (balanceFactor > 1 && data > node->left->data) { node->left = leftRotate(node->left); return rightRotate(node); } // 右-左情况,先对右子节点进行右旋,再对当前节点进行左旋 if (balanceFactor < -1 && data < node->right->data) { node->right = rightRotate(node->right); return leftRotate(node); } return node; } // 中序遍历二叉树 void inorderTraversal(Node* root) { if (root != NULL) { inorderTraversal(root->left); printf("%d ", root->data); inorderTraversal(root->right); } } int main() { Node* root = NULL; root = insertNode(root, 10); root = insertNode(root, 20); root = insertNode(root, 30); root = insertNode(root, 40); root = insertNode(root, 50); root = insertNode(root, 25); printf("中序遍历结果:"); inorderTraversal(root); return 0; } ``` 这段代码实现了一个平衡二叉树的插入和中序遍历操作。你可以根据需要自行修改和扩展。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值