二叉树

 【数据机构和算法】章节中的【二叉树】,一直都觉得比较难。

  使用C++语言用类进行了封装,以便于今后学习!

 

  首先,定义了二叉树的节点类

View Code
// BinaryTreeNode.h: interface for the BinaryTreeNode class.
// 二叉树的节点 NODE
// 节点 、 左节点 、右节点
// 2011-12-13 chen ang
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_BINARYTREENODE_H__C6FD71B8_0809_43C0_B6C3_E6B7B5B032D2__INCLUDED_)
#define AFX_BINARYTREENODE_H__C6FD71B8_0809_43C0_B6C3_E6B7B5B032D2__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

template<class T>
class BinaryTreeNode
{
private:
T m_element; //节点数据域
BinaryTreeNode<T>* m_pLeft; //左节点指针
BinaryTreeNode<T>* m_pRight; //右节点指针
BinaryTreeNode<T>* m_pParent; //父节点指针:使用三叉链表存储
public:
//默认的构造函数
BinaryTreeNode()
{
memset(&m_element, 0, sizeof(T));
m_pLeft = NULL;
m_pRight = NULL;
m_pParent = NULL;
};
//析构函数
virtual ~BinaryTreeNode(){};
//传参的构造函数:二叉链表
BinaryTreeNode(const T& element, BinaryTreeNode<T>* l=NULL, BinaryTreeNode<T>* r=NULL)
{
m_element = element;
m_pLeft = l;
m_pRight = r;
m_pParent = NULL;
};
//传参的构造函数:三叉链表
BinaryTreeNode(const T& element, BinaryTreeNode<T>* p, BinaryTreeNode<T>* l=NULL, BinaryTreeNode<T>* r=NULL)
{
m_element = element;
m_pLeft = l;
m_pRight = r;
m_pParent = p;
};
//设置节点内容
void SetValue(const T& element)
{
m_element = element;
};
//设置左节点指针
void SetLeftChild(BinaryTreeNode<T>* l)
{
m_pLeft = l;
};
//设置右节点指针
void SetRightChild(BinaryTreeNode<T>* r)
{
m_pRight = r;
};
//设置父节点指针
void SetParent(BinaryTreeNode<T>* p)
{
if (NULL == m_pParent)
{
m_pParent = p;
}
};
//获取节点内容
T GetValue() const
{
return m_element;
};
//获取节点内容
void DisplayValue() const
{
cout<<GetValue()<<" ";
};
//获取左节点指针
BinaryTreeNode<T>* GetLeftChild() const
{
return m_pLeft;
};
//获取右节点指针
BinaryTreeNode<T>* GetRightChild() const
{
return m_pRight;
};
//获取父节点指针
BinaryTreeNode<T>* GetParent() const
{
return m_pParent;
};
//重载赋值函数
BinaryTreeNode<T>& operator=(const BinaryTreeNode<T>& node)
{
m_element = node.m_element;
m_pLeft = node.m_pLeft;
m_pRight = node.m_pRight;
m_pParent = node.m_pParent;
};
//判断当前节点是否为叶节点:是则返回true, 否则返回false
bool IsLeaf() const
{
if ((NULL == m_pLeft) && (NULL == m_pRight))
{
return true;
}
else
{
return false;
};
};
};

#endif // !defined(AFX_BINARYTREENODE_H__C6FD71B8_0809_43C0_B6C3_E6B7B5B032D2__INCLUDED_)


  其次:定义和实现了二叉树

View Code
// BinaryTree.h: interface for the BinaryTree class.
// 二叉树
// 2011-12-13 chen ang
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_BINARYTREE_H__E94D44AE_ADCE_4F3E_A731_699E58A76F6C__INCLUDED_)
#define AFX_BINARYTREE_H__E94D44AE_ADCE_4F3E_A731_699E58A76F6C__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <stack>
#include "BinaryTreeNode.h"

