MFC二叉树可视化绘制 (C++)—— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(基于平衡二叉树实现)
界面展示:
相关文章目录
平衡二叉树的构造过程图解
C/C++平衡二叉树实现 —— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(设计详解)
MFC 在对话框中绘制图形的方法 —— 及二叉树绘制实例
MFC 类型互转方案整理
MFC 屏蔽ESC键和ENTER键关闭对话框的方法
MFC 清除、重绘对话框上指定区域绘制的图形
MFC 设置对话框背景和边框颜色
MFC 获取控件在对话框上的坐标
LNK2019 无法解析的外部符号 “public: void __thiscall XXX…” —— 关于MFC中C++模板类报错问题
文章目录
- 相关文章目录
- 前言
- 一、实现功能
- 二、代码设计
- 1. 平衡二叉树类定义
- (1)定义平衡二叉树的结点结构
- (2)定义层序遍历链队列相关结构和函数
- (3)获取最大值、最小值结点,获取树的高度,查找结点,完全二叉树判断
- (4)定义结点旋转函数 —— 用于平衡二叉树的结点平衡
- (5)定义结点插入函数
- (6)定义结点删除函数
- (7)定义二叉树遍历与输出函数
- 2. MFC窗口界面函数(窗口绘制、事件处理)
- (1)全局类对象定义
- (2)平衡二叉树绘制函数定义
- (3)类型转换函数定义
- (4)插入按钮事件处理
- (5)删除按钮事件处理
- (6)清除按钮事件处理
- (7)防止回车键确认返回,结束对话框程序
- (8)增加插入/删除文本框对回车键的响应、屏蔽ESC退出程序
- 三、运行结果
- 四、完整代码
- MFC_BinaryTreeDlg.cpp: 实现文件
- MFC_BinaryTreeDlg.h: 头文件
- MFC_BinaryTreeDlg.cpp: 实现文件
- `资源获取`
前言
该程序基于平衡二叉树实现,即默认构建为平衡二叉树,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;elsereturn 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);elsereturn 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);elset = 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 0using 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;elsereturn 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);elset = 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);elsereturn 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
MFC二叉树可视化绘制 (C++)—— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(基于平衡二叉树实现)相关推荐
- 二叉树前序遍历python输出_[宜配屋]听图阁 - Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例...
本文实例讲述了Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作.分享给大家供大家参考,具体如下: 实现一个功能: 输入:一颗二叉树的先序和中序遍历 输出:后续遍历 思想: 先序遍历中,第 ...
- 二叉树前序中序后续线索树_后序线索二叉树怎么画 线索二叉树基本操作详解 - 办公软件 - 服务器之家...
后序线索二叉树怎么画 线索二叉树基本操作详解 发布时间:2017-05-23 来源:服务器之家 遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序,中序或后序序列.这实际上 ...
- 给定二叉树先序、中序遍历序列,求后序遍历
给定一个二叉树的前序遍历和中序遍历的序列,输出对应这个二叉树的后续遍历序列. 输入描述: 输入为一行. 两个字符串,分别表示二叉树的前序遍历和中序遍历结果,用空格分隔.保证数据合法 输出描述: 对应输 ...
- 二叉树前序中序后序_leetcode889_go_根据前序和后序遍历构造二叉树
leetcode889_根据前序和后序遍历构造二叉树 01 - 题目 返回与给定的前序和后序遍历匹配的任何二叉树. pre 和 post 遍历中的值是不同的正整数. 示例:输入:pre = [1,2, ...
- 已知二叉树先序和中序遍历结果,求后序遍历结果
以下面的例题为例进行讲解:已知一棵二叉树的先序遍历序列和中序遍历序列分别是ABDCEF.BDAECF,求二叉树及后序遍历序列. 分析:先序遍历序列的第一个字符为根结点.对于中序遍历,根结点在中序遍历序 ...
- 二叉树的进阶操作---(求二叉树中所有结点个数,求叶子结点个数,求第k层结点个数;在二叉树中查找某一结点;层序遍历;判断是否为完全二叉树)
typedef struct TreeNode {struct TreeNode *left;struct TreeNode *right;char val; }TreeNode;typedef st ...
- 我刚做的一个TreeView的CheckBox进行选中插入数据库,从数据库中读取数据后让CheckBox勾选的代码!...
#region 绑定角色 /// <summary> /// 绑定权限信息 /// </summary> protected void Bind ...
- 二十五、二叉树的前序、中序、后序遍历
一.为何使用树这种数据结构 数组存储方式的分析 优点:通过下标方式访问元素,速度快.对于有序数组,还可使用二分查找提高检索速度. 缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较 ...
- 二叉树的相关性质及其前中后层序遍历实现
写在前面: 本文前面部分介绍相关概念,后面部分是程序. 点击下面链接查看更多! 点击此处发现更多 一.二叉树的概念 1.1 相关术语 ①结点:包含一个数据元素及若干指向子树分支的信息 . ②结点的度: ...
最新文章
- 基于python和OpenCV构建智能停车系统
- ICML2020 | 基于贝叶斯元学习在关系图上进行小样本关系抽取
- 二进制补码求值用c语言,C语言程序设计第2章数据类型.运算符与表达式.ppt
- springboot 实现微信小程序授权并解密手机号
- python之csv模块(part1)--写入csv文件
- tcga癌症亚型获取_亚型多态性应用于元组的危险
- snmp有android代理端吗,Android实现snmp协议(1)
- 陈天桥:为何总是半夜惊醒?《前程密码》
- ipv4转ipv6 在线工具_TOOLFK在线工具-在线文字转语音/语音合成mp3工具
- 输出100-1000之间的水仙花数 是三位数 水仙花数就是 每个位上的数字的三次方的和仍然为原数字 例如:153是一个“水仙花数“,因为153=1的三次方+5的三次方+3的三次方;
- 盘点一款Python发包收包利器——scapy
- 用HTML5做一个个人网站,此文仅展示个人主页界面。内附源代码下载地址
- matlab cplex 死机,matlab Cplex使用
- 网站被黑跳转到其他网站的解决办法
- 蚁群算法原理介绍,算法框架以及代码实现
- 服务器修复工具,Microsoft .NET Framework 修复工具可用
- web逻辑思维题目_Java Web面试题整理(思维导图)
- 当红小生酒店施暴性感女星
- 数字图像处理——红眼去除(Python)
- SSM+基于Vue框架的在线投票系统的设计与实现 毕业设计-附源码221604