415 lines
14 KiB
MQL5
415 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Tree.mqh |
|
|
//| Copyright 2000-2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#include "TreeNode.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Class CTree. |
|
|
//| Purpose: Building a binary tree of instances of CTreeNode class |
|
|
//| and its derviatives. |
|
|
//| Derives from class CTreeNode. |
|
|
//+------------------------------------------------------------------+
|
|
class CTree : public CTreeNode
|
|
{
|
|
protected:
|
|
CTreeNode *m_root_node; // root node of the tree
|
|
|
|
public:
|
|
CTree(void);
|
|
~CTree(void);
|
|
//--- methods of access to protected data
|
|
CTreeNode *Root(void) const { return(m_root_node); }
|
|
//--- method of identifying the object
|
|
virtual int Type() const { return(0x9999); }
|
|
//--- method of filling the tree
|
|
CTreeNode *Insert(CTreeNode *new_node);
|
|
//--- methods of removing tree nodes
|
|
bool Detach(CTreeNode *node);
|
|
bool Delete(CTreeNode *node);
|
|
void Clear(void);
|
|
//--- method of searching data in the tree
|
|
CTreeNode *Find(const CTreeNode *node);
|
|
//--- method to create elements in the tree
|
|
virtual CTreeNode *CreateElement() { return(NULL); }
|
|
//--- methods for working with files
|
|
virtual bool Save(const int file_handle);
|
|
virtual bool Load(const int file_handle);
|
|
|
|
protected:
|
|
void Balance(CTreeNode *node);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CTree::CTree(void) : m_root_node(NULL)
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTree::~CTree(void)
|
|
{
|
|
Clear();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method of adding a node to the tree |
|
|
//+------------------------------------------------------------------+
|
|
CTreeNode *CTree::Insert(CTreeNode *new_node)
|
|
{
|
|
CTreeNode *p_node;
|
|
CTreeNode *result=m_root_node;
|
|
//--- check
|
|
if(!CheckPointer(new_node))
|
|
return(NULL);
|
|
//--- add
|
|
if(result!=NULL)
|
|
{
|
|
p_node=NULL;
|
|
result=m_root_node;
|
|
while(result!=NULL && result.Compare(new_node)!=0)
|
|
{
|
|
p_node=result;
|
|
result=result.GetNext(new_node);
|
|
}
|
|
if(result!=NULL)
|
|
return(result);
|
|
if(p_node.Compare(new_node)>0)
|
|
p_node.Left(new_node);
|
|
else
|
|
p_node.Right(new_node);
|
|
new_node.Parent(p_node);
|
|
Balance(p_node);
|
|
}
|
|
else
|
|
m_root_node=new_node;
|
|
//--- result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method of removing a node from the tree |
|
|
//+------------------------------------------------------------------+
|
|
bool CTree::Delete(CTreeNode *node)
|
|
{
|
|
//--- check
|
|
if(!CheckPointer(node))
|
|
return(false);
|
|
//--- delete
|
|
if(Detach(node) && CheckPointer(node)==POINTER_DYNAMIC)
|
|
delete node;
|
|
//--- successful
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method of detaching node from the tree |
|
|
//+------------------------------------------------------------------+
|
|
bool CTree::Detach(CTreeNode *node)
|
|
{
|
|
CTreeNode *curr_node,*tmp_node;
|
|
CTreeNode *nodeA,*nodeB;
|
|
//--- check
|
|
curr_node=node;
|
|
if(!CheckPointer(curr_node))
|
|
return(false);
|
|
//--- detach
|
|
if(curr_node.BalanceL()>curr_node.BalanceR())
|
|
{
|
|
nodeA=curr_node.Left();
|
|
while(nodeA.Right()!=NULL)
|
|
nodeA=nodeA.Right();
|
|
nodeB=nodeA.Parent();
|
|
if(nodeB!=curr_node)
|
|
{
|
|
nodeB.Right(nodeA.Left());
|
|
tmp_node=nodeB.Right();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeB);
|
|
tmp_node=curr_node.Left();
|
|
nodeA.Left(tmp_node);
|
|
tmp_node.Parent(nodeA);
|
|
}
|
|
//--- left link of curr_node is already installed as it should be
|
|
curr_node.Left(NULL);
|
|
//--- transferring the right link of curr_node to nodeA
|
|
nodeA.Right(curr_node.Right());
|
|
tmp_node=curr_node.Right();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeA);
|
|
curr_node.Right(NULL);
|
|
//--- transferring the root link of curr_node to nodeA
|
|
tmp_node=curr_node.Parent();
|
|
nodeA.Parent(tmp_node);
|
|
if(tmp_node!=NULL)
|
|
{
|
|
if(tmp_node.Left()==curr_node)
|
|
tmp_node.Left(nodeA);
|
|
else
|
|
tmp_node.Right(nodeA);
|
|
}
|
|
else
|
|
{
|
|
curr_node.Parent(NULL);
|
|
m_root_node=nodeA;
|
|
tmp_node=nodeA;
|
|
}
|
|
Balance(tmp_node);
|
|
}
|
|
else
|
|
{
|
|
if(curr_node.BalanceR()>0)
|
|
{
|
|
nodeA=curr_node.Right();
|
|
while(nodeA.Left()!=NULL)
|
|
nodeA=nodeA.Left();
|
|
nodeB=nodeA.Parent();
|
|
if(nodeB!=curr_node)
|
|
{
|
|
nodeB.Left(nodeA.Right());
|
|
tmp_node=nodeB.Left();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeB);
|
|
tmp_node=curr_node.Right();
|
|
nodeA.Right(tmp_node);
|
|
tmp_node.Parent(nodeA);
|
|
}
|
|
//--- right link of curr_node is already installed as it should be
|
|
curr_node.Right(NULL);
|
|
//--- transferring the left link of curr_node to nodeA
|
|
nodeA.Left(curr_node.Left());
|
|
tmp_node=curr_node.Left();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeA);
|
|
curr_node.Left(NULL);
|
|
//--- transferring the root link of curr_node to nodeA
|
|
tmp_node=curr_node.Parent();
|
|
nodeA.Parent(tmp_node);
|
|
if(tmp_node!=NULL)
|
|
{
|
|
if(tmp_node.Left()==curr_node)
|
|
tmp_node.Left(nodeA);
|
|
else
|
|
tmp_node.Right(nodeA);
|
|
}
|
|
else
|
|
{
|
|
curr_node.Parent(NULL);
|
|
m_root_node=nodeA;
|
|
tmp_node=nodeA;
|
|
}
|
|
Balance(tmp_node);
|
|
}
|
|
else
|
|
{
|
|
//--- node list
|
|
if(curr_node.Parent()==NULL)
|
|
m_root_node=NULL;
|
|
else
|
|
{
|
|
tmp_node=curr_node.Parent();
|
|
if(tmp_node.Left()==curr_node)
|
|
tmp_node.Left(NULL);
|
|
else
|
|
tmp_node.Right(NULL);
|
|
curr_node.Parent(NULL);
|
|
}
|
|
Balance(curr_node.Parent());
|
|
}
|
|
}
|
|
//--- successful
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method of cleaning the tree |
|
|
//+------------------------------------------------------------------+
|
|
void CTree::Clear(void)
|
|
{
|
|
if(CheckPointer(m_root_node)==POINTER_DYNAMIC)
|
|
delete m_root_node;
|
|
m_root_node=NULL;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method of searching for a node in the tree |
|
|
//+------------------------------------------------------------------+
|
|
CTreeNode *CTree::Find(const CTreeNode *node)
|
|
{
|
|
CTreeNode *result=m_root_node;
|
|
//--- find
|
|
while(result!=NULL && result.Compare(node)!=0)
|
|
result=result.GetNext(node);
|
|
//--- result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Method of balancing the tree |
|
|
//+------------------------------------------------------------------+
|
|
void CTree::Balance(CTreeNode *node)
|
|
{
|
|
CTreeNode *nodeA,*nodeB,*nodeC,*curr_node,*tmp_node;
|
|
//---
|
|
curr_node=node;
|
|
while(curr_node!=NULL)
|
|
{
|
|
curr_node.RefreshBalance();
|
|
if(MathAbs(curr_node.BalanceL()-curr_node.BalanceR())<=1)
|
|
curr_node=curr_node.Parent();
|
|
else
|
|
{
|
|
if(curr_node.BalanceR()>curr_node.BalanceL())
|
|
{
|
|
//--- rotation to the right
|
|
tmp_node=curr_node.Right();
|
|
if(tmp_node.BalanceL()>tmp_node.BalanceR())
|
|
{
|
|
//--- great rotation to the right
|
|
nodeA=curr_node;
|
|
nodeB=nodeA.Right();
|
|
nodeC=nodeB.Left();
|
|
nodeC.Parent(nodeA.Parent());
|
|
tmp_node=nodeC.Parent();
|
|
if(tmp_node!=NULL)
|
|
{
|
|
if(tmp_node.Right()==nodeA)
|
|
tmp_node.Right(nodeC);
|
|
else
|
|
tmp_node.Left(nodeC);
|
|
}
|
|
else
|
|
m_root_node=nodeC;
|
|
nodeA.Parent(nodeC);
|
|
nodeB.Parent(nodeC);
|
|
nodeA.Right(nodeC.Left());
|
|
tmp_node=nodeA.Right();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeA);
|
|
nodeC.Left(nodeA);
|
|
nodeB.Left(nodeC.Right());
|
|
tmp_node=nodeB.Left();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeB);
|
|
nodeC.Right(nodeB);
|
|
if(m_root_node==nodeA)
|
|
m_root_node=nodeC;
|
|
curr_node=nodeC.Parent();
|
|
}
|
|
else
|
|
{
|
|
//--- slight rotation to the right
|
|
nodeA=curr_node;
|
|
nodeB=nodeA.Right();
|
|
nodeB.Parent(nodeA.Parent());
|
|
tmp_node=nodeB.Parent();
|
|
if(tmp_node!=NULL)
|
|
{
|
|
if(tmp_node.Right()==nodeA)
|
|
tmp_node.Right(nodeB);
|
|
else
|
|
tmp_node.Left(nodeB);
|
|
}
|
|
else
|
|
m_root_node=nodeB;
|
|
nodeA.Parent(nodeB);
|
|
nodeA.Right(nodeB.Left());
|
|
tmp_node=nodeA.Right();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeA);
|
|
nodeB.Left(nodeA);
|
|
if(m_root_node==nodeA)
|
|
m_root_node=nodeB;
|
|
curr_node=nodeB.Parent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- rotation to the left
|
|
tmp_node=curr_node.Left();
|
|
if(tmp_node.BalanceR()>tmp_node.BalanceL())
|
|
{
|
|
//--- great rotation to the left
|
|
nodeA=curr_node;
|
|
nodeB=nodeA.Left();
|
|
nodeC=nodeB.Right();
|
|
nodeC.Parent(nodeA.Parent());
|
|
tmp_node=nodeC.Parent();
|
|
if(tmp_node!=NULL)
|
|
{
|
|
if(tmp_node.Right()==nodeA)
|
|
tmp_node.Right(nodeC);
|
|
else
|
|
tmp_node.Left(nodeC);
|
|
}
|
|
else
|
|
m_root_node=nodeC;
|
|
nodeA.Parent(nodeC);
|
|
nodeB.Parent(nodeC);
|
|
nodeA.Left(nodeC.Right());
|
|
tmp_node=nodeA.Left();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeA);
|
|
nodeC.Right(nodeA);
|
|
nodeB.Right(nodeC.Left());
|
|
tmp_node=nodeB.Right();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeB);
|
|
nodeC.Left(nodeB);
|
|
if(m_root_node==nodeA)
|
|
m_root_node=nodeC;
|
|
curr_node=nodeC.Parent();
|
|
}
|
|
else
|
|
{
|
|
//--- small rotation to the left
|
|
nodeA=curr_node;
|
|
nodeB=nodeA.Left();
|
|
nodeB.Parent(nodeA.Parent());
|
|
tmp_node=nodeB.Parent();
|
|
if(tmp_node!=NULL)
|
|
{
|
|
if(tmp_node.Right()==nodeA)
|
|
tmp_node.Right(nodeB);
|
|
else
|
|
tmp_node.Left(nodeB);
|
|
}
|
|
else
|
|
m_root_node=nodeB;
|
|
nodeA.Parent(nodeB);
|
|
nodeA.Left(nodeB.Right());
|
|
tmp_node=nodeA.Left();
|
|
if(tmp_node!=NULL)
|
|
tmp_node.Parent(nodeA);
|
|
nodeB.Right(nodeA);
|
|
if(m_root_node==nodeA)
|
|
m_root_node=nodeB;
|
|
curr_node=nodeB.Parent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Writing tree to file |
|
|
//+------------------------------------------------------------------+
|
|
bool CTree::Save(const int file_handle)
|
|
{
|
|
//--- check
|
|
if(file_handle==INVALID_HANDLE)
|
|
return(false);
|
|
if(m_root_node==NULL)
|
|
return(true);
|
|
//--- result
|
|
return(m_root_node.SaveNode(file_handle));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reading tree from file |
|
|
//+------------------------------------------------------------------+
|
|
bool CTree::Load(const int file_handle)
|
|
{
|
|
//--- check
|
|
if(file_handle==INVALID_HANDLE)
|
|
return(false);
|
|
//--- create root node only
|
|
Clear();
|
|
Insert(CreateElement());
|
|
//--- result
|
|
return(m_root_node.LoadNode(file_handle,m_root_node));
|
|
}
|
|
//+------------------------------------------------------------------+
|