template<class T>
class BinaryTree
{
private:
BinaryTreeNode<T>* m_root; //二叉树的根节点
public:
//默认的构造函数
BinaryTree()
{
//默认为空树
m_root = NULL;
};
//传参的构造函数
BinaryTree(const T& element)
{
//只有根节点一个节点的树
m_root = new BinaryTreeNode<T>(element);
};
//析构函数
virtual ~BinaryTree(){};
//判断二叉树是否为空:空返回true; 非空返回false.
bool IsEmpty() const
{
if (NULL == m_root)
{
return true;
}
else
{
return false;
}
};
//返回二叉树的根节点
BinaryTreeNode<T>* GetRoot() const
{
return m_root;
};
//返回current结点的父结点
BinaryTreeNode<T>* GetParent(BinaryTreeNode<T>* current)
{
if ((NULL == current) || (m_root == current))
{
return NULL;
}

stack<BinaryTreeNode<T>*> s_t;
BinaryTreeNode<T>* temp = m_root;
while(!s_t.empty() || temp)
{
if (temp)
{
s_t.push(temp);
if (temp->GetLeftChild() == current || temp->GetRightChild() == current)
{
return temp;
}
temp = temp->GetLeftChild();
}
else
{
temp = s_t.top();
s_t.pop();
temp = temp->GetRightChild();
}
}

return NULL;
};
//返回current结点的左兄弟结点
BinaryTreeNode<T>* GetLeftSibling(BinaryTreeNode<T>* current)
{
if (current)
{
BinaryTreeNode<T>* temp = GetParent(current);
if ((NULL == temp) || (current == temp->GetLeftChild()))
{
return NULL;
}
else
{
return temp->GetLeftChild();
}
}
else
{
return NULL;
}
};
//返回current结点的右兄弟结点
BinaryTreeNode<T>* GetRightSibling(BinaryTreeNode<T>* current)
{
if (current)
{
BinaryTreeNode<T>* temp = GetParent(current);
if ((NULL == temp) || (current == temp->GetRightChild()))
{
return NULL;
}
else
{
return temp->GetRightChild();
}
}
else
{
return NULL;
}
};
//打印节点数据
void DisplayValue(BinaryTreeNode<T>* node)
{
if (node)
{
cout<<"值等于"<<node->GetValue()<<endl;
}
else
{
cout<<"该节点为NULL!"<<endl;
}
}
//概念: “周游”--系统地访问数据结构中的结点。每个结点都正好被访问到一次
//二叉树的遍历:前序周游二叉树或其子树
//二叉树抽象为以下共性: 所有二叉树都由【根节点或者子树的根节点】、 【左子树】、 【右子树】组成
void PreOrder(BinaryTreeNode<T>* root)
{
//方法一:深度优先周游二叉树 (使用递归)
if (NULL != root)
{
root->DisplayValue(); //访问【根节点或者子树的根节点】
PreOrder(root->GetLeftChild()); //访问【根节点或者子树的根节点】的左节点
PreOrder(root->GetRightChild()); //访问【根节点或者子树的根节点】的右节点
}
};
void PreOrderInStack(BinaryTreeNode<T>* root)
{
//方法二:非递归深度优先周游二叉树
// 栈是实现递归的最常用的结构
// 利用一个栈来记下尚待周游的结点或子树,以备以后访问。
stack<BinaryTreeNode<T>*> s_t;
BinaryTreeNode<T>* temp = root;
while (!s_t.empty() || temp)
{
if (temp)
{
temp->DisplayValue(); //访问【根节点或者子树的根节点】
s_t.push(temp); // 【根节点或者子树的根节点】入栈
temp = temp->GetLeftChild(); //转到【根节点或者子树的根节点】的左节点
}
else
{
temp = s_t.top(); //回到【根节点或者子树的根节点】
s_t.pop(); // 【根节点或者子树的根节点】弹出栈
temp = temp->GetRightChild(); //转到【根节点或者子树的根节点】的右节点
}
}
}
void PreOrderInStack1(BinaryTreeNode<T>* root)
{
//方法二:非递归深度优先周游二叉树
// 栈是实现递归的最常用的结构
// 利用一个栈来记下尚待周游的结点或子树,以备以后访问。
stack<BinaryTreeNode<T>*> s_t;
s_t.push(NULL);

BinaryTreeNode<T>* temp = root;
while(temp)
{
temp->DisplayValue();
if (temp->GetRightChild())
{
s_t.push(temp->GetRightChild());
}
if (temp->GetLeftChild())
{
temp = temp->GetLeftChild();
}
else
{
temp = s_t.top();
s_t.pop();
}
}
}

//二叉树的遍历:中序周游二叉树或其子树
void InOrder(BinaryTreeNode<T>* root)
{
//方法一:深度优先周游二叉树 (使用递归)
if (NULL != root)
{
InOrder(root->GetLeftChild()); //访问【根节点或者子树的根节点】的左节点
root->DisplayValue(); //访问【根节点或者子树的根节点】
InOrder(root->GetRightChild()); //访问【根节点或者子树的根节点】的右节点
}
};
void InOrderInStack(BinaryTreeNode<T>* root)
{
//方法二:非递归深度优先周游二叉树
// 栈是实现递归的最常用的结构
// 利用一个栈来记下尚待周游的结点或子树,以备以后访问。
stack<BinaryTreeNode<T>*> s_t;
BinaryTreeNode<T>* temp = root;
while (!s_t.empty() || temp)
{
if (temp)
{
s_t.push(temp); // 【根节点或者子树的根节点】入栈
temp = temp->GetLeftChild(); //转到【根节点或者子树的根节点】的左节点
}
else
{
temp = s_t.top(); //回到【根节点或者子树的根节点】
s_t.pop(); // 【根节点或者子树的根节点】弹出栈
temp->DisplayValue(); //访问【根节点或者子树的根节点】的左节点
temp = temp->GetRightChild(); //转到【根节点或者子树的根节点】的右节点
}
}
}

//二叉树的遍历:后序周游二叉树或其子树
void PostOrder(BinaryTreeNode<T>* root)
{
//方法一:深度优先周游二叉树 (使用递归)
if (NULL != root)
{
PostOrder(root->GetLeftChild()); //访问【根节点或者子树的根节点】的左节点
PostOrder(root->GetRightChild()); //访问【根节点或者子树的根节点】的右节点
root->DisplayValue(); //访问【根节点或者子树的根节点】
}
};
void PostOrderInStack(BinaryTreeNode<T>* root)
{
//方法二:非递归深度优先周游二叉树
// 栈是实现递归的最常用的结构
// 利用一个栈来记下尚待周游的结点或子树,以备以后访问。
cout<<"NULL"<<endl;
}

//二叉树的遍历:按层次周游二叉树或其子树
void LevelOrder(BinaryTreeNode<T>* root)
{
// 使用队列
deque<BinaryTreeNode<T>*> d_t;
BinaryTreeNode<T>* temp;

if (NULL != root)
{
d_t.push_back(root); // 【根节点或者子树的根节点】入队列尾部
}

while(!d_t.empty())
{
temp = d_t.front(); // 【根节点或者子树的根节点】
d_t.pop_front(); // 【根节点或者子树的根节点】出队列前端
temp->DisplayValue(); //访问【根节点或者子树的根节点】

if (temp->GetLeftChild())
{
d_t.push_back(temp->GetLeftChild());// 【根节点或者子树的根节点】的左节点入队列
}

if (temp->GetRightChild())
{
d_t.push_back(temp->GetRightChild());// 【根节点或者子树的根节点】的右节点入队列
}
} // end while
};

//删除二叉树或其子树
void DeleteBinaryTree(BinaryTreeNode<T>* root)
{
//后序周游二叉树或其子树
if (root)
{
DeleteBinaryTree(root->GetLeftChild()); //递归删除二叉树的左子树
DeleteBinaryTree(root->GetRightChild()); //递归删除二叉树的右子树
delete root; //删除根节点
root = NULL;
}
};
};

