树
树的概念
树是一种重要的数据结构,广泛应用于计算机科学中。树的定义如下:
根节点:深度唯一,唯一没有父节点的节点
子结点:一个节点的后继节点
叶子节点:没有后继的子结点
深度:从根到节点的路径中边的数量,树的深度是所有叶子节点深度的最大值
路径:一个节点到另一个节点边构成的有序集合,任意两个节点的路径唯一
高度:节点到叶子节点中边的最大值
兄弟节点:拥有相同父节点的节点
除了根节点,每个节点有且仅有一个前驱节点(父节点),有 0 ~ n 个后继节点(子结点);
一棵树由n个节点和n-1条边构成。
节点定义
struct node
{
int data; // 数据域
struct node *first_child; // 第一个子结点
struct node *next_sibling; // 下一个兄弟节点
}
二叉树(binary tree)
概念:每个节点最多只有两个子节点的树。
二叉树分类
满二叉树:每层节点都是满的,节点数量为2^n(n为节点深度)。
完全二叉树:与满二叉树相似,但叶子节点聚集左边。
最大(小)堆:父节点大于(或小于)子节点。
二叉搜索树:左子树小于父节点,右子树大于父节点。
实现
-
顺序存储,基于树组,时间复杂度为
O(1)
- 满二叉树
- 完全二叉树
- 堆
- 优先级队列
节点的父、子节点位置计算公式:(n为当前节点索引)
- 父节点:n / 2 - 1
- 子结点:n * 2 + 1(左子树), n * 2 + 2(右子树)
-
链式存储,基于节点和指针
二叉树定义
struct node
{
int data; // 数据域
struct node *left; // 左子树
struct node *right; // 右子树
};
二叉树的遍历
- 先序遍历:根节点->左子树->右子树
- 中序遍历:左->根->右
- 后续遍历:左->右->根
- 层序遍历:从上往下,从左到右
堆
12,9,23,5 ,17,42 ,56 ,11 ,20 ,3
最大堆:父节点大于子结点
最小堆:父节点小于子结点
构建一个最大堆:
56
20 42
17 9 12 23
5 11 3
每次取走堆顶元素后,把最后一个元素补到空缺处,再进行堆排序,这样可以保证堆内没有空缺
堆定义
struct heap
{
int *data; // 使用数组实现最小堆,避免空间浪费
int size;
int capacity;
};
实现一个最小堆
/*
* @Author: ZPY
* @TODO: 实现一个最小堆
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct heap
{
int *data; // 使用数组实现最小堆,避免空间浪费
int size;
int capacity;
} Heap;
void init(Heap *heap);
void insert(Heap *heap, int data);
void delete(Heap *heap);
void swap(int *, int *);
void display(Heap *heap);
int main(int argc, char const *argv[])
{
Heap *heap = malloc(sizeof(Heap));
// 初始化堆
init(heap);
// 插入元素 12,2,31,4,15,11,17,82,54
insert(heap, 12);
insert(heap, 2);
insert(heap, 31);
insert(heap, 4);
insert(heap, 15);
insert(heap, 11);
insert(heap, 17);
insert(heap, 82);
insert(heap, 54);
printf("堆中元素:");
display(heap);
// 删除堆顶元素
delete(heap);
printf("删除堆顶元素后,堆中元素:");
display(heap);
delete(heap);
printf("删除堆顶元素后,堆中元素:");
display(heap);
free(heap);
return 0;
}
void init(Heap *heap)
{
heap->capacity = 10;
heap->size = 0;
heap->data = malloc(sizeof(int) * heap->capacity);
}
void insert(Heap *heap, int data)
{
// 插入之前检查数组余量,满了就扩容为原来的 1.5 倍
if (heap->size == heap->capacity - 1)
{
int incr = heap->capacity >> 1;
heap->capacity += incr;
heap->data = realloc(heap->data, sizeof(int) * heap->capacity);
}
heap->data[heap->size] = data;
heap->size++;
// 插入之后,从下往上调整
for (int i = heap->size - 1; i > 0; i--)
{
int parent = (i - 1) >> 1;
if (heap->data[parent] > heap->data[i])
{
swap(&heap->data[parent], &heap->data[i]);
}
else
{
break;
}
}
}
void delete(Heap *heap)
{
// 如果堆为空,则返回
if (heap == NULL)
{
return;
}
// 每次删除堆顶元素,将最后1个元素放到堆顶
heap->data[0] = heap->data[heap->size - 1];
heap->size--;
if (heap->size == 0)
{
return;
}
// 删除之后,从上往下调整
for (int i = 0; i < heap->size; i++)
{
int left = (i << 1) + 1;
int right = (i << 1) + 2;
// 父节点与最小的子节点交换
if (right < heap->size && heap->data[right] < heap->data[left] && heap->data[right] < heap->data[i])
{
swap(&heap->data[right], &heap->data[i]);
}
else if (left < heap->size && heap->data[left] < heap->data[i])
{
swap(&heap->data[left], &heap->data[i]);
}
}
}
void display(Heap *heap)
{
for (int i = 0; i < heap->size; i++)
{
printf("%d, ", heap->data[i]);
}
printf("\n");
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
输出结果
堆中元素:2, 4, 11, 12, 15, 31, 17, 82, 54,
删除堆顶元素后,堆中元素:4, 12, 11, 54, 15, 31, 17, 82,
删除堆顶元素后,堆中元素:11, 12, 17, 54, 15, 31, 82,
二叉搜索树
为实现快速的搜索,左子树比父节点小,右子树比父节点大。
12,9,23,5 ,17,42 ,56 ,11 ,20 ,3
构建一个二叉搜索树:
12
9 23
5 11 17 42
3 _ _ _ _ 20 _ 56
遍历结果:
- 先序遍历:12;9;5;3;11;23;17;20;42;56
- 中序遍历:3;5;9;11;12;17;20;23;42;56 // 排序
- 后续遍历:3;5;11;9;20;17;56;42;23;12
- 层序遍历:12;9;23;5;11;17;42;3;20;56
完全二叉树的层序遍历:A,B,C,D,E,F,G,H,I,J,K
A
B C
D E F G
H I J K
先序:A,B,D,H,I,E,J,K,C,F,G
中序:H,D,I,B,J,E,K,A,F,C,G
后序:H,I,D,J,K,E,B,F,G,C,A
二叉搜索树的实现
/*
* @Author: ZPY
* @TODO: 二叉搜索树
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int data; // 数据域
struct node *left; // 左子树
struct node *right; // 右子树
} Node;
typedef struct node Tree;
// 队列结构
typedef struct queue
{
Tree **data; // 队列存储节点指针
int front;
int rear;
int capacity;
int size;
} Queue;
Node *insert(Tree *t, int v); // 插入
int max(Tree *t); // 最大值,递归/循环
int min(Tree *t); // 最小值
void preorder(Tree *t); // 先序遍历
void inorder(Tree *t); // 中序遍历
void postorder(Tree *t); // 后序遍历
void levelorder(Tree *t); // 层序遍历,基于队列
void reverse(Tree *t); // 翻转
void free_tree(Tree *t); // 释放
Queue *create_queue(); // 创建队列
void enqueue(Queue *q, Tree *t);// 入队
Tree *dequeue(Queue *q); // 出队
void destory_queue(Queue *q); // 销毁队列
int main(int argc, char const *argv[])
{
// 创建根节点
Tree *t = malloc(sizeof(Tree));
// 12,2,31,4,15,11,17,82,54
// 插入节点
t = insert(NULL, 12); // 将插入的第一个值作为根,否则会默认把0作为根
t = insert(t, 2);
t = insert(t, 31);
t = insert(t, 4);
t = insert(t, 15);
t = insert(t, 11);
t = insert(t, 17);
t = insert(t, 82);
t = insert(t, 54);
printf("最大值:%d\n", max(t));
printf("最小值:%d\n", min(t));
printf("先序遍历:");
preorder(t);
printf("\n中序遍历:");
inorder(t);
printf("\n后序遍历:");
postorder(t);
printf("\n层序遍历:");
levelorder(t);
printf("\n=============翻转树================");
reverse(t);
printf("\n");
printf("先序遍历:");
preorder(t);
printf("\n中序遍历:");
inorder(t);
printf("\n后序遍历:");
postorder(t);
printf("\n层序遍历:");
levelorder(t);
printf("\n");
free_tree(t);
return 0;
}
Node *insert(Tree *t, int v)
{
if (t == NULL)
{
// 创建根节点
t = malloc(sizeof(Tree));
t->data = v;
t->left = t->right = NULL;
}
else if (v < t->data)
{
t->left = insert(t->left, v);
}
else if (v > t->data)
{
t->right = insert(t->right, v);
}
return t;
}
int max(Tree *t)
{
int max = t->data;
while (t->right) // 循环实现
{
max = t->right->data;
t = t->right;
}
return max;
}
int min(Tree *t)
{
if (t->left == NULL)
{
return t->data;
}
return min(t->left); // 递归实现,最左边的一定是最小的
}
void preorder(Tree *t)
{
if (t == NULL)
{
return;
}
// 根->左->右
printf("%d, ", t->data); // 访问根
preorder(t->left); // 递归遍历左子树
preorder(t->right); // 递归遍历右子树
}
void inorder(Tree *t)
{
if (t != NULL)
{
// 左->根->右
inorder(t->left); // 递归遍历左子树
printf("%d, ", t->data); // 访问根
inorder(t->right); // 递归遍历右子树
}
}
void postorder(Tree *t)
{
if (t != NULL)
{
// 左->右->根
postorder(t->left); // 递归遍历左子树
postorder(t->right); // 递归遍历右子树
printf("%d, ", t->data); // 访问根
}
}
void levelorder(Tree *t)
{
if (t == NULL)
{
return;
}
Queue *q = create_queue();
enqueue(q, t);
while (q->size)
{
t = dequeue(q);
printf("%d, ", t->data);
if (t->left)
{
enqueue(q, t->left);
}
if (t->right)
{
enqueue(q, t->right);
}
}
destory_queue(q);
}
void reverse(Tree *t)
{
if (t == NULL)
{
return;
}
// 交换左右子树
Tree *temp = t->left;
t->left = t->right;
t->right = temp;
// 递归遍历左右子树
reverse(t->left);
reverse(t->right);
}
void free_tree(Tree *t)
{
if (t != NULL)
{
free_tree(t->left);
free_tree(t->right);
free(t);
}
}
// 创建队列
Queue *create_queue()
{
Queue *q = malloc(sizeof(Queue));
if (q == NULL)
{
printf("创建队列失败\n");
return NULL;
}
q->front = 0;
q->rear = 0;
q->size = 0;
q->capacity = 10;
q->data = malloc(sizeof(Tree *) * q->capacity);
return q; // 返回队列指针
}
// 入队
void enqueue(Queue *q, Tree *t)
{
if (q->size == q->capacity)
{
printf("队列已满\n");
return;
}
q->data[q->rear] = t; // 队尾入队
q->rear = (q->rear + 1) % q->capacity; // 队尾移到下一个位置
q->size++;
}
// 出队
Tree *dequeue(Queue *q)
{
if (q->size == 0)
{
printf("队列为空\n");
return NULL;
}
Tree *t = q->data[q->front];
q->data[q->front] = NULL; // 清除指针
q->front = (q->front + 1) % q->capacity; // 队首移到下一个位置
q->size--;
return t;
}
// 销毁队列
void destory_queue(Queue *q)
{
for (int i = 0; i < q->capacity; i++)
{
q->data[i] = NULL;
}
free(q->data);
free(q);
}
输出结果
最大值:82
最小值:2
先序遍历:12, 2, 4, 11, 31, 15, 17, 82, 54,
中序遍历:2, 4, 11, 12, 15, 17, 31, 54, 82,
后序遍历:11, 4, 2, 17, 15, 54, 82, 31, 12,
层序遍历:12, 2, 31, 4, 15, 82, 11, 17, 54,
=============翻转树================
先序遍历:12, 31, 82, 54, 15, 17, 2, 4, 11,
中序遍历:82, 54, 31, 17, 15, 12, 11, 4, 2,
后序遍历:54, 82, 17, 15, 31, 11, 4, 2, 12,
层序遍历:12, 31, 2, 82, 15, 4, 54, 17, 11,