界面展示:
相关文章目录
C/C++平衡二叉树实现 —— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(设计详解)
LNK2019 无法解析的外部符号 “public: void __thiscall XXX…” —— 关于MFC中C++模板类报错问题
前言
该程序基于平衡二叉树实现,即默认构建为平衡二叉树,C++编写,可运行于vs2015以上版本环境。
一、实现功能
- 平衡二叉树的创建、绘制。
- 实现操作:插入结点、删除结点、清空二叉树。
- 实现输出:输出先序遍历、中序遍历、后序遍历、层序遍历的结果。
二、代码设计
1. 平衡二叉树类定义
AvlTree.cpp
(1)定义平衡二叉树的结点结构
平衡二叉树的结点结构:
// 平衡二叉树结点结构
template <typename T>
struct AvlNode
{
T data; // 结点数据
AvlNode<T>* left; // 左子节点指针
AvlNode<T>* right; // 右子节点指针
int height; // 结点所在高度
AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(0) { } // 构造方法
};
(2)定义层序遍历链队列相关结构和函数
链队列结构:
/* 链队列结点结构(用于层序遍历) */
template <typename T>
struct QNode
{
AvlNode<T>* t; // 结点数据域
struct QNode<T>* next; // 结点指针域
};
/* 链队列结构(用于层序遍历) */
template <typename T>
struct LinkQueue
{
QNode<T>* front; // 队首指针
QNode<T>* rear; // 队尾指针
};
初始化链队列函数、进队函数、出队函数定义:
/* 初始化链队列函数 */
template <typename T>
Status InitQueue(LinkQueue<T>& Q)
{
// 创建一个带附加头结点的空链队列
Q.front = Q.rear = (QNode<T>*)malloc(sizeof(QNode<T>));
if (!Q.front) {
return ERROR;
}
Q.front->next = NULL;
return OK;
}
/* 进队函数 */
/* e:插入元素 */
template <typename T>
Status EnQueue(LinkQueue<T>& Q, AvlNode<T>* e)
{
// 将元素e插入到链队列中
QNode<T>* p;
p = (QNode<T>*)malloc(sizeof(QNode<T>));
if (!p) {
return ERROR;
}
p->t = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
/* 出队函数 */
/* e:出队元素 */
template <typename T>
Status DeQueue(LinkQueue<T>& Q, AvlNode<T>*& e)
{
// 出队,将出队元素放入e中
QNode<T>* p;
if (Q.rear == Q.front) {
return ERROR;
}
p = Q.front->next;
e = p->t;
Q.front->next = p->next;
if (Q.rear == p) {
Q.rear = Q.front;
}
free(p);
return OK;
}
(3)获取最大值、最小值结点,获取树的高度,查找结点,完全二叉树判断
(根据需要提供)
// 获取树的最大值结点
template <typename T>
AvlNode<T>* FindMax(AvlNode<T>* t) const
{
if (t == NULL)
return NULL;
if (t->right == NULL)
return t;
return FindMax(t->right);
}
// 返回树的最小值结点
template <typename T>
AvlNode<T>* FindMin(AvlNode<T>* t) const
{
if (t == NULL)
return NULL;
if (t->left == NULL)
return t;
return FindMin(t->left);
}
// 获取树的高度
template <typename T>
int GetHeight(AvlNode<T>* t)
{
if (t == NULL)
return -1;
else
return t->height;
}
//查找结点
template <typename T>
bool Contains(AvlNode<T>* t, const T x) const
{
if (t == NULL)
return false;
if (x < t->data)
return Contains(t->left, x);
else if (x > t->data)
return Contains(t->right, x);
else
return true;
}
// 判断是否为完全二叉树
template <typename T>
bool IsCompleteTree(AvlNode<T>* t) {
if(!t) return true;
LinkQueue<T> Q;
AvlNode<T>* p;
p=t;
InitQueue(Q);//初始化队列
EnQueue(Q,p);//根结点入队
while(Q.front != Q.rear){
DeQueue(Q,p);
if(!p) break;//读到空指针则停止循环
EnQueue(Q,p->left);//左孩子入队
EnQueue(Q,p->right);//右孩子入队
}
while(Q.front != Q.rear){//检查此时队列中是否还有未访问到的结点
DeQueue(Q,p);
if(p) return false;
}
return true;
}
(4)定义结点旋转函数 —— 用于平衡二叉树的结点平衡
LL调整(左单旋)、RR调整(右单旋)、LR调整(先左旋再右旋)、RL调整(先右旋再左旋)。
关于平衡二叉树的旋转问题请参考:平衡二叉树的构造过程图解
//单旋转
//左左插入导致的不平衡
template <typename T>
AvlNode<T>* LL(AvlNode<T>* t)
{
AvlNode<T>* q = t->left;
t->left = q->right;
q->right = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
//单旋转
//右右插入导致的不平衡
template <typename T>
AvlNode<T>* RR(AvlNode<T>* t)
{
AvlNode<T>* q = t->right;
t->right = q->left;
q->left = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
//双旋转
//插入点位于t的左儿子的右子树
template <typename T>
AvlNode<T>* LR(AvlNode<T>* t)
{
//双旋转可以通过两次单旋转实现
//对t的左结点进行RR旋转,再对根节点进行LL旋转
RR(t->left);
return LL(t);
}
//双旋转
//插入点位于t的右儿子的左子树
template <typename T>
AvlNode<T>* RL(AvlNode<T>* t)
{
LL(t->right);
return RR(t);
}
(5)定义结点插入函数
template <typename T>
void InsertAvlNode(AvlNode<T>*& t, T x)
{
if (t == NULL)
t = new AvlNode<T>(x);
else if (x < t->data)
{
InsertAvlNode(t->left, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{
//分两种情况 左左或左右
if (x < t->left->data)//左左
t = LL(t);
else //左右
t = LR(t);
}
}
else if (x > t->data)
{
InsertAvlNode(t->right, x);
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{
if (x > t->right->data)
t = RR(t);
else
t = RL(t);
}
}
else
;//数据重复
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
(6)定义结点删除函数
template <typename T>
bool DeleteAvlNode(AvlNode<T>*& t, T x)
{
//t为空 未找到要删除的结点
if (t == NULL)
return false;
//找到了要删除的结点
else if (t->data == x)
{
//左右子树都非空
if (t->left != NULL && t->right != NULL)
{//在高度更大的那个子树上进行删除操作
//左子树高度大,删除左子树中值最大的结点,将其赋给根结点
if (GetHeight(t->left) > GetHeight(t->right))
{
t->data = FindMax(t->left)->data;
DeleteAvlNode(t->left, t->data);
}
else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
{
t->data = FindMin(t->right)->data;
DeleteAvlNode(t->right, t->data);
}
}
else
{//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
AvlNode<T>* old = t;
t = t->left ? t->left : t->right;//t赋值为不空的子结点
delete old;
}
}
else if (x < t->data)//要删除的结点在左子树上
{
//递归删除左子树上的结点
DeleteAvlNode(t->left, x);
//判断是否仍然满足平衡条件
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{
if (GetHeight(t->right->left) > GetHeight(t->right->right))
{
//RL双旋转
t = RL(t);
}
else
{//RR单旋转
t = RR(t);
}
}
else//满足平衡条件 调整高度信息
{
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
else//要删除的结点在右子树上
{
//递归删除右子树结点
DeleteAvlNode(t->right, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{
if (GetHeight(t->left->right) > GetHeight(t->left->left))
{
//LR双旋转
t = LR(t);
}
else
{
//LL单旋转
t = LL(t);
}
}
else//满足平衡性 调整高度
{
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
return true;
}
(7)定义二叉树遍历与输出函数
//前序遍历
template <typename T>
void PreOrder(AvlNode<T>* t)
{
if (t)
{
strOrderResult = strOrderResult + to_string(t->data) + " ";
PreOrder(t->left);
PreOrder(t->right);
}
}
//中序遍历
template <typename T>
void MidOrder(AvlNode<T>* t)
{
if (t)
{
MidOrder(t->left);
strOrderResult = strOrderResult + to_string(t->data) + " ";
MidOrder(t->right);
}
}
//后序遍历
template <typename T>
void PostOrder(AvlNode<T>* t)
{
if (t)
{
PostOrder(t->left);
PostOrder(t->right);
strOrderResult = strOrderResult + to_string(t->data) + " ";
}
}
//层序遍历
template <typename T>
void LevelOrder(AvlNode<T>* t)
{
LinkQueue<T> Q;
AvlNode<T>* p;
if (t != NULL)
{
InitQueue(Q);
EnQueue(Q, t);
while (Q.front != Q.rear) {
DeQueue(Q, p);
strOrderResult = strOrderResult + to_string(p->data) + " ";
if (p->left != NULL) EnQueue(Q, p->left);
if (p->right != NULL) EnQueue(Q, p->right);
}
}
}
获取遍历结果并返回
// 获取前序遍历结果
template <typename T>
string getPreOrderResult(AvlNode<T>* t) {
strOrderResult = "";
PreOrder(t);
// 返回遍历结果
return strOrderResult;
}
// 获取中序遍历结果
template <typename T>
string getMidOrderResult(AvlNode<T>* t) {
strOrderResult = "";
MidOrder(t);
// 返回遍历结果
return strOrderResult;
}
// 获取后序遍历结果
template <typename T>
string getPostOrderResult(AvlNode<T>* t) {
strOrderResult = "";
PostOrder(t);
// 返回遍历结果
return strOrderResult;
}
// 获取层序遍历结果
template <typename T>
string getLevelOrderResult(AvlNode<T>* t) {
strOrderResult = "";
LevelOrder(t);
// 返回遍历结果
return strOrderResult;
}
2. MFC窗口界面函数(窗口绘制、事件处理)
MFC_BinaryTreeDlg.cpp
(1)全局类对象定义
// 全局类对象定义
AvlTree<int> tree;
(2)平衡二叉树绘制函数定义
// 平衡二叉树绘制函数
void paintTree(CDC* pDC, AvlNode<int>*& node, int x, int y, int c, int d, int depth) { //depth为树的深度
const int width = 10; //这个值表示最底层结点之间的距离
const int height = 30; //这个值是层与层之间的距离
int interal = pow(2, double(depth) - c) * width / 2; //interal是父节点与子节点水平方向上的距离
int ix, iy;
if (node != NULL) {
pDC->Ellipse(x - 8, y - 8, x + 8, y + 8); //画圆圈
CString cStr;
cStr.Format(_T("%d"), node->data);
pDC->TextOut(x - 4, y - 8, cStr); // 写字
if (node->left != NULL)
{
pDC->MoveTo(x, y); //移动当前画笔位置
ix = x + interal; // 计算下一个节点坐标
iy = y + height;
pDC->LineTo(ix, iy); // 画线连接
paintTree(pDC, node->left, x - interal, y + height, c + 1, -1, depth);
}
if (node->right != NULL) {
pDC->MoveTo(x, y); //移动当前画笔位置
ix = x - interal; // 计算下一个节点坐标
iy = y + height;
pDC->LineTo(ix, iy); // 画线连接
paintTree(pDC, node->right, x + interal, y + height, c + 1, 1, depth); //递归调用下一层的绘图
}
}
}
(3)类型转换函数定义
// 类型转换函数:string -> LPCTSTR
std::wstring StoWs(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
(4)插入按钮事件处理
// 插入按钮事件处理
void CMFCBinaryTreeDlg::OnBnClickedButtonInsert()
{
CString cstrInsertText;
// 获取插入文本框数据
GetDlgItem(IDC_EDIT_INSERT)->GetWindowText(cstrInsertText);
if (cstrInsertText == "") {
return;
}
int data = _ttoi(cstrInsertText);
// 插入
tree.InsertAvlNode(tree.root, data);
// 平衡二叉树类型判断显示,默认为平衡二叉树
SetDlgItemText(IDC_EDIT_JUDGE_AVLTREE, StoWs("yes").c_str());
// 完全二叉树类型判断显示
if (tree.IsCompleteTree(tree.root) == true) {
SetDlgItemText(IDC_EDIT_JUDGE_CPLTREE, StoWs("yes").c_str());
}
else {
SetDlgItemText(IDC_EDIT_JUDGE_CPLTREE, StoWs("no").c_str());
}
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_PREORDER, StoWs(tree.getPreOrderResult(tree.root)).c_str());
// 显示中序遍历结果
SetDlgItemText(IDC_EDIT_MIDORDER, StoWs(tree.getMidOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_POSTORDER, StoWs(tree.getPostOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_LEVELORDER, StoWs(tree.getLevelOrderResult(tree.root)).c_str());
// 清空插入文本框
SetDlgItemText(IDC_EDIT_INSERT, StoWs("").c_str());
// 绘制二叉树
// 清空绘制区域
CRect rectDlg;
int pointWidth;
int pointHeight;
GetClientRect(rectDlg); //获得窗体的大小
pointWidth = rectDlg.Width(); //获取窗体宽度
pointHeight = rectDlg.Height(); //获取窗体高度
RedrawWindow(CRect(0, 0, pointWidth, pointHeight)); //重绘指定区域
// 绘制
int depth = tree.GetHeight(tree.root);
CDC* pDC = GetDC();
paintTree(pDC, tree.root, 750, 250, 0, 0, depth);
ReleaseDC(pDC);
}
(5)删除按钮事件处理
// 删除按钮事件处理
void CMFCBinaryTreeDlg::OnBnClickedButtonDelete()
{
CString cstrDeleteText;
// 获取删除文本框数据
GetDlgItem(IDC_EDIT_DELETE)->GetWindowText(cstrDeleteText);
if (cstrDeleteText == "") {
return;
}
int data = _ttoi(cstrDeleteText);
// 插入
tree.DeleteAvlNode(tree.root, data);
// 绘制二叉树
//piont();
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_PREORDER, StoWs(tree.getPreOrderResult(tree.root)).c_str());
// 显示中序遍历结果
SetDlgItemText(IDC_EDIT_MIDORDER, StoWs(tree.getMidOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_POSTORDER, StoWs(tree.getPostOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_LEVELORDER, StoWs(tree.getLevelOrderResult(tree.root)).c_str());
// 清空删除文本框
SetDlgItemText(IDC_EDIT_DELETE, StoWs("").c_str());
// 绘制二叉树
// 清空绘制区域
CRect rectDlg;
int pointWidth;
int pointHeight;
GetClientRect(rectDlg); //获得窗体的大小
pointWidth = rectDlg.Width(); //获取窗体宽度
pointHeight = rectDlg.Height(); //获取窗体高度
RedrawWindow(CRect(0, 0, pointWidth, pointHeight)); //重绘指定区域
// 绘制
int depth = tree.GetHeight(tree.root);
CDC* pDC = GetDC();
paintTree(pDC, tree.root, 750, 250, 0, 0, depth);
ReleaseDC(pDC);
}
(6)清除按钮事件处理
// 清除按钮事件处理
void CMFCBinaryTreeDlg::OnBnClickedButtonClear()
{
tree.root = NULL;
CRect rectDlg;
int pointWidth;
int pointHeight;
GetClientRect(rectDlg); //获得窗体的大小
pointWidth = rectDlg.Width(); //获取窗体宽度
pointHeight = rectDlg.Height(); //获取窗体高度
RedrawWindow(CRect(0, 0, pointWidth, pointHeight)); //重绘指定区域(清空)
//清空显示框
SetDlgItemText(IDC_EDIT_JUDGE_AVLTREE, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_JUDGE_CPLTREE, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_PREORDER, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_MIDORDER, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_POSTORDER, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_LEVELORDER, StoWs("").c_str());
}
(7)防止回车键确认返回,结束对话框程序
MFC_BinaryTreeDlg.h: 头文件
// CMFCBinaryTreeDlg 对话框
class CMFCBinaryTreeDlg : public CDialogEx
{
protected:
virtual void OnOK(); //对话框确认事件
//virtual void OnCancel() {} //对话框取消事件
}
MFC_BinaryTreeDlg.cpp: 实现文件
// 对话框确认事件(OK)处理
// 重载 BOOL CMFCBinaryTreeDlg::ONOK(void)
// 不做任何处理:只为防止按回车键时确认返回,结束对话框程序
void CMFCBinaryTreeDlg::OnOK() {}
(8)增加插入/删除文本框对回车键的响应、屏蔽ESC退出程序
// 插入/删除文本框对回车键的响应:触发插入/删除按钮事件
// 对PreTranslateMessage函数的重载
BOOL CMFCBinaryTreeDlg::PreTranslateMessage(MSG* pMsg)
{
//if (WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST){
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) // 捕获按下键盘回车键事件
{
int iID = GetFocus()->GetDlgCtrlID();
if (iID == IDC_EDIT_INSERT) // 插入框
{
CMFCBinaryTreeDlg::OnBnClickedButtonInsert(); // 插入按钮事件
}
if (iID == IDC_EDIT_DELETE) // 删除框
{
CMFCBinaryTreeDlg::OnBnClickedButtonDelete(); // 删除按钮事件
}
}
// 屏蔽ESC退出程序
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
//return __super::PreTranslateMessage(pMsg);
//return TRUE;
//return CFormView::PreTranslateMessage(pMsg);
}
三、运行结果
四、完整代码
MFC_BinaryTreeDlg.cpp: 实现文件
#include "pch.h"
#include <string>
#include <cstdlib>
#define OK 1
#define ERROR 0
using namespace std;
typedef int Status;
static string strOrderResult = ""; // 用于返回多种遍历结果
// 平衡二叉树结点结构
template <typename T>
struct AvlNode
{
T data; // 结点数据
AvlNode<T>* left; // 左子节点指针
AvlNode<T>* right; // 右子节点指针
int height; // 结点所在高度
AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(0) { } // 构造方法
};
/* 链队列结点结构(用于层序遍历) */
template <typename T>
struct QNode
{
AvlNode<T>* t; // 结点数据域
struct QNode<T>* next; // 结点指针域
};
/* 链队列结构(用于层序遍历) */
template <typename T>
struct LinkQueue
{
QNode<T>* front; // 队首指针
QNode<T>* rear; // 队尾指针
};
//------------- AvlTree类 -------------
template <typename T>
class AvlTree {
public:
AvlNode<T>* root;
AvlTree<T>() : root(NULL) { }
~AvlTree<T>() { }
/* 初始化链队列函数 */
template <typename T>
Status InitQueue(LinkQueue<T>& Q)
{
// 创建一个带附加头结点的空链队列
Q.front = Q.rear = (QNode<T>*)malloc(sizeof(QNode<T>));
if (!Q.front) {
return ERROR;
}
Q.front->next = NULL;
return OK;
}
/* 进队函数 */
/* e:插入元素 */
template <typename T>
Status EnQueue(LinkQueue<T>& Q, AvlNode<T>* e)
{
// 将元素e插入到链队列中
QNode<T>* p;
p = (QNode<T>*)malloc(sizeof(QNode<T>));
if (!p) {
return ERROR;
}
p->t = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
/* 出队函数 */
/* e:出队元素 */
template <typename T>
Status DeQueue(LinkQueue<T>& Q, AvlNode<T>*& e)
{
// 出队,将出队元素放入e中
QNode<T>* p;
if (Q.rear == Q.front) {
return ERROR;
}
p = Q.front->next;
e = p->t;
Q.front->next = p->next;
if (Q.rear == p) {
Q.rear = Q.front;
}
free(p);
return OK;
}
// 获取树的最大值结点
template <typename T>
AvlNode<T>* FindMax(AvlNode<T>* t) const
{
if (t == NULL)
return NULL;
if (t->right == NULL)
return t;
return FindMax(t->right);
}
// 返回树的最小值结点
template <typename T>
AvlNode<T>* FindMin(AvlNode<T>* t) const
{
if (t == NULL)
return NULL;
if (t->left == NULL)
return t;
return FindMin(t->left);
}
// 获取树的高度
template <typename T>
int GetHeight(AvlNode<T>* t)
{
if (t == NULL)
return -1;
else
return t->height;
}
// 判断是否为完全二叉树
template <typename T>
bool IsCompleteTree(AvlNode<T>* t) {
if(!t) return true;
LinkQueue<T> Q;
AvlNode<T>* p;
p=t;
InitQueue(Q);//初始化队列
EnQueue(Q,p);//根结点入队
while(Q.front != Q.rear){
DeQueue(Q,p);
if(!p) break;//读到空指针则停止循环
EnQueue(Q,p->left);//左孩子入队
EnQueue(Q,p->right);//右孩子入队
}
while(Q.front != Q.rear){//检查此时队列中是否还有未访问到的结点
DeQueue(Q,p);
if(p) return false;
}
return true;
}
//单旋转
//左左插入导致的不平衡
template <typename T>
AvlNode<T>* LL(AvlNode<T>* t)
{
AvlNode<T>* q = t->left;
t->left = q->right;
q->right = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
//单旋转
//右右插入导致的不平衡
template <typename T>
AvlNode<T>* RR(AvlNode<T>* t)
{
AvlNode<T>* q = t->right;
t->right = q->left;
q->left = t;
t = q;
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
return q;
}
//双旋转
//插入点位于t的左儿子的右子树
template <typename T>
AvlNode<T>* LR(AvlNode<T>* t)
{
//双旋转可以通过两次单旋转实现
//对t的左结点进行RR旋转,再对根节点进行LL旋转
RR(t->left);
return LL(t);
}
//双旋转
//插入点位于t的右儿子的左子树
template <typename T>
AvlNode<T>* RL(AvlNode<T>* t)
{
LL(t->right);
return RR(t);
}
template <typename T>
void InsertAvlNode(AvlNode<T>*& t, T x)
{
if (t == NULL)
t = new AvlNode<T>(x);
else if (x < t->data)
{
InsertAvlNode(t->left, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{
//分两种情况 左左或左右
if (x < t->left->data)//左左
t = LL(t);
else //左右
t = LR(t);
}
}
else if (x > t->data)
{
InsertAvlNode(t->right, x);
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{
if (x > t->right->data)
t = RR(t);
else
t = RL(t);
}
}
else
;//数据重复
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
template <typename T>
bool DeleteAvlNode(AvlNode<T>*& t, T x)
{
//t为空 未找到要删除的结点
if (t == NULL)
return false;
//找到了要删除的结点
else if (t->data == x)
{
//左右子树都非空
if (t->left != NULL && t->right != NULL)
{//在高度更大的那个子树上进行删除操作
//左子树高度大,删除左子树中值最大的结点,将其赋给根结点
if (GetHeight(t->left) > GetHeight(t->right))
{
t->data = FindMax(t->left)->data;
DeleteAvlNode(t->left, t->data);
}
else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
{
t->data = FindMin(t->right)->data;
DeleteAvlNode(t->right, t->data);
}
}
else
{//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
AvlNode<T>* old = t;
t = t->left ? t->left : t->right;//t赋值为不空的子结点
delete old;
}
}
else if (x < t->data)//要删除的结点在左子树上
{
//递归删除左子树上的结点
DeleteAvlNode(t->left, x);
//判断是否仍然满足平衡条件
if (GetHeight(t->right) - GetHeight(t->left) > 1)
{
if (GetHeight(t->right->left) > GetHeight(t->right->right))
{
//RL双旋转
t = RL(t);
}
else
{//RR单旋转
t = RR(t);
}
}
else//满足平衡条件 调整高度信息
{
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
else//要删除的结点在右子树上
{
//递归删除右子树结点
DeleteAvlNode(t->right, x);
//判断平衡情况
if (GetHeight(t->left) - GetHeight(t->right) > 1)
{
if (GetHeight(t->left->right) > GetHeight(t->left->left))
{
//LR双旋转
t = LR(t);
}
else
{
//LL单旋转
t = LL(t);
}
}
else//满足平衡性 调整高度
{
t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}
}
return true;
}
//查找结点
template <typename T>
bool Contains(AvlNode<T>* t, const T x) const
{
if (t == NULL)
return false;
if (x < t->data)
return Contains(t->left, x);
else if (x > t->data)
return Contains(t->right, x);
else
return true;
}
//前序遍历
template <typename T>
void PreOrder(AvlNode<T>* t)
{
if (t)
{
strOrderResult = strOrderResult + to_string(t->data) + " ";
PreOrder(t->left);
PreOrder(t->right);
}
}
//中序遍历
template <typename T>
void MidOrder(AvlNode<T>* t)
{
if (t)
{
MidOrder(t->left);
strOrderResult = strOrderResult + to_string(t->data) + " ";
MidOrder(t->right);
}
}
//后序遍历
template <typename T>
void PostOrder(AvlNode<T>* t)
{
if (t)
{
PostOrder(t->left);
PostOrder(t->right);
strOrderResult = strOrderResult + to_string(t->data) + " ";
}
}
//层序遍历
template <typename T>
void LevelOrder(AvlNode<T>* t)
{
LinkQueue<T> Q;
AvlNode<T>* p;
if (t != NULL)
{
InitQueue(Q);
EnQueue(Q, t);
while (Q.front != Q.rear) {
DeQueue(Q, p);
strOrderResult = strOrderResult + to_string(p->data) + " ";
if (p->left != NULL) EnQueue(Q, p->left);
if (p->right != NULL) EnQueue(Q, p->right);
}
}
}
// 获取前序遍历结果
template <typename T>
string getPreOrderResult(AvlNode<T>* t) {
strOrderResult = "";
PreOrder(t);
// 返回遍历结果
return strOrderResult;
}
// 获取中序遍历结果
template <typename T>
string getMidOrderResult(AvlNode<T>* t) {
strOrderResult = "";
MidOrder(t);
// 返回遍历结果
return strOrderResult;
}
// 获取后序遍历结果
template <typename T>
string getPostOrderResult(AvlNode<T>* t) {
strOrderResult = "";
PostOrder(t);
// 返回遍历结果
return strOrderResult;
}
// 获取层序遍历结果
template <typename T>
string getLevelOrderResult(AvlNode<T>* t) {
strOrderResult = "";
LevelOrder(t);
// 返回遍历结果
return strOrderResult;
}
};
MFC_BinaryTreeDlg.h: 头文件
新增代码
// CMFCBinaryTreeDlg 对话框
class CMFCBinaryTreeDlg : public CDialogEx
{
protected:
virtual void OnOK(); //对话框确认事件
//virtual void OnCancel() {} //对话框取消事件
}
MFC_BinaryTreeDlg.cpp: 实现文件
新增代码
// MFC_BinaryTreeDlg.cpp: 实现文件
#include "AvlTree.cpp"
// 全局类对象定义
AvlTree<int> tree;
// 平衡二叉树绘制函数
void paintTree(CDC* pDC, AvlNode<int>*& node, int x, int y, int c, int d, int depth) { //depth为树的深度
const int width = 10; //这个值表示最底层结点之间的距离
const int height = 30; //这个值是层与层之间的距离
int interal = pow(2, double(depth) - c) * width / 2; //interal是父节点与子节点水平方向上的距离
int ix, iy;
if (node != NULL) {
pDC->Ellipse(x - 8, y - 8, x + 8, y + 8); //画圆圈
CString cStr;
cStr.Format(_T("%d"), node->data);
pDC->TextOut(x - 4, y - 8, cStr); // 写字
if (node->left != NULL)
{
pDC->MoveTo(x, y); //移动当前画笔位置
ix = x + interal; // 计算下一个节点坐标
iy = y + height;
pDC->LineTo(ix, iy); // 画线连接
paintTree(pDC, node->left, x - interal, y + height, c + 1, -1, depth);
}
if (node->right != NULL) {
pDC->MoveTo(x, y); //移动当前画笔位置
ix = x - interal; // 计算下一个节点坐标
iy = y + height;
pDC->LineTo(ix, iy); // 画线连接
paintTree(pDC, node->right, x + interal, y + height, c + 1, 1, depth); //递归调用下一层的绘图
}
}
}
// 类型转换函数:string -> LPCTSTR
std::wstring StoWs(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
// 插入文本框事件
void CMFCBinaryTreeDlg::OnEnChangeEditInsert()
{
// TODO: 如果该控件是 RICHEDIT 控件,它将不
// 发送此通知,除非重写 CDialogEx::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
// TODO: 在此添加控件通知处理程序代码
}
// 插入按钮事件处理
void CMFCBinaryTreeDlg::OnBnClickedButtonInsert()
{
CString cstrInsertText;
// 获取插入文本框数据
GetDlgItem(IDC_EDIT_INSERT)->GetWindowText(cstrInsertText);
if (cstrInsertText == "") {
return;
}
int data = _ttoi(cstrInsertText);
// 插入
tree.InsertAvlNode(tree.root, data);
// 平衡二叉树类型判断显示,默认为平衡二叉树
SetDlgItemText(IDC_EDIT_JUDGE_AVLTREE, StoWs("yes").c_str());
// 完全二叉树类型判断显示
if (tree.IsCompleteTree(tree.root) == true) {
SetDlgItemText(IDC_EDIT_JUDGE_CPLTREE, StoWs("yes").c_str());
}
else {
SetDlgItemText(IDC_EDIT_JUDGE_CPLTREE, StoWs("no").c_str());
}
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_PREORDER, StoWs(tree.getPreOrderResult(tree.root)).c_str());
// 显示中序遍历结果
SetDlgItemText(IDC_EDIT_MIDORDER, StoWs(tree.getMidOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_POSTORDER, StoWs(tree.getPostOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_LEVELORDER, StoWs(tree.getLevelOrderResult(tree.root)).c_str());
// 清空插入文本框
SetDlgItemText(IDC_EDIT_INSERT, StoWs("").c_str());
// 绘制二叉树
// 清空绘制区域
CRect rectDlg;
int pointWidth;
int pointHeight;
GetClientRect(rectDlg); //获得窗体的大小
pointWidth = rectDlg.Width(); //获取窗体宽度
pointHeight = rectDlg.Height(); //获取窗体高度
RedrawWindow(CRect(0, 0, pointWidth, pointHeight)); //重绘指定区域
// 绘制
int depth = tree.GetHeight(tree.root);
CDC* pDC = GetDC();
paintTree(pDC, tree.root, 750, 250, 0, 0, depth);
ReleaseDC(pDC);
}
void CMFCBinaryTreeDlg::OnEnChangeEditDelete()
{
// TODO: 如果该控件是 RICHEDIT 控件,它将不
// 发送此通知,除非重写 CDialogEx::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
// TODO: 在此添加控件通知处理程序代码
}
// 删除按钮事件处理
void CMFCBinaryTreeDlg::OnBnClickedButtonDelete()
{
CString cstrDeleteText;
// 获取删除文本框数据
GetDlgItem(IDC_EDIT_DELETE)->GetWindowText(cstrDeleteText);
if (cstrDeleteText == "") {
return;
}
int data = _ttoi(cstrDeleteText);
// 插入
tree.DeleteAvlNode(tree.root, data);
// 绘制二叉树
//piont();
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_PREORDER, StoWs(tree.getPreOrderResult(tree.root)).c_str());
// 显示中序遍历结果
SetDlgItemText(IDC_EDIT_MIDORDER, StoWs(tree.getMidOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_POSTORDER, StoWs(tree.getPostOrderResult(tree.root)).c_str());
// 显示前序遍历结果
SetDlgItemText(IDC_EDIT_LEVELORDER, StoWs(tree.getLevelOrderResult(tree.root)).c_str());
// 清空删除文本框
SetDlgItemText(IDC_EDIT_DELETE, StoWs("").c_str());
// 绘制二叉树
// 清空绘制区域
CRect rectDlg;
int pointWidth;
int pointHeight;
GetClientRect(rectDlg); //获得窗体的大小
pointWidth = rectDlg.Width(); //获取窗体宽度
pointHeight = rectDlg.Height(); //获取窗体高度
RedrawWindow(CRect(0, 0, pointWidth, pointHeight)); //重绘指定区域
// 绘制
int depth = tree.GetHeight(tree.root);
CDC* pDC = GetDC();
paintTree(pDC, tree.root, 750, 250, 0, 0, depth);
ReleaseDC(pDC);
}
// 清除按钮事件处理
void CMFCBinaryTreeDlg::OnBnClickedButtonClear()
{
tree.root = NULL;
CRect rectDlg;
int pointWidth;
int pointHeight;
GetClientRect(rectDlg); //获得窗体的大小
pointWidth = rectDlg.Width(); //获取窗体宽度
pointHeight = rectDlg.Height(); //获取窗体高度
RedrawWindow(CRect(0, 0, pointWidth, pointHeight)); //重绘指定区域(清空)
//清空显示框
SetDlgItemText(IDC_EDIT_JUDGE_AVLTREE, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_JUDGE_CPLTREE, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_PREORDER, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_MIDORDER, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_POSTORDER, StoWs("").c_str());
SetDlgItemText(IDC_EDIT_LEVELORDER, StoWs("").c_str());
}
// 插入/删除文本框对回车键的响应:触发插入/删除按钮事件
// 对PreTranslateMessage函数的重载
BOOL CMFCBinaryTreeDlg::PreTranslateMessage(MSG* pMsg)
{
//if (WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST){
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) // 捕获按下键盘回车键事件
{
int iID = GetFocus()->GetDlgCtrlID();
if (iID == IDC_EDIT_INSERT) // 插入框
{
CMFCBinaryTreeDlg::OnBnClickedButtonInsert(); // 插入按钮事件
}
if (iID == IDC_EDIT_DELETE) // 删除框
{
CMFCBinaryTreeDlg::OnBnClickedButtonDelete(); // 删除按钮事件
}
}
// 屏蔽ESC退出程序
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
//return __super::PreTranslateMessage(pMsg);
//return TRUE;
//return CFormView::PreTranslateMessage(pMsg);
}
// 对话框确认事件(OK)处理
// 重载 BOOL CMFCBinaryTreeDlg::ONOK(void)
// 不做任何处理:只为防止按回车键时确认返回,结束对话框程序
void CMFCBinaryTreeDlg::OnOK() {}
资源获取
- csdn下载地址:MFC二叉树绘制
- 百度网盘链接: https://pan.baidu.com/s/10ONB2w04aYzl_ekJO1uUvg 提取码: n4ni