深入探索二叉树

目录

1. 树与二叉树的基石

1.1 树的本质特性

1.2 树的相关概念

2.二叉树的概念

3. 顺序存储:堆的实现艺术

3.1. 顺序存储

3.2.堆

3.3.堆的实现

4.二叉树链式结构的实现

4.1.创建二叉树

4.2.二叉树的三序遍历


1. 树与二叉树的基石

1.1 树的本质特性

树是典型的非线性数据结构,其核心特征体现在:

  • 层次结构:每个节点(除根节点)有且仅有一个父节点

  • 递归定义:子树具有与整棵树完全相同的结构特征

1.2 树的相关概念

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6

叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点

非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点

双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点

孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点

兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点

树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

树的高度或深度:树中节点的最大层次; 如上图:树的高度为4

堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点

节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先  

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林;


2.二叉树的概念

定义:每个节点最多拥有2个子节点的有序树

特殊形态

  • 满二叉树:一棵高度为 h 的满二叉树,其每一层的节点数都达到最大值,深度h的树具有2^h-1个节点(每一层的节点数都满员,没有任何缺失)

  • 完全二叉树:一棵高度为 h 的二叉树,如果从根节点开始,从上到下、从左到右依次填满节点,直到最后一个节点为止,则这棵二叉树是完全二叉树。完全二叉树的前 h−1 层是满的,最后一层的节点可能不满,但必须从左到右依次填满。

性质

1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^{i-1}个结点.

2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是 2^{h}-1.

3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为n2 ,则有 n0=n2+1.

4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=\log_{2}(n+1)

5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则表示二叉树在数组位置中父子下标关系有:

父节点:parent=(child-1)/2

左孩子节点:leftchild = 2*parent+1

右孩子节点:rightchild = 2*parent+2


3. 顺序存储:堆的实现艺术

3.1. 顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空 间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

3.2.堆

堆是一种特殊的完全二叉树,满足以下性质:

  • 大堆(大根堆):父节点值 ≥ 子节点

  • 小堆(小根堆):父节点值 ≤ 子节点

3.3.堆的实现

//模拟实现大堆

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

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

//初始化堆
void HeapInit(HP* php);
//销毁堆
void HeapDestroy(HP* php);

//插入数据
void HeapPush(HP* php, HPDataType x);

//删除数据
void HeapPop(HP* php);

//判断堆数据是否为空
bool HeapEmpty(HP* php);

//查看堆顶数据
HPDataType HeapTop(HP* php);

//返回元素个数
int HeapSize(HP* php);


// 定义堆的数据类型
typedef int HPDataType;

// 定义堆的结构体
typedef struct Heap 
{
    HPDataType* a;       // 堆数组
    int size;            // 当前堆的大小
    int capacity;        // 堆的最大容量
} HP;

// 辅助函数:交换两个元素的值
void Swap(HPDataType* p1, HPDataType* p2) 
{
    HPDataType tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}

// 向上调整(上浮操作)
void AdjustUp(HPDataType* a, int child) 
{
    int parent = (child - 1) / 2;  // 计算父节点的索引
    while (child > 0)  // 当前节点不是根节点时
    {
        if (a[parent] < a[child])  // 如果父节点小于子节点
        {
            Swap(&a[parent], &a[child]);  // 交换父子节点
            child = parent;  // 更新当前节点为父节点
            parent = (child - 1) / 2;  // 更新父节点的索引
        }
        else 
        {
            break;  // 如果父节点大于子节点,调整完成
        }
    }
}

// 向下调整(下沉操作)
void AdjustDown(HPDataType* a, int size, int parent) 
{
    int child = parent * 2 + 1;  // 假设左孩子是最大的
    while (child < size)  // 当前节点有子节点时
    {
        if (child + 1 < size && a[child + 1] > a[child])  // 如果右孩子存在且大于左孩子
        {
            child++;  // 将child指向较大的子节点
        }
        if (a[child] > a[parent])  // 如果子节点大于父节点
        {
            Swap(&a[child], &a[parent]);  // 交换父子节点
            parent = child;  // 更新当前节点为子节点
            child = parent * 2 + 1;  // 更新子节点的索引
        }
        else 
        {
            break;  // 如果父节点大于子节点,调整完成
        }
    }
}

// 初始化堆
void HeapInit(HP* php) 
{
    assert(php);  // 确保传入的堆指针非空

    php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);  // 分配初始内存空间
    if (php->a == NULL) 
    {
        perror("malloc fail");  // 如果分配失败,打印错误信息
        return;
    }

    php->capacity = 4;  // 设置堆的初始容量为4
    php->size = 0;      // 堆的初始大小为0
}

// 销毁堆
void HeapDestroy(HP* php) 
{
    assert(php);  // 确保堆指针非空

    free(php->a);  // 释放堆数组的内存
    php->capacity = 0;  // 将堆的容量重置为0
    php->size = 0;      // 将堆的大小重置为0
}

// 插入元素到堆中
void HeapPush(HP* php, HPDataType x) 
{
    assert(php);  // 确保堆指针非空

    if (php->size == php->capacity)  // 如果堆已满,扩容
    {
        HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * php->capacity * 2);
        if (tmp == NULL) 
        {
            perror("realloc fail");  // 如果扩容失败,打印错误信息
            return;
        }
        php->a = tmp;  // 更新堆数组指针
        php->capacity *= 2;  // 更新堆的容量
    }

    php->a[php->size] = x;  // 将新元素放在堆的末尾
    php->size++;  // 堆的大小加1
    AdjustUp(php->a, php->size - 1);  // 对新插入的元素进行上浮操作
}