#endif // !defined(AFX_BINARYTREE_H__E94D44AE_ADCE_4F3E_A731_699E58A76F6C__INCLUDED_)


  最后,就是对二叉树进行数据录入,测试。

// 二叉树.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>

#include "BinaryTree.h"

using namespace std;

int main(int argc, char* argv[])
{
//创建一个二叉树
BinaryTree<int> bTree(0);

//判断是否为空
if (bTree.IsEmpty())
{
cout<<"binary tree is empty"<<endl;
}
else
{
cout<<"binary tree is not empty"<<endl;
}

//二叉树中插入节点
BinaryTreeNode<int>* pNode;
//新建节点
pNode = new BinaryTreeNode<int>(1);
bTree.GetRoot()->SetLeftChild(pNode);
pNode = new BinaryTreeNode<int>(2);
bTree.GetRoot()->SetRightChild(pNode);
pNode = new BinaryTreeNode<int>(3);
(bTree.GetRoot()->GetLeftChild())->SetLeftChild(pNode);
pNode = new BinaryTreeNode<int>(4);
(bTree.GetRoot()->GetLeftChild())->SetRightChild(pNode);
pNode = new BinaryTreeNode<int>(5);
(bTree.GetRoot()->GetRightChild())->SetLeftChild(pNode);
pNode = new BinaryTreeNode<int>(6);
(bTree.GetRoot()->GetRightChild())->SetRightChild(pNode);
//先序遍历
cout<<endl<<"先序遍历:";
bTree.PreOrder(bTree.GetRoot());
cout<<endl<<"先序遍历(栈):";
bTree.PreOrderInStack(bTree.GetRoot());
//中序遍历
cout<<endl<<"中序遍历:";
bTree.InOrder(bTree.GetRoot());
cout<<endl<<"中序遍历(栈):";
bTree.InOrderInStack(bTree.GetRoot());
//后序遍历
cout<<endl<<"后序遍历:";
bTree.PostOrder(bTree.GetRoot());

//层次遍历
cout<<endl<<"层次遍历:";
bTree.LevelOrder(bTree.GetRoot());
//获取父节点
cout<<endl<<"获取父节点:";
bTree.DisplayValue(bTree.GetParent(pNode));
cout<<"获取左节点:";
bTree.DisplayValue(bTree.GetLeftSibling(pNode));
cout<<"获取右节点:";
bTree.DisplayValue(bTree.GetRightSibling(pNode));
//删除二叉树
bTree.DeleteBinaryTree(bTree.GetRoot());
cout<<endl;
return 0;
}


  总结:测试结果,输出数据,截图。

  

