1、二叉树的创建
#include <cstdio>
// 二叉树
struct node
{
int data;
node *lchild;
node *rchild;
};
// 建立1棵二叉树(只有1个根结点)
node *Create(int val)
{
node *root = new node({val, nullptr, nullptr});
return root;
}
// 查找二叉树中值为x的结点并修改其数据域
void Search(node *root, int x, int newdata)
{
if (!root) // 树不空 找
{
if (root->data == x) // 找到了, 修改退出
{
root->data = newdata;
return;
}
else // 没找到接着分别去左右子树找
{
Search(root->lchild, x, newdata);
Search(root->rchild, x, newdata);
}
}
else // 树为空 结束
return;
}
// 二叉树结点的插入 //引用--操作原数据 int & a 等价 node * & root;
void Insert(node * &root, int x)
{
//插入位置? 结点的左右子树都有可能
if (!root)
{
//插入规则未定
Insert(root->lchild, x);
Insert(root->rchild, x);
}
else
{
//在空位置新建1个结点
root = new node({x, nullptr, nullptr});
return ;//结束递归
}
}
//二叉树的创建(1整棵非空树)
node * Create(int arr[], int n)
{
node * root = new node;
for (int i = 0; i < n; ++i)
Insert(root, arr[i]);
return root;
}
int main()
{
return 0;
}
1、二叉树的遍历
#include <cstdio>
#include <queue>
struct node
{
int data;
node *lchild;
node *rchild;
};
// 树结点增加层次
struct Layernode
{
int data;
int layer;
Layernode *lchild;
Layernode *rchild;
};
// 先根遍历 根-左-右
void PreOrderTraverse(node *root)
{
// 深度优先DFS 一条路走到底--不行就返回上一个(位置)函数调用
// 树为空结束--递归出口
// 树不为空 1 访问根结点
// 2 先根遍历左子树
// 3 先根遍历右子树
if (!root)
return;
else
{
printf("%d ", root->data);
PreOrderTraverse(root->lchild);
PreOrderTraverse(root->rchild);
}
}
// 中根遍历 左-根-右
void InOrderTraverse(node *root)
{
// 深度优先搜索DFS 一条路走到底 不行就回退上一个位置(返给函数的调用者)
// 树为空--递归边界
// 树不空 中根遍历左子树
// 访问根结点
// 中根遍历右子树
if (!root)
return;
else
{
InOrderTraverse(root->lchild);
printf("%d ", root->data);
InOrderTraverse(root->rchild);
}
}
// 后根遍历 左-右-根
void PostOrderTraverse(node *root)
{
// 深度优先搜索DFS 一条路走到底 不行就回退上一个位置(返给函数的调用者)
// 树为空 --递归边界
// 树不空 后根遍历左子树
// 后根遍历右子树
// 访问根结点
if (!root)
return;
else
{
PostOrderTraverse(root->lchild);
PostOrderTraverse(root->rchild);
printf("%d ", root->data);
}
}
// 层次遍历
void LayerOrderTraverse(node *root)
{
// 广度优先遍历BFS 一层一层来
// 初始化: 树的根结点入队
// 队列非空 访问队头结点
// 队头结点出队 前队头结点的左右孩子结点入队
// 队列空 结束
if (!root)
return;
else
{
std::queue<node *> q;
node *tmp = nullptr; // 用来保存队头元素
q.push(root);
while (!q.empty())
{
tmp = q.front();
printf("%d ", tmp->data); // 访问队头结点
q.pop(); // 队头结点出队
if (tmp->lchild)
q.push(tmp->lchild);
if (tmp->rchild)
q.push(tmp->rchild); // 前队头结点的左右孩子入队
}
}
}
// 层次遍历计算出每个结点所处的层次
void LayerOrderTraverse(Layernode *root)
{
if (!root)
return;
else
{
// 根结点层次置1 根结点入队
std::queue<Layernode *> q;
Layernode *tmp = nullptr; // 用来记录队头结点
root->layer = 1;
q.push(root);
while (!q.empty())
{
tmp = q.front();
// 访问队头结点
printf("data = %d layer = %d\n", tmp->data, tmp->layer);
// 队头结点出队
q.pop();
// 原队头结点的左右孩子入队
if (tmp->lchild)
{
tmp->lchild->layer = tmp->layer + 1;
q.push(tmp->lchild);
}
if (tmp->rchild)
{
tmp->rchild->layer = tmp->layer + 1;
q.push(tmp->rchild);
}
}
}
}
int main()
{
return 0;
}
1、从先序+中序遍历序列推导二叉树
#include <cstdio>
#include <queue>
// 二叉树结点
struct node
{
char data;
node *lchild;
node *rchild;
};
// 由先序序列和中序序列创建二叉树
node *Create(char pre[], int pbegin, int pend, char cur[], int cbegin, int cend)
{
// 递归出口
if (pbegin > pend)
return nullptr;
// 递归
// 1、开辟根结点
node *root = new node({pre[pbegin], nullptr, nullptr});
// 查找根结点在中序数组的位置
int i, j;
for (i = cbegin; i <= cend; ++i)
{
if (cur[i] == pre[pbegin])
{
j = i; // 以该位置划分左右子树的位置
break;
}
}
// 2、创建根结点的左子树 左子树结点个数 = j - cbegin;
root->lchild = Create(pre, pbegin + 1, pbegin + j - cbegin, cur, cbegin, j - 1);
// 3、创建根结点的右子树 右子树结点个数 = cend - (j + 1);
root->rchild = Create(pre, pbegin + j - cbegin + 1, pend, cur, j + 1, cend);
// 二叉树创建结束
return root;
}
// 层次遍历
void LayerOrderTraverse(node *root)
{
// 根结点入队
// 队列不空 访问队头结点 队头出队 原队头的左右孩子入队
// 队列空 结束
if (!root)
return;
std::queue<node *> q;
node *tmp = nullptr;
q.push(root);
while (!q.empty())
{
tmp = q.front();
printf("%c ", tmp->data);
q.pop();
if (tmp->lchild)
q.push(tmp->lchild);
if (tmp->rchild)
q.push(tmp->rchild);
}
printf("\n");
}
int main()
{
// 先序序列
char pre[8] = {'A', 'B', 'D', 'E', 'C', 'F', 'G'};
// 中序序列
char cur[8] = {'B', 'E', 'D', 'A', 'F', 'C', 'G'};
// 创建二叉树
node *root = Create(pre, 0, 6, cur, 0, 6);
// 层次遍历检查二叉树
LayerOrderTraverse(root);
return 0;
}
2、二叉排序树
#include <cstdio>
#include <queue>
// 二叉排序树 二叉搜索树 二叉查找树
struct node
{
int data;
node *lchild;
node *rchild;
};
// 二叉排序树的查找
node *Search(node *root, int x)
{
// 树为空 查找失败 结束
if (!root)
return nullptr;
// 树不空 递归查找
// root->data < x 去root的左子树查找
// root->data > x 去root的右子树查找
// root->data = x 查找成功 结束
if (root->data < x)
Search(root->lchild, x);
else if (root->data > x)
Search(root->rchild, x);
else
{
printf("查找成功\n");
return root;
}
}
// 二叉排序树结点的插入
void Insert(node *&root, int x)
{
// 结点已经存在--不用插入
// 插入--空结点的位置--查找失败的位置
if (!root)
{
// 查找失败
root = new node({x, nullptr, nullptr});
return;
}
else
{
if (root->data > x)
Insert(root->lchild, x);
else if (root->data < x)
Insert(root->rchild, x);
else
return;
}
}
// 二叉排序树的建立
node *Create(int data[], int n)
{
// 挨个插入
node *root = nullptr; // 新建根结点
for (int i = 0; i < n; ++i)
Insert(root, data[i]); // 每个结点挨个插入
return root;
}
// 二叉排序树结点的删除
void Del(node *&root, int x)
{
// 找到该结点
if (!root)
return; // 没有该结点--删除失败
else
{
if (root->data < x)
Del(root->rchild, x); // 去右子树上寻找
else if (root->data > x)
Del(root->lchild, x); // 去左子树上寻找
else // 找到了, 怎么释放该结点
{
// 找双亲结点不现实, 每一轮root都更新了 无从找起
// 另一种删除方式, 找某个结点值代替它 释放某个结点
node * tmp = nullptr;
if (!root->lchild && !root->rchild)
{
// 该结点没有孩子结点(叶子直接置空) // 叶子最好删除
tmp = root;
root = nullptr;
delete tmp;
return ;
}
else if (!root->lchild)
{
// 该结点左子树为空时
tmp = root;
// 在右子树中寻找最小结点(叶子 或第一个左孩子为空的结点)取代
node * pre = root;
node * cur = root->rchild;
while (cur)
{
pre = cur;
cur = cur->lchild; // 往左一直找
} // pre指向最小结点
tmp->data = pre->data;
//递归到叶子情况
// 右子树开始继续删除
Del(root->rchild, pre->data);
}
else
{
// 该结点右子树为空
tmp = root;
// 在左子树中寻找最大结点(叶子 或第一个右孩子为空的结点)取代
node * pre = root;
node * cur = root->lchild;
while (cur)
{
pre = cur;
cur = cur->rchild; // 往右一直找
} // pre指向最大结点
tmp->data = pre->data;
//递归到叶子情况
// 左子树开始继续删除 // 要反给原二叉树 用root相关的
Del(root->lchild, pre->data);
}
}
}
}
// 层次遍历检查
void LayerOrderTraverse(node *root)
{
// 根结点入队
// 队列不空 访问队头结点 队头出队 原队头的左右孩子入队
// 队列空 结束
if (!root)
return;
std::queue<node *> q;
node *tmp = nullptr;
q.push(root);
while (!q.empty())
{
tmp = q.front();
printf("%d ", tmp->data);
q.pop();
if (tmp->lchild)
q.push(tmp->lchild);
if (tmp->rchild)
q.push(tmp->rchild);
}
printf("\n");
}
int main()
{
// 创建1棵排序二叉树
node *root = nullptr;
int data[7] = {5, 3, 7, 2, 4, 6};
root = Create(data, 6);
LayerOrderTraverse(root);
// 删除
Del(root, 5);
LayerOrderTraverse(root);
return 0;
}
2、二叉排序树的性质-层次遍历结果有序(从小到大)
#include <cstdio>
#include <queue>
struct node
{
int data;
node * lchild;
node * rchild;
};
// 二叉排序树的插入
void Insert(node * &root, int x)
{
if (!root)
{
// 插入位置(插入后成叶子结点)
root = new node({x, nullptr, nullptr});
return ;
}
else
{
if (x > root->data)
Insert(root->rchild, x); // 往右子树上插入
else if (x < root->data)
Insert(root->lchild, x); // 往左子树上插入
else
return ; // 已有该结点,不再插入
}
}
// 创建二叉排序树
node * Create(int data[], int n)
{
node * root = nullptr;
for (int i = 0; i < n; ++i)
Insert(root, data[i]);
return root;
}
// 层次遍历(基于广度优先搜索BFS)
void LayerOrderTraverse(node *root)
{
// 根结点入队
// 队列不空 访问队头结点 队头出队 原队头的左右孩子入队
// 队列空 结束
if (!root)
return;
std::queue<node *> q;
node *tmp = nullptr;
q.push(root);
while (!q.empty())
{
tmp = q.front();
printf("%d ", tmp->data);
q.pop();
if (tmp->lchild)
q.push(tmp->lchild);
if (tmp->rchild)
q.push(tmp->rchild);
}
printf("\n");
}
// 中序遍历(基于深度优先搜索DFS)
void InOrderTraverse(node * root)
{
// 左根右
if (!root)
return ;
else
{
InOrderTraverse(root->lchild);
printf("%d ", root->data);
InOrderTraverse(root->rchild);
}
}
int main()
{
// 二叉排序树的性质 中序遍历得到有序序列(从小到大)
node * root = nullptr;
int data[7] = {8, 6, 10, 5, 7, 9, 11};
root = Create(data, 7);
printf("层次遍历\n");
LayerOrderTraverse(root);
printf("中序遍历\n");
InOrderTraverse(root);
printf("\n");
return 0;
}
3、平衡二叉树
#include <cstdio>
struct node
{
int data;
node * lchild;
node * rchild;
int height; // 结点的的高度(子树总共多少层) // 用来计算平衡因子
};
// 新建1个结点
node * New_node(int x)
{
node * root = new node({x, nullptr, nullptr, 1});
return root;
}
// 获取某个结点的高度
int Get_Height(node * root)
{
if (!root)
return 0;
else
return root->height;
}
// 计算某个结点的平衡因子 = 左子树高度 - 右子树高度
int Get_Balance(node * root)
{
return root->lchild->height - root->rchild->height;
}
// 更新某个结点的高度 = MAX(左子树高度, 右子树高度) + 1
int Update_Height(node * root)
{
root->height = (root->lchild->height > root->rchild->height) ?
root->lchild->height + 1 : root->rchild->height + 1;
return root->height;
}
int main()
{
return 0;
}
3、平衡二叉树的失衡调整--转化成LL RR型失衡
#include <cstdio>
#include <math.h>
struct node
{
int data;
node *lchild;
node *rchild;
int height; // 增加结点的高度, 用来计算结点的平衡因子(左子树高度-右子树高度)
};
// 新建1个结点
node * New_Node(int x)
{
node * tmp = new node({x, nullptr, nullptr, 0}); // 新结点没有子树=该结点高度为0
return tmp;
}
// 获取某个结点的高度
int Get_Height(node * root)
{
if (!root)
return -1;
else
return root->height;
}
// 计算某个结点的平衡因子
int Get_Balance(node * root)
{
return root->lchild->height - root->rchild->height; // 该结点的左右子树高度之差
}
// 更新某个结点的高度
int Update_Height(node * root)
{
root->height = (int)fmax(root->lchild->height, root->rchild->height) + 1;
return root->height;
}
// 二叉平衡树的查找
void Search(node *root, int x)
{
// 本身仍然是二叉排序树
if (!root)
{
printf("没找到查找失败\n");
return; // 树所有结点均遍历, 查找失败
}
else
{
if (root->data < x)
Search(root->rchild, x);
else if (root->data > x)
Search(root->lchild, x);
else
{
printf("找到了 %d\n", root->data);
return;
}
}
}
// 左旋 RR型插入新结点 root < B < 新结点
void L(node * &root)
{
// 1、B的原左子树成为A的右子树
// 2、A(root)成为B的左子树
// 3、B成为新的根结点
node * tmp = root->rchild; // 记录B的位置
root->rchild = tmp->lchild; // B的原左子树成为A的右子树
tmp->lchild = root; // A成为B的左子树 // 已经完成调整
// 更新A B结点的高度
Update_Height(root);
Update_Height(tmp);
root = tmp; // B成为新的根结点
}
// 右旋 LL型插入新结点 root > B > 新结点
void R(node * &root)
{
// 1、B的原右子树成为root的左子树
// 2、root成为B的右子树
// 3、B成为新的root结点
node * tmp = root->lchild;
root->rchild = tmp->rchild;
tmp->rchild = root; // 已经完成调整
// 更新A B结点的高度
Update_Height(root);
Update_Height(tmp);
root = tmp; // 修改根结点的位置
}
// 二叉平衡树的插入
void Insert(node * &root, int x)
{
// 4种插入的平衡调整策略 左旋、右旋的使用
// 1、LL型插入新结点 root > B > 新结点 右旋
// 2、RR型插入新结点 root < B < 新结点 左旋
// 3、LR型插入新结点 B < 新结点 < root //先右旋 --- RR型
// 4、RL型插入新结点 新结点 < B > root //先左旋 --- LL型
if (!root)
{
// 插入的位置
root = new node({x, nullptr, nullptr, 0});
return ;
}
else
{
if (root->data > x)
{
Insert(root->lchild, x);
Update_Height(root); // 更新树高
if (Get_Balance(root) == 2) // 插入新结点后失衡了 LL型和LR型进行调整
{
if (Get_Balance(root->lchild) == 1) // LL型
R(root);
else if (Get_Balance(root->lchild) == -1) // LR型
{
L(root->lchild); // 先左旋成LL型
R(root);
}
}
}
else
{
Insert(root->rchild, x);
Update_Height(root); // 更新树高
if (Get_Balance(root) == -2) // 插入新结点后失衡了 RR型和RL型进行调整
{
if (Get_Balance(root->rchild) == -1) // RR型
L(root);
else if (Get_Balance(root->rchild) == 1) // RL型
{
R(root->rchild); // 先右旋
L(root);
}
}
}
}
}
// AVL(二叉平衡)树的建立
node * Create(int data[], int n)
{
node * root = nullptr;
for (int i = 0; i < n; ++i)
Insert(root, data[i]);
return root;
}
int main()
{
return 0;
}
4、哈夫曼树
#include <cstdio>
// n个结点构成1片森林 F = {T1, T2, ... , Tn};(每个结点作为1棵树(只有根结点))
// 1、从森林中剔除2棵权值最小的树作为新树的左右孩子 // 贪心
// 2、新树加入森林(新树的权值 = 左右孩子权的和)
// 重复1、2、直到森林剩下1棵树(哈夫曼树)
// 选最小的2棵--小根堆--优先队列
#include <queue>
// 计算合并一堆果子消耗的最小体力
int Haffman(int arr[], int len)
{
// 定义1个小根堆(默认是大根堆)
std::priority_queue<int, std::vector<int>, std::greater<int> > q; // > >空1个, 避免当成 >> 右移
// 把这堆果子入队
int i, left, right, nval, sum = 0;
for (i = 0; i < len; ++i)
q.push(arr[i]);
// 1、从队列内连续2次取出队头(出队)
// 2、这2次的值求和赋给新数 新数加入队列(入队)
// 重复1、2、直到队列内只剩1个元素
while (q.size() >= 2)
{
left = q.top();
q.pop();
right = q.top();
q.pop();
nval = left + right;
sum += nval;
q.push(nval);
}
nval = q.top();
q.pop();
return sum;
}
int main()
{
int arr[5] = {1, 2, 2, 3, 6};
printf("%d\n", Haffman(arr, 5));
return 0;
}
5、并查集
#include <cstdio>
#define MAXSIZE 100
// 并查集-合并查找集合
int Father[MAXSIZE];
// 顺序存树实现 int Father[N]; Father[i] = m; m结点为i结点的父结点(i m均在集合内)
// 1、合并 // 合并2个集合
// 2、查找 // 判断2个元素是否在同一个集合内
// 初始化并查集
void Init(int Father[], int n)
{
int i;
for (i = 0; i < n; ++i)
Father[i] = i;
return;
}
// 2、查找 判断元素是否在集合内 // 寻找某个结点所在集合的根结点
int FindFather(int val)
{
// 只知道i结点的父结点--一直找前趋--根结点为止(结束递归)
if (val == Father[val])
return val; // val元素在集合内
else
return FindFather(Father[val]);
}
// 1、合并 2个不同集合合并(从2个根结点中保留1个)
void Union(int a, int b)
{
int faA = FindFather(a); // 寻找a结点的根
int faB = FindFather(b); // 寻找b结点的根
if (faA != faB) // 判断2个元素是否在同一个集合
{
Father[faB] = faA; // 保留a的根结点
}
}
int main()
{
return 0;
}