// 删除堆顶元素
void HeapPop(HP* php) 
{
    assert(php);  // 确保堆指针非空
    assert(!HeapEmpty(php));  // 确保堆不为空

    Swap(&php->a[0], &php->a[php->size - 1]);  // 将堆顶元素与最后一个元素交换
    php->size--;  // 堆的大小减1
    AdjustDown(php->a, php->size, 0);  // 对新的堆顶元素进行下沉操作
}

// 判断堆是否为空
bool HeapEmpty(HP* php) 
{
    assert(php);  // 确保堆指针非空
    return php->size == 0;  // 返回堆是否为空
}

// 获取堆顶元素
HPDataType HeapTop(HP* php) 
{
    assert(php);  // 确保堆指针非空
    return php->a[0];  // 返回堆顶元素
}

// 获取堆的大小
int HeapSize(HP* php) 
{
    assert(php);  // 确保堆指针非空
    return php->size;  // 返回堆的大小
}

4.二叉树链式结构的实现

4.1.创建二叉树

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

// 定义二叉树节点的数据类型
typedef int BTDataType;

// 定义二叉树节点的结构体
typedef struct BinaryTreeNode {
    BTDataType data;                // 节点存储的数据
    struct BinaryTreeNode* left;    // 指向左孩子的指针
    struct BinaryTreeNode* right;   // 指向右孩子的指针
} BTNode;

// 创建一个新的二叉树节点
BTNode* BuyNode(BTDataType x) {
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));  // 分配内存空间
    if (node == NULL) {
        perror("malloc fail");  // 如果分配失败,打印错误信息
        return NULL;
    }

    node->data = x;             // 初始化节点数据
    node->left = NULL;          // 初始化左孩子指针
    node->right = NULL;         // 初始化右孩子指针

    return node;                // 返回创建的节点
}

// 创建一个简单的二叉树
BTNode* CreateBinaryTree() {
    // 创建各个节点
    BTNode* node1 = BuyNode(1);  // 根节点
    BTNode* node2 = BuyNode(2);
    BTNode* node3 = BuyNode(3);
    BTNode* node4 = BuyNode(4);
    BTNode* node5 = BuyNode(5);
    BTNode* node6 = BuyNode(6);

    // 构建二叉树的结构
    node1->left = node2;        // 1 的左孩子是 2
    node1->right = node4;       // 1 的右孩子是 4
    node2->left = node3;        // 2 的左孩子是 3
    node4->left = node5;        // 4 的左孩子是 5
    node4->right = node6;       // 4 的右孩子是 6

    return node1;               // 返回根节点
}

结构:

4.2.二叉树的三序遍历

// 前序遍历:根 左子树 右子树
void PreOrder(BTNode* root) 
{
    if (root == NULL) 
    {
        printf("NULL ");  // 如果当前节点为空,打印 NULL
        return;
    }

    printf("%d ", root->data);  // 先访问根节点
    PreOrder(root->left);       // 递归遍历左子树
    PreOrder(root->right);      // 递归遍历右子树
}

// 中序遍历:左子树 根 右子树
void InOrder(BTNode* root) 
{
    if (root == NULL) 
    {
        printf("NULL ");  // 如果当前节点为空,打印 NULL
        return;
    }

    InOrder(root->left);       // 递归遍历左子树
    printf("%d ", root->data); // 访问根节点
    InOrder(root->right);      // 递归遍历右子树
}

// 后序遍历:左子树 右子树 根
void PostOrder(BTNode* root) 
{
    if (root == NULL) 
    {
        printf("NULL ");  // 如果当前节点为空,打印 NULL
        return;
    }

    PostOrder(root->left);       // 递归遍历左子树
    PostOrder(root->right);      // 递归遍历右子树
    printf("%d ", root->data);   // 最后访问根节点
}

//层序遍历:自上而下,从左至右
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);

	if (root)
	{
		QueuePush(&q,root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		printf("%d ", front->data);
		if (front->left)
		{
			QueuePush(&q,front->left);
		}

		if (front->right)
		{
			QueuePush(&q,front->right);
		}
	}

	QueueDestroy(&q);
}

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

// 二叉树叶子节点个数:如果当前节点是叶子,返回1,否则返回左右子树的叶子节点数之和。
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}

	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

//求高度,当前树高度 = 左右子树高度大的那个 + 1
int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	/*return BinaryTreeHeight(root->left) > BinaryTreeHeight(root->right) ?
		BinaryTreeHeight(root->left) + 1 : BinaryTreeHeight(root->right) + 1;*/

	int leftheight = BinaryTreeHeight(root->left);
	int rightheight = BinaryTreeHeight(root->right);

	return BinaryTreeHeight(root->left) > BinaryTreeHeight(root->right) ? 
		leftheight + 1 : rightheight + 1;
}

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	/*if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}

	BTNode* result = BinaryTreeFind(root->left, x);
	if (result != NULL)
	{
		return result;
	}

	return BinaryTreeFind(root->right, x);*/

	if (root == NULL)
	{
		return NULL;
	}

	if (root->data == x)
	{
		return root;
	}

	BTNode* lret = BinaryTreeFind(root->left, x);
	if (lret)
	{
		return lret;
	}

	BTNode* rret = BinaryTreeFind(root->right, x);
	if (rret)
	{
		return rret;
	}

	return NULL;
}

//判断是不是完全二叉树
bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q,root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	//判断队列还有没有非空的
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}

	QueueDestroy(&q);
	return true;
}

层序遍历:队列里面存的树的结点的指针,还有下一个结点的地址。出一层,入一层

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值