树
#include <stdio.h>
#include <stdlib.h>
// 树存储结构
typedef struct BiTNode {
int data;
struct BiTNode *lchild, *rchild; // 左右孩子指针
} BiTNode, * BiTree;
// 循环队列算法,辅助完成二叉树层次遍历
#define MaxSize 50 // 定义队列中元素的最大个数
typedef struct {
BiTree data[MaxSize]; // 存放队列元素
int front, rear; // 队头和队尾指针
} SqQueue;
// 初始化队列(带头结点)
void Queue_Init(SqQueue &Q) {
Q.rear = Q.front = 0;
}
// 判断队列是否为空
bool Queue_Empty(SqQueue Q) {
return Q.front == Q.rear;
}
// 入队
bool Queue_En(SqQueue &Q, BiTree e) {
if ((Q.rear + 1) % MaxSize == Q.front) return false; // 队满则报错
Q.data[Q.rear] = e;
Q.rear = (Q.rear + 1) % MaxSize; // 队尾指针加 1 取模
return true;
}
// 出队
bool Queue_De(SqQueue &Q, BiTree &e) {
if (Queue_Empty(Q)) return false; // 队空则报错
e = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize;
return true;
}
// 通过给定数组创建二叉树,0为空结点
// nums 为数组、len 为数组长度、i 为当前数组下标
BiTNode * BiTree_Create (char* nums, int len , int i) {
if (i >= len || nums[i] == 0) return NULL;
BiTNode * node = (BiTNode *)malloc(sizeof(BiTNode));
node->data = nums[i];
node->lchild = BiTree_Create(nums, len, 2 * i + 1);
node->rchild = BiTree_Create(nums, len, 2 * i + 2);
return node;
}
// 先序遍历: 根左右(NLR)
void Order_Pre(BiTree tree) {
if(tree == NULL) return;
printf("%d,", tree->data); // 访问根树内容
Order_Pre(tree->lchild); // 递归遍历左子树
Order_Pre(tree->rchild); // 递归遍历右子树
}
// 中序遍历: 左根右(LNR)
void Order_In(BiTree tree) {
if(tree == NULL) return;
Order_In(tree->lchild); // 递归遍历左子树
printf("%d,", tree->data); // 访问根树内容
Order_In(tree->rchild); // 递归遍历右子树
}
// 后序遍历: 左根右(LRN)
void Order_Post(BiTree tree) {
if(tree == NULL) return;
Order_Post(tree->lchild); // 递归遍历左子树
Order_Post(tree->rchild); // 递归遍历右子树
printf("%d,", tree->data); // 访问根树内容
}
// 层次遍历
void Order_Level (BiTree tree) {
SqQueue Q; // 初始化队列
Queue_Init(Q);
BiTree p;
Queue_En(Q, tree); // 根节点入队
while (!Queue_Empty(Q)) { // 队列不空则继续遍历
Queue_De(Q, p); // 队头结点出队
printf("%d,", p->data); // 访问出队结点内容
if (p->lchild != NULL)
Queue_En(Q, p->lchild); // 左子树不为空,则左子树根入队
if (p->rchild != NULL)
Queue_En(Q, p->rchild); // 右子树不为空,则右子树根入队
}
}
int main() {
char nums[11] = {1, 2, 3, 0, 4, 0, 5, 0, 0, 6, 0};
BiTree tree = BiTree_Create(nums, 11, 0);
printf("先序遍历:");
Order_Pre(tree);
printf("\n");
printf("中序遍历:");
Order_In(tree);
printf("\n");
printf("后序遍历:");
Order_Post(tree);
printf("\n");
// 层次遍历需要借助队列实现
printf("层次遍历: ");
Order_Level(tree);
return 0;
}
二叉排序树-链式存储
#include <stdio.h>
#include <stdlib.h>
// 树存储结构
typedef struct BiTNode {
int data;
struct BiTNode *lchild, *rchild; // 左右孩子指针
} BSTNode, * BSTree;
// 循环队列算法,辅助完成二叉树层次遍历
#define MaxSize 50 // 定义队列中元素的最大个数
typedef struct {
BSTree data[MaxSize]; // 存放队列元素
int front, rear; // 队头和队尾指针
} SqQueue;
// 初始化队列(带头结点)
void Queue_Init(SqQueue &Q) {
Q.rear = Q.front = 0;
}
// 判断队列是否为空
bool Queue_Empty(SqQueue Q) {
return Q.front == Q.rear;
}
// 入队
bool Queue_En(SqQueue &Q, BSTree e) {
if ((Q.rear + 1) % MaxSize == Q.front) return false; // 队满则报错
Q.data[Q.rear] = e;
Q.rear = (Q.rear + 1) % MaxSize; // 队尾指针加 1 取模
return true;
}
// 出队
bool Queue_De(SqQueue &Q, BSTree &e) {
if (Queue_Empty(Q)) return false; // 队空则报错
e = Q.data[Q.front];
Q.front = (Q.front + 1) % MaxSize;
return true;
}
// 插入关键字为 k 的新结点
int BST_Insert (BSTree &tree, int s) {
if (tree == NULL) { // 传入的为空节点,新插入的结点为根节点
tree = (BSTNode *)malloc(sizeof(BSTNode));
tree->data = s;
tree->lchild = tree->rchild = NULL;
return 1;
}
else if (s == tree->data) return 0; // 相同节点,失败
else if (s < tree->data) // 插入到 tree 的左子树
return BST_Insert(tree->lchild, s);
else if (s > tree->data) // 插入到 tree 的右子树
return BST_Insert(tree->rchild, s);
}
// tree 根节点、 nums 数组、len 数组长度
void BST_Create (BSTree &tree, char* nums, int len) {
tree = NULL;
for (int i = 0; i < len; i++) {
BST_Insert(tree, nums[i]);
}
}
// 删除结点
void BST_Delete (BSTree &tree, int e) {
// 要删除的结点
BSTree p = tree;
// 要删除结点的父结点
BSTree f = NULL;
while (p) { // 查找
if (p->data == e) {
break; // 找到指定结点,结束循环
}
f = p; // 记录要删除结点的父结点
if (p->data > e)
p = p->lchild; // 在 p 的左子树中查找
else
p = p->rchild; // 在 p 的右子树中查找
}
// 要删除的结点不存在
if (!p) return;
// 二叉树待删除结点有四种情况
// 待删除的为叶子结点、仅有一颗子节点树、左右子树均不为空
// 情况1:要删除的结点为叶子结点,直接删除
if (p->lchild == NULL && p->rchild == NULL) {
if (f == NULL)
tree = NULL; // 删除根结点
else if (p == f->lchild)
f->lchild == NULL; // 删除左子树结点
else
f->rchild = NULL; // 删除右子树结点
free(p);
}
// 情况2:要删除的结点只有一个子树
else if (p->lchild == NULL) { // 仅有右子树
if (f == NULL)
tree = p->rchild; // 删除根节点
else if (p == f->lchild)
f->lchild = p->rchild; // 删除左子树
else
f->rchild = p->rchild; // 删除右子树
free(p);
}
else if (p->rchild == NULL) { // 仅有左子树
if (f == NULL)
tree = p->lchild; // 删除根节点
else if (p == f->lchild)
f->lchild = p->lchild; // 删除左子树
else
f->rchild = p->lchild; // 删除右子树
free(p);
}
// 情况3: 左右子树均不为空
else if (p->lchild && p->rchild) {
BSTree s = p->lchild; // 待替换p的结点
BSTree sf = p; // 指向 s 结点的父
// 左子树中找到最大的值(即最右的结点)
while (s->rchild) {
sf = s;
s = s->rchild; // 往最右下走
}
p->data = s->data; // 替换值,然后删除
if (s == sf->lchild) {
// s为sf的左子树,由于s已经为最大结点,所以s没有右子树
sf->lchild = s->lchild;
} else {
// s为sf的右子树,由于s已经为最大结点,所以s没有右子树
sf->rchild = s->lchild;
}
free(s);
}
}
// 先序遍历: 根左右(NLR)
void Order_Pre(BSTree tree) {
if(tree == NULL) return;
printf("%d,", tree->data); // 访问根树内容
Order_Pre(tree->lchild); // 递归遍历左子树
Order_Pre(tree->rchild); // 递归遍历右子树
}
// 中序遍历: 左根右(LNR)
void Order_In(BSTree tree) {
if(tree == NULL) return;
Order_In(tree->lchild); // 递归遍历左子树
printf("%d,", tree->data); // 访问根树内容
Order_In(tree->rchild); // 递归遍历右子树
}
// 后序遍历: 左根右(LRN)
void Order_Post(BSTree tree) {
if(tree == NULL) return;
Order_Post(tree->lchild); // 递归遍历左子树
Order_Post(tree->rchild); // 递归遍历右子树
printf("%d,", tree->data); // 访问根树内容
}
// 层次遍历
void Order_Level (BSTree tree) {
SqQueue Q; // 初始化队列
Queue_Init(Q);
BSTree p;
Queue_En(Q, tree); // 根节点入队
while (!Queue_Empty(Q)) { // 队列不空则继续遍历
Queue_De(Q, p); // 队头结点出队
printf("%d,", p->data); // 访问出队结点内容
if (p->lchild != NULL)
Queue_En(Q, p->lchild); // 左子树不为空,则左子树根入队
if (p->rchild != NULL)
Queue_En(Q, p->rchild); // 右子树不为空,则右子树根入队
}
}
int main() {
char nums[11] = {50,30,60,40,70,10,20,80};
BSTree tree;
BST_Create(tree, nums, 11);
printf("先序遍历:");
Order_Pre(tree);
printf("\n");
printf("中序遍历:");
Order_In(tree);
printf("\n");
printf("后序遍历:");
Order_Post(tree);
printf("\n");
// 层次遍历需要借助队列实现
printf("层次遍历: ");
Order_Level(tree);
return 0;
}
哈夫曼树-顺序存储
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 树存储结构
typedef struct HTNode {
int weight; // 权值
char data; // 待编码字符
int parent; // 父节点
int lchild, rchild; // 左右孩子
} HTNode, * HuffmanTree;
typedef char **HuffmanCode; // 字符指针数组中存储的元素类型
// 查找在 0 到 i 中的两个最小值,且没有被处理过(parent == 0)
void Select(HuffmanTree &HT, int n, int &min1, int &min2) {
int min;
// 找到第一个最小值
for (int i = 0; i < n; i++) {
if (HT[i].parent == 0) {
min = i;
break;
}
}
for (int i = min + 1; i < n; i++) {
if (
HT[i].parent == 0 &&
HT[i].weight < HT[min].weight
) {
min = i;
}
}
min1 = min; // 第一个最小值给 s1
// 找第二个最小值
for (int i = 0; i < n; i++) {
if (HT[i].parent == 0 && i != min1) {
min = i;
break;
}
}
for (int i = min + 1; i < n; i++) {
if (
HT[i].parent == 0 &&
HT[i].weight < HT[min].weight &&
i != min1
) {
min = i;
}
}
min2 = min; // 第二个最小值
}
// 构建哈夫曼树 HT=哈夫曼树、w=权值、n=待编码数量
void Huffman_Create (HuffmanTree &HT, int *w, char *data, int n) {
int m = 2 * n - 1; // 哈夫曼树的节点总数为 2n-1
HT = (HuffmanTree)calloc(m, sizeof(HTNode)); // 创建 m 个结点,注意malloc和calloc区别
for (int i = 0; i < n; i++) {
HT[i].weight = w[i]; // 权值给 n 个叶子结点
HT[i].data = data[i];
}
for (int i = n; i < m; i++) {
int min1, min2;
// 找到权值最小的两个结点,期中min1权值小于min2的权值
Select(HT, i, min1, min2);
// 新结点的左右权值为两个最小树的权值之和
HT[i].weight = HT[min1].weight + HT[min2].weight;
HT[min1].parent = i;
HT[min2].parent = i;
// 较小的为左孩子
HT[i].lchild = min1;
HT[i].rchild = min2;
}
}
// 生成哈夫曼编码 HT=哈夫曼树、HC=编码结果保存(相当于二维数组)、n=待编码字符个数
void Huffman_Coding (HuffmanTree &HT, HuffmanCode &HC, int n) {
// 开辟需要编码的个数空间(编码串地址)
HC = (HuffmanCode)malloc(sizeof(char *) * n);
// 辅助空间(编码串),编码最长为n-1
char *code = (char*)malloc(sizeof(char) * n); // 字符串最后一位\0表示结束
code[n - 1] = '\0';
for (int i = 0; i < n; i++) {
int j = i; // 当前第i个数据的编码
int p = HT[j].parent; // 找到该数据的根结点
// 由于是从编码结点一直向上找父,所以编码顺序是相反的
int start = n - 1; // 那么存储编码的时候从最后面开始存即编码正序了
// 直到父结点为0,即父结点为根节点时停止
while (p) {
// 如果该节点是其父结点的左孩子,则编码为0,否则为1
if (HT[p].lchild == j)
code[--start] = '0';
else
code[--start] = '1';
j = p; // 继续向上进行编码
p = HT[j].parent; // j 的父节点
}
// 由于code是整个n长度,所以这里从新根据编码长度开辟存储编码的空间
HC[i] = (char*)malloc(sizeof(char) * (n - start));
strcpy(HC[i], &code[start]); // 字符串拷贝
}
free(code); // 释放辅助空间
}
// 打印哈夫曼树,n=编码节点个数
void HuffmanTree_Print(HuffmanTree HT, int n) {
printf("哈夫曼树为:>\n");
printf("下标 字符 权值 父结点 左孩子 右孩子\n");
for (int i = 0; i < n; i++) {
printf("%-4d %-4c %-6d %-6d %-6d %-6d\n", i, HT[i].data, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
}
printf("\n");
}
void HuffmanCode_Print (HuffmanTree HT, HuffmanCode HC, int n) {
for (int i = 0; i < n; i++) {
printf("数据%c的编码为:%s\n", HT[i].data, HC[i]);
}
}
int main() {
int n = 4;
HuffmanTree HT;
int w[] = {7, 5, 2, 4};
char data[] = {'a', 'b', 'c', 'd'};
Huffman_Create(HT, w, data, n);
HuffmanTree_Print(HT, n);
HuffmanCode HC;
Huffman_Coding(HT, HC, n);
HuffmanCode_Print(HT, HC, n);
return 0;
}
// 定义二叉树节点的数据类型为整数
#define ElemType int
// 定义二叉树节点的结构体,包括数据域,左右孩子指针,以及左右线索标记
// ltag, rtag 为 0 表示指向孩子节点的指针
// 为 1 表示指向前驱或后继线索的指针
typedef struct ThreadNode {
ElemType data;
struct ThreadNode *lchild, *rchild;
int ltag, rtag;
} ThreadNode, * ThreadTree;
// 前序线索化中用于记录前驱节点的全局变量
ThreadNode *pre = NULL;
// 通过给定数组创建二叉树,0 表示空节点
// nums 为数组、len 为数组长度、i 为当前数组下标
ThreadTree Thread_Create(char* nums, int len, int i) {
if (i >= len || nums[i] == 0) return NULL;
// 分配内存创建新节点
ThreadTree node = (ThreadTree)malloc(sizeof(ThreadNode));
node->data = nums[i];
// 递归创建左右子树
node->lchild = Thread_Create(nums, len, 2 * i + 1);
node->rchild = Thread_Create(nums, len, 2 * i + 2);
node->ltag = 0;
node->rtag = 0;
return node;
}
// 前序线索化函数
void PreThread(ThreadTree T) {
if (T != NULL) {
// 当前节点的左孩子为空,建立前驱线索
if (T->lchild == NULL) {
T->lchild = pre;
T->ltag = 1; // 设置左线索标记
}
// 前驱节点的右孩子为空,建立后继线索
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = T;
pre->rtag = 1; // 设置右线索标记
}
pre = T; // 更新前驱节点
// 递归处理左子树
if (T->ltag == 0) {
PreThread(T->lchild);
}
// 递归处理右子树
if (T->rtag == 0) {
PreThread(T->rchild);
}
}
}
// 创建前序线索二叉树
void CreatePreThread(ThreadTree T) {
pre = NULL; // 初始化前驱节点
if (T != NULL) {
PreThread(T);
// 处理最后一个节点的后继线索
if (pre->rchild == NULL)
pre->rtag = 1;
}
}
// 中序线索化函数
void InThread(ThreadTree T) {
if (T != NULL) {
// 递归处理左子树
InThread(T->lchild);
// 当前节点的左孩子为空,建立前驱线索
if (T->lchild == NULL) {
T->lchild = pre;
T->ltag = 1; // 设置左线索标记
}
// 前驱节点的右孩子为空,建立后继线索
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = T;
pre->rtag = 1; // 设置右线索标记
}
pre = T; // 更新前驱节点
// 递归处理右子树
InThread(T->rchild);
}
}
// 创建中序线索二叉树
void CreateInThread(ThreadTree T) {
pre = NULL; // 初始化前驱节点
if (T != NULL) {
InThread(T);
// 处理最后一个节点的后继线索
if (pre->rchild == NULL) {
pre->rtag = 1;
}
}
}
// 后序线索化函数
void PostThread(ThreadTree T) {
if (T != NULL) {
// 递归处理左子树
PostThread(T->lchild);
// 递归处理右子树
PostThread(T->rchild);
// 当前节点的左孩子为空,建立前驱线索
if (T->lchild == NULL) {
T->lchild = pre;
T->ltag = 1; // 设置左线索标记
}
// 前驱节点的右孩子为空,建立后继线索
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = T;
pre->rtag = 1; // 设置右线索标记
}
pre = T; // 更新前驱节点
}
}
// 创建后序线索二叉树
void CreatePostThread(ThreadTree T) {
pre = NULL; // 初始化前驱节点
if (T != NULL) {
PostThread(T);
// 处理最后一个节点的后继线索
if (pre->rchild == NULL) {
pre->rtag = 1;
}
}
}
// 前序线索二叉树的前序遍历输出
void PreThreadTraverse(ThreadTree T) {
if (T != NULL) {
printf("%d ", T->data); // 输出节点数据
// 如果左孩子不是线索节点,递归遍历左子树
if (T->ltag == 0) {
PreThreadTraverse(T->lchild);
}
// 如果右孩子不是线索节点,递归遍历右子树
if (T->rtag == 0) {
PreThreadTraverse(T->rchild);
}
}
}
// 中序线索二叉树的中序遍历输出
void InThreadTraverse(ThreadTree T) {
if (T != NULL) {
// 递归遍历左子树
if (T->ltag == 0) {
InThreadTraverse(T->lchild);
}
printf("%d ", T->data); // 输出节点数据
// 递归遍历右子树
if (T->rtag == 0) {
InThreadTraverse(T->rchild);
}
}
}
// 后序线索二叉树的后序遍历输出
void PostThreadTraverse(ThreadTree T) {
if (T != NULL) {
// 递归遍历左子树
if (T->ltag == 0) {
PostThreadTraverse(T->lchild);
}
// 递归遍历右子树
if (T->rtag == 0) {
PostThreadTraverse(T->rchild);
}
printf("%d ", T->data); // 输出节点数据
}
}
int main() {
// 创建测试二叉树
char nums[11] = {1, 2, 3, 0, 4, 0, 5, 0, 0, 6, 0};
ThreadTree preTree = Thread_Create(nums, 11, 0);
ThreadTree inTree = Thread_Create(nums, 11, 0);
ThreadTree postTree = Thread_Create(nums, 11, 0);
// 创建前序线索二叉树
CreatePreThread(preTree);
// 输出前序遍历结果
printf("前序遍历结果:");
PreThreadTraverse(preTree);
printf("\n");
// 创建中序线索二叉树
CreateInThread(inTree);
// 输出中序遍历结果
printf("中序遍历结果:");
InThreadTraverse(inTree);
printf("\n");
// 创建后序线索二叉树
CreatePostThread(postTree);
// 输出后序遍历结果
printf("后序遍历结果:");
PostThreadTraverse(postTree);
printf("\n");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
struct TreeNode* lchild;
struct TreeNode* rchild;
int data;
int height; // 新增一个高度字段
} TreeNode;
// 计算节点的高度
int height(TreeNode* node) {
if (node == NULL)
return -1;
return node->height;
}
// 更新节点的高度
void updateHeight(TreeNode* node) {
// 计算左子树的高度
int leftHeight = height(node->lchild);
// 计算右子树的高度
int rightHeight = height(node->rchild);
// 将节点的高度设置为左右子树中较大的高度加1
// 这样可以确保节点的高度是其子树高度的最大值加1
node->height = (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
}
// 获取节点的平衡因子
int getBalanceFactor(TreeNode* node) {
if (node == NULL)
return 0;
return height(node->lchild) - height(node->rchild);
}
// 单旋转 - LL平衡旋转(右单旋转)
TreeNode* LL(TreeNode* node) {
// 创建一个指针 newRoot,用来存储将要成为新根节点的左孩子
TreeNode* newRoot = node->lchild;
// 将原根节点的左孩子设置为 newRoot 的右孩子
node->lchild = newRoot->rchild;
// 将原根节点作为 newRoot 的右孩子
newRoot->rchild = node;
// 更新节点高度
updateHeight(node);
updateHeight(newRoot);
// 返回新的根节点
return newRoot;
}
// 单旋转 - RR平衡旋转(左单旋转)
TreeNode* RR(TreeNode* node) {
// 将右孩子作为新的根节点
TreeNode* newRoot = node->rchild;
// 将原根节点的左孩子设置为新根节点的左孩子
node->rchild = newRoot->lchild;
// 将原根节点作为新根节点的左孩子
newRoot->lchild = node;
// 更新节点高度
updateHeight(node);
updateHeight(newRoot);
// 返回新的根节点
return newRoot;
}
// 插入节点
TreeNode* insert(TreeNode* root, int data) {
// 如果树为空,创建一个新节点并返回
if (root == NULL) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
newNode->data = data;
newNode->lchild = NULL;
newNode->rchild = NULL;
newNode->height = 0;
return newNode;
}
// 如果要插入的数据小于当前节点的数据,插入到左子树中
if (data < root->data)
root->lchild = insert(root->lchild, data);
// 如果要插入的数据大于当前节点的数据,插入到右子树中
else if (data > root->data)
root->rchild = insert(root->rchild, data);
else
return root; // 已存在相同值的节点,不插入
// 更新节点高度
updateHeight(root);
// 获取平衡因子
int balance = getBalanceFactor(root);
// 平衡维护
// 平衡因子 大于 0 则是左孩子,小于 0 则是右孩子
// 插入的数据在左孩子的左子树 LL
if (balance > 1 && data < root->lchild->data)
return LL(root);
// 插入的数据在右孩子的右子树 RR
if (balance < -1 && data > root->rchild->data)
return RR(root);
// 插入的数据在左孩子的右子树 LR
if (balance > 1 && data > root->lchild->data) {
root->lchild = RR(root->lchild);
return LL(root);
}
// 插入的数据在右孩子的左子树 RL
if (balance < -1 && data < root->rchild->data) {
root->rchild = LL(root->rchild);
return RR(root);
}
return root; // 返回更新后的根节点
}
// 前序遍历以打印树的内容
void preorderTraversal(TreeNode* root) {
if (root == NULL)
return;
printf("%d ", root->data);
preorderTraversal(root->lchild);
preorderTraversal(root->rchild);
}
// 中序遍历以打印树的内容
void inorderTraversal(TreeNode* root) {
if (root == NULL)
return;
inorderTraversal(root->lchild);
printf("%d ", root->data);
inorderTraversal(root->rchild);
}
// 主函数
int main() {
int arr[6] = {15, 3, 7, 10, 9, 8};
TreeNode* root = NULL;
for (int i = 0; i < 6; i++) {
root = insert(root, arr[i]);
}
printf("中序遍历结果:");
inorderTraversal(root);
printf("\n");
printf("前序遍历结果:");
preorderTraversal(root);
return 0;
}