4种旋转操作:
1. 左左旋(LL)
2. 左右旋(LR)
3. 右右旋(RR)
4. 右左旋(RL)
创建二叉树实现下图操作:
实现代码:
#include <stdio.h>
#include <stdlib.h>
// 平衡二叉树,首先是有序二叉树,任意子树的高度差都小于等于1。
// 定义二叉树节点结构体
typedef struct TreeNode
{
int data; // 数据域
int height; // 记录树的高度
struct TreeNode* LChild; // 指向左孩子节点指针
struct TreeNode* RChild; // 指向右孩子节点指针
}TreeNode;
// 1.创建节点 并 插入进行链接 注意要传递二级指针,因为要进行改变节点
void CreateAndInsert(TreeNode** T, int data);
// 遍历输出
// 2.前序遍历 递归
void PreOrder(TreeNode* T);
// 2.1、前序遍历 栈实现
void PreOrderStack(TreeNode* T);
// 3.中序遍历 递归
void MidOrder(TreeNode* T);
// 3.1、中序遍历 栈实现
void MidOrderByStack(TreeNode* T);
// 4.后序遍历 递归
void LastOrder(TreeNode* T);
// 4.1、后序遍历 栈实现
void LastOrderByStack(TreeNode* T);
// 获取树高度
// 5.获取树节点的高度
int getHeight(TreeNode* tree);
// 5.1、获取树节点的最大高度 左右子树的最大高度
// 参数1:左子树高度 参数2:右子树高度
int maxHeight(int leftHeight, int rightHeight);
// 进行旋转操作
//6.1、 进行LL 调整 参数1:父节点 参数2:父节点地址
void LL_Rotation(TreeNode* fatherNode, TreeNode** root);
//6.2、 进行RR 调整 参数1:父节点 参数2:父节点的地址
void RR_Rotation(TreeNode* fatherNode, TreeNode** root);
int main()
{
// 定义节点
TreeNode* T = NULL;
// 创建或插入节点
for (int i = 1; i < 7; i++)
{
CreateAndInsert(&T, i);
}
// 前序遍历 递归 打印数据
printf("前序遍历(利用递归):");
PreOrder(T);
// 前序遍历 栈实现 打印数据
printf("\n前序遍历(利用栈实现):");
PreOrderStack(T);
// 中序遍历 递归
printf("中序遍历(利用递归):");
MidOrder(T);
// 中序遍历 栈实现 打印数据
printf("\n中序遍历(利用栈实现):");
MidOrderByStack(T);
// 后序遍历 递归
printf("\n后序遍历(利用递归):");
LastOrder(T);
// 后序遍历 栈实现 打印数据
printf("\n后序遍历(利用栈实现):");
LastOrderByStack(T);
system("pause>0");
return 0;
}
// 1.创建节点 并 插入进行链接 注意要传递二级指针,因为要进行改变节点
void CreateAndInsert(TreeNode** T, int data)
{
// 检验参数合法性
if (NULL == T)
{
return;
}
// 判断树是否有节点 (空树)父节点 则进行创建节点操作
if (NULL == *T)
{
// 动态申请内存空间 创建节点
*T = (TreeNode*)malloc(sizeof(TreeNode));
// 判空
if (NULL == *T)
return;
// 给节点成员赋值
(*T)->data = data;
(*T)->height = 0;
(*T)->LChild = NULL;
(*T)->RChild = NULL;
}
else if (data < (*T)->data)
{// 将节点插入到父节点的左孩子上
// 利用递归 将节点插入到父节点的左孩子上
CreateAndInsert(&(*T)->LChild, data);
// 获取左右子树的高度 用于计算树的高度差
int Lheight = getHeight((*T)->LChild);
int Rheight = getHeight((*T)->RChild);
// 判断高度差
if (Lheight - Rheight == 2)
{
// 通过要插入的节点数据与父节点的数据比较大小进行调整
if (data < (*T)->LChild->data)
{
// 进行LL 调整 参数1:父节点 参数2:父节点地址
LL_Rotation(*T, T);
}
else
{
// 进行LR 调整
// 先进行RR 调整
RR_Rotation((*T)->LChild, &(*T)->LChild);
// 再进行LL 调整
LL_Rotation(*T, T);
}
}
}
else if (data > (*T)->data)
{// 将节点插入到父节点的右孩子上
// 利用递归 将节点插入到父节点的右孩子上
CreateAndInsert(&(*T)->RChild, data);
// 获取左右子树的高度 用于计算树的高度差
int Lheight = getHeight((*T)->LChild);
int Rheight = getHeight((*T)->RChild);
// 判断高度差
if (Rheight - Lheight == 2)
{
// 通过要插入的节点数据与父节点的数据比较大小进行调整
if (data > (*T)->RChild->data)
{
// 进行RR 调整 参数1:父节点 参数2:父节点的地址
RR_Rotation(*T, T);
}
else
{
// 进行RL 调整
// 先进行LL 调整
LL_Rotation((*T)->RChild, &(*T)->RChild);
// 再进行RR 调整
RR_Rotation(*T, T);
}
}
}
// 调整树的高度
(*T)->height = maxHeight(getHeight((*T)->LChild), getHeight((*T)->RChild)) + 1;
}
// 2.前序遍历 根左右
void PreOrder(TreeNode* T)
{
// 当节点不为空时 进行遍历输出
if (NULL != T)
{
// 直接先打印节点数据
printf("%d ", T->data);
// 递归 打印左孩子节点
PreOrder(T->LChild);
// 递归 打印右孩子节点
PreOrder(T->RChild);
}
}
// 2.1、前序遍历 栈实现 根左右
void PreOrderStack(TreeNode* T)
{
// 当节点为空时,退出函数
if (NULL == T)
return;
// 声明数组(栈)
TreeNode* stack[100];
// 定义用于下标的变量
int stackTop = -1;
// 定义移动指针
TreeNode* pMove = T;
// 进行循环输出数据 前序遍历
while (-1 != stackTop || NULL != pMove)
{
// 入栈操作 移动指针左移
while (NULL != pMove)
{
// 前序遍历先打印数据
printf("%d ", pMove->data);
// 入栈操作 将移动指针装进数组中
stack[++stackTop] = pMove;
// 移动指针左移
pMove = pMove->LChild;
}
// 出栈操作 移动指针右移
if (-1 != stackTop)
{
// 将数组元素赋给移动指针
pMove = stack[stackTop--];
// 移动指针右移
pMove = pMove->RChild;
}
}
printf("\n");
}
// 3.中序遍历 递归 左根右
void MidOrder(TreeNode* T)
{
// 当节点不为空时 进行打印数据
if (NULL != T)
{
// 打印左孩子节点 递归调用
MidOrder(T->LChild);
// 打印数据
printf("%d ", T->data);
// 打印右孩子节点 递归调用
MidOrder(T->RChild);
}
}
// 3.1、中序遍历 栈实现 左根右
void MidOrderByStack(TreeNode* T)
{
// 当节点为空时,退出函数
if (NULL == T)
return;
// 声明数组(栈)
TreeNode* stack[100];
// 定义用于下标的变量
int stackTop = -1;
// 定义移动指针
TreeNode* pMove = T;
// 进行遍历 中序遍历 入栈、出栈操作
while (-1 != stackTop || NULL != pMove)
{
// 入栈
while (NULL != pMove)
{
// 入栈 将移动指针装进数组中
stack[++stackTop] = pMove;
// 移动指针左移
pMove = pMove->LChild;
}
// 出栈
if (-1 != stackTop)
{
// 出栈操作 将数组元素赋给移动指针
pMove = stack[stackTop--];
// 打印数据
printf("%d ", pMove->data);
// 移动指针右移
pMove = pMove->RChild;
}
}
}
// 4.后序遍历 递归
void LastOrder(TreeNode* T)
{
// 当节点不为空时 进行打印数据操作
if (NULL != T)
{
// 左右根
LastOrder(T->LChild);
LastOrder(T->RChild);
// 打印数据 根
printf("%d ", T->data);
}
}
// 4.1、后序遍历 栈实现
void LastOrderByStack(TreeNode* T)
{
// 当节点为空时 退出函数
if (NULL == T)
return;
// 声明数组 栈
TreeNode* stack[100];
// 定义用于下标的变量
int stackTop = -1;
// 定义移动指针
TreeNode* pMove = T;
// 声明一个记录已打印数据的指针
TreeNode* pPreMove = NULL;
// 循环入栈操作
while (NULL != pMove)
{
// 入栈 将移动指针装进数组中
stack[++stackTop] = pMove;
// 移动指针左移
pMove = pMove->LChild;
}
// 出栈操作
while (-1 != stackTop)
{
// 出栈 将数组元素赋给移动指针
pMove = stack[stackTop--];
// 判断是否进行打印数据 当右节点为空,或是右节点已被访问,则进行打印数据操作
if (NULL == pMove->RChild || pPreMove == pMove->RChild)
{
// 打印数据
printf("%d ", pMove->data);
// 记录已打印的数据节点
pPreMove = pMove;
}
else
{
if (NULL != pMove)
{
// 入栈 将移动指针装进数组中
stack[++stackTop] = pMove;
// 移动指针右移
pMove = pMove->RChild;
}
// 移动指针左移
while (NULL != pMove)
{
// 入栈 将移动指针装进数组中
stack[++stackTop] = pMove;
// 移动指针左移
pMove = pMove->LChild;
}
}
}
}
// 5.获取树节点的高度
int getHeight(TreeNode* tree)
{
// 当树不为空时,返回节点的高度
if (NULL != tree)
{
return tree->height;
}
return 0;
}
// 5.1、获取树节点的最大高度 左右子树的最大高度
// 参数1:左子树高度 参数2:右子树高度
int maxHeight(int leftHeight, int rightHeight)
{
// 比较返回最大值
if (leftHeight > rightHeight)
{
return leftHeight;
}
else
{
return rightHeight;
}
}
//6.1、 进行LL 调整 参数1:父节点 参数2:父节点地址
void LL_Rotation(TreeNode* fatherNode, TreeNode** root)
{
// 检验参数
if (NULL == fatherNode)
return;
// 注意要判断父节点的左孩子是否为空
if (NULL == fatherNode->LChild)
return;
// 获取中间节点
TreeNode* midNode = fatherNode->LChild;
// 如果中间节点有右孩子节点,将右孩子连接到父节点的左孩子上
fatherNode->LChild = midNode->RChild;
// 将父节点连接到中间节点的右孩子上
midNode->RChild = fatherNode;
// 获取父节点树的高度 最大高度 注意要+1操作 加上本身节点
fatherNode->height = maxHeight(getHeight(fatherNode->LChild), getHeight(fatherNode->RChild)) + 1;
// 获取中间节点树的高度 注意要+1操作 加上本身节点
midNode->height = maxHeight(getHeight(midNode->LChild), getHeight(midNode->RChild)) + 1;
// 将中间节点设置成根节点(父节点)
*root = midNode;
}
//6.2、 进行RR 调整 参数1:父节点 参数2:父节点的地址
void RR_Rotation(TreeNode* fatherNode, TreeNode** root)
{
// 检验参数
if (NULL == fatherNode)
return;
//
// 获取中间节点
TreeNode* midNode = fatherNode->RChild;
// 将中间节点的左孩子插入到父节点的右孩子上
fatherNode->RChild = midNode->LChild;
// 将父节点连接到中间节点左孩子上
midNode->LChild = fatherNode;
// 获取父节点树的高度 注意要加上自身节点 +1
fatherNode->height = maxHeight(getHeight(fatherNode->LChild), getHeight(fatherNode->RChild)) + 1;
// 获取中间节点 树的高度 注意要加上自身节点 +1
midNode->height = maxHeight(getHeight(midNode->LChild), getHeight(midNode->RChild)) + 1;
// 重置根节点(父节点) 将中间节点置成根
*root = midNode;
}
程序运行结果: