MFC二叉树可视化绘制 (C++)—— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(基于平衡二叉树实现)

界面展示:
在这里插入图片描述

相关文章目录

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

C/C++平衡二叉树实现 —— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(设计详解)

MFC 在对话框中绘制图形的方法 —— 及二叉树绘制实例

MFC 类型互转方案整理

MFC 屏蔽ESC键和ENTER键关闭对话框的方法

MFC 清除、重绘对话框上指定区域绘制的图形

MFC 设置对话框背景和边框颜色

MFC 获取控件在对话框上的坐标

LNK2019 无法解析的外部符号 “public: void __thiscall XXX…” —— 关于MFC中C++模板类报错问题

前言

该程序基于平衡二叉树实现,即默认构建为平衡二叉树,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;
        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
    在这里插入图片描述
  • 9
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
在Java,你可以使用一些库和工具来可视化二叉树。以下是一个示例,使用Swing库来实现二叉树可视化: ```java import javax.swing.*; import java.awt.*; class TreeNode { int val; TreeNode left; TreeNode right; public TreeNode(int val) { this.val = val; } } class TreePanel extends JPanel { private TreeNode root; public TreePanel(TreeNode root) { this.root = root; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); drawTree(g, getWidth() / 2, 30, root, getWidth() / 4); } private void drawTree(Graphics g, int x, int y, TreeNode node, int offsetX) { if (node == null) return; g.drawString(String.valueOf(node.val), x, y); if (node.left != null) { int childX = x - offsetX; int childY = y + 50; g.drawLine(x, y, childX, childY); drawTree(g, childX, childY, node.left, offsetX / 2); } if (node.right != null) { int childX = x + offsetX; int childY = y + 50; g.drawLine(x, y, childX, childY); drawTree(g, childX, childY, node.right, offsetX / 2); } } } public class BinaryTreeVisualization { public static void main(String[] args) { TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(3); root.left.left = new TreeNode(4); root.left.right = new TreeNode(5); root.right.left = new TreeNode(6); root.right.right = new TreeNode(7); JFrame frame = new JFrame("Binary Tree Visualization"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(800, 600); TreePanel treePanel = new TreePanel(root); frame.add(treePanel); frame.setVisible(true); } } ``` 这个示例创建了一个简单的二叉树,并使用Swing库的JPanel来实现可视化。在`paintComponent`方法,使用递归方式遍历二叉树的每个节点,并在对应的位置绘制节点值,并使用`drawLine`方法绘制节点之间的连线。最后,通过创建一个JFrame并将TreePanel添加到其,可以显示二叉树可视化结果。 你可以根据需要自定义二叉树的结构和样式,以适应你的需求。这只是一个简单的示例,可以作为起点进行扩展和定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitemeen太白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值