摘要: 对于MFC中所提到的基本容器类,做一个基本的介绍和编程操作。 如有数据结构基础,可以直接看第四部分对容器类的操作。 第一,MFC提供三种基本的容器类:arrays(数组),lists(链表), maps(映射,也称作字典). 第二,各容器类的特征分类类型排序?索引?插入元素查找特定元素重复的元素?ListYesNoFastSlowYesArrayYesBy intSlowSlowYesMapNoBy keyFastFastNo (keys)Yes (values) 第三,模板容器类和非模板容器类 模板容器类:Collection contentsArraysListsMaps任...阅读全文
posted @ 2011-11-16 16:36 陈昂 阅读(16) | 评论 (0) 编辑
 
摘要: 展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告。 走过的路,回忆起来是那么曲折,把自己的一些心得体会分享给程序员兄弟姐妹们,虽然时代在变化,但是很可能你也会走我已经做过的10年的路程,有些心得体会你可以借鉴一下,觉得说得有道理的你就接纳,觉得说得没道理的,你就抛弃,以下是我发自内心的,给大家的忠告,特别是针对那些小弟弟妹妹们。 01. 自己的户口档案、养老保险、医疗保险、住房公积金一定要保管好。 由于程序员行业每年跳槽一次,我不隐瞒大家,我至少换过5个以上的单位,这期间跳来跳去,甚至是城市都换过3个。还好户口没丢掉,其他都已经是乱了,好几个城市里,都有交...阅读全文
posted @ 2011-11-07 03:03 陈昂 阅读(792) | 评论 (20) 编辑
 
摘要: 今天对WINDOWS的HOOK技术进行了应用:主要包括以下:1,HOOK技术原理的了解;2,HOOK技术中的常见钩子应用:键盘钩子,鼠标钩子,消息钩子3,钩子DLL在调用程序EXE中的调试。首先,HOOK技术中的几个常用函数:View Code HHOOK SetWindowsHookEx( int idHook, // type of hook to install HOOKPROC lpfn, // address of hook procedure HINSTANCE hMod, // handle to application instance ...阅读全文
posted @ 2011-11-04 18:46 陈昂 阅读(1186) | 评论 (3) 编辑
 
摘要: 这篇文章主要是介绍非客户区的自绘,目前只用了对话框功能。该程序可以实现最大化,最小化,最大化恢复,绘制的效果,如下///////////////////////////////////////////////////////////////////////////以下为源代码: 第一部分,头文件// 对话框非客户区自绘Dlg.h : header file//#if !defined(AFX_DLG_H__29895C86_9D01_4C17_B374_289E36621F88__INCLUDED_)#define AFX_DLG_H__29895C86_9D01_4C17_B374_289E阅读全文
posted @ 2011-11-03 13:53 陈昂 阅读(99) | 评论 (0) 编辑
 
摘要: 博客园喜欢你的原因,只因为你是我们程序员的家园!喜欢你的原因,只因为你的博客,可以更方便贴代码,看代码!喜欢你的原因,只因为你的功能比较全,符合我们程序员的习惯!喜欢你的原因,只因为你在这里--博客园~阅读全文
posted @ 2011-11-03 13:44 陈昂 阅读(5) | 评论 (0) 编辑
原文地址:https://www.cnblogs.com/Leo_wl/p/2289462.html