界面展示:

相关文章目录

平衡二叉树的构造过程图解

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. 平衡二叉树的创建、绘制。
  2. 实现操作:插入结点、删除结点、清空二叉树。
  3. 实现输出:输出先序遍历、中序遍历、后序遍历、层序遍历的结果。

二、代码设计

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++)—— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(基于平衡二叉树实现)相关推荐

  1. 二叉树前序遍历python输出_[宜配屋]听图阁 - Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例...

    本文实例讲述了Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作.分享给大家供大家参考,具体如下: 实现一个功能: 输入:一颗二叉树的先序和中序遍历 输出:后续遍历 思想: 先序遍历中,第 ...

  2. 二叉树前序中序后续线索树_后序线索二叉树怎么画 线索二叉树基本操作详解 - 办公软件 - 服务器之家...

    后序线索二叉树怎么画 线索二叉树基本操作详解 发布时间:2017-05-23 来源:服务器之家 遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序,中序或后序序列.这实际上 ...

  3. 给定二叉树先序、中序遍历序列,求后序遍历

    给定一个二叉树的前序遍历和中序遍历的序列,输出对应这个二叉树的后续遍历序列. 输入描述: 输入为一行. 两个字符串,分别表示二叉树的前序遍历和中序遍历结果,用空格分隔.保证数据合法 输出描述: 对应输 ...

  4. 二叉树前序中序后序_leetcode889_go_根据前序和后序遍历构造二叉树

    leetcode889_根据前序和后序遍历构造二叉树 01 - 题目 返回与给定的前序和后序遍历匹配的任何二叉树. pre 和 post 遍历中的值是不同的正整数. 示例:输入:pre = [1,2, ...

  5. 已知二叉树先序和中序遍历结果,求后序遍历结果

    以下面的例题为例进行讲解:已知一棵二叉树的先序遍历序列和中序遍历序列分别是ABDCEF.BDAECF,求二叉树及后序遍历序列. 分析:先序遍历序列的第一个字符为根结点.对于中序遍历,根结点在中序遍历序 ...

  6. 二叉树的进阶操作---(求二叉树中所有结点个数,求叶子结点个数,求第k层结点个数;在二叉树中查找某一结点;层序遍历;判断是否为完全二叉树)

    typedef struct TreeNode {struct TreeNode *left;struct TreeNode *right;char val; }TreeNode;typedef st ...

  7. 我刚做的一个TreeView的CheckBox进行选中插入数据库,从数据库中读取数据后让CheckBox勾选的代码!...

    #region 绑定角色     /// <summary>     /// 绑定权限信息     /// </summary>     protected void Bind ...

  8. 二十五、二叉树的前序、中序、后序遍历

    一.为何使用树这种数据结构 数组存储方式的分析 优点:通过下标方式访问元素,速度快.对于有序数组,还可使用二分查找提高检索速度. 缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较 ...

  9. 二叉树的相关性质及其前中后层序遍历实现

    写在前面: 本文前面部分介绍相关概念,后面部分是程序. 点击下面链接查看更多! 点击此处发现更多 一.二叉树的概念 1.1 相关术语 ①结点:包含一个数据元素及若干指向子树分支的信息 . ②结点的度: ...

最新文章

  1. 基于python和OpenCV构建智能停车系统
  2. ICML2020 | 基于贝叶斯元学习在关系图上进行小样本关系抽取
  3. 二进制补码求值用c语言,C语言程序设计第2章数据类型.运算符与表达式.ppt
  4. springboot 实现微信小程序授权并解密手机号
  5. python之csv模块(part1)--写入csv文件
  6. tcga癌症亚型获取_亚型多态性应用于元组的危险
  7. snmp有android代理端吗,Android实现snmp协议(1)
  8. 陈天桥:为何总是半夜惊醒?《前程密码》
  9. ipv4转ipv6 在线工具_TOOLFK在线工具-在线文字转语音/语音合成mp3工具
  10. 输出100-1000之间的水仙花数 是三位数 水仙花数就是 每个位上的数字的三次方的和仍然为原数字 例如:153是一个“水仙花数“,因为153=1的三次方+5的三次方+3的三次方;
  11. 盘点一款Python发包收包利器——scapy
  12. 用HTML5做一个个人网站,此文仅展示个人主页界面。内附源代码下载地址
  13. matlab cplex 死机,matlab Cplex使用
  14. 网站被黑跳转到其他网站的解决办法
  15. 蚁群算法原理介绍,算法框架以及代码实现
  16. 服务器修复工具,Microsoft .NET Framework 修复工具可用
  17. web逻辑思维题目_Java Web面试题整理(思维导图)
  18. 当红小生酒店施暴性感女星
  19. 数字图像处理——红眼去除(Python)
  20. SSM+基于Vue框架的在线投票系统的设计与实现 毕业设计-附源码221604

热门文章

  1. OpenStack部署之Placement项目(7-4)
  2. 超详细Openstack核心组件——Placement部署
  3. 视频怎么压缩变小?视频压缩变小的具体操作步骤
  4. 【UEFI实战】HII之配置
  5. 【报告分享】2021天猫坚果消费趋势报告-CBNData(附下载)
  6. 完整的十字架(漫画)
  7. Java兔子生兔子问题
  8. Windows程序设计(中文第五版·珍藏版).mobi 电子书
  9. Android 调用相机拍照并保存
  10. 锐龙r9 5900hx相当于英特尔的什么 r9 5900hx属于什么档次