Click here to Skip to main content
15,894,343 members
Articles / Desktop Programming / MFC

Ultimate FastMaps!

Rate me:
Please Sign up or sign in to vote.
3.73/5 (7 votes)
19 Jan 2000 153.4K   1.1K   35  
A fully featured map class that uses balanced trees to store and retrieve data quickly by key
// Copyright (c) 1998 Dundas Software Ltd., ALL RIGHTS RESERVED
//
// tbtree.cpp : implementation file
//

#include "tbtree.h"                     // class header

//==========================================================================
// tBalTree class: Abstract base class for balanced binary trees.
//==========================================================================

//--------------------------------------------------------------------------
// Construction
tBalTree::tBalTree()
{
    m_imbalance = 15;                   // empirically determined default
    m_root = 0;                         // tree initially empty
    ResetStatistics();                  // no-op in debug build
}

//--------------------------------------------------------------------------
// Destruction.  NOTE: All derived classes MUST call RemoveAll() in their
// destructor in order for dynamically allocated storage to be freed.
tBalTree::~tBalTree()
{
    ASSERT(!m_root);    // derived class did not call RemoveAll() in dtor
}

//--------------------------------------------------------------------------
// Empties the tree.  NOTE: All derived classes MUST call this member in
// their destructor in order for dynamically allocated storage to be freed.
void tBalTree::RemoveAll(void)
{
    if (!m_root)
        return;
	tBalTreeNode* temp;
	tBalTreeNode* cur = m_root;
	m_root = 0;
	while (cur)
	{
		// find a leaf.
		while (cur->m_leftSubtree || cur->m_rightSubtree)
		{
			if (cur->m_leftSubtree)
				cur = cur->m_leftSubtree;
			else
				cur = cur->m_rightSubtree;
		}
		// keep leaf pointer, move to parent, reset parent's leaf-ptr.
		temp = cur;
		cur = cur->m_parent;
		if (cur)
		{
			if (cur->m_leftSubtree == temp)
				cur->m_leftSubtree = 0;
			else
				cur->m_rightSubtree = 0;
		}
		// delete the disconnected leaf.
        if (temp->m_nodeKey)
            onDeleteKey((void*&)temp->m_nodeKey);
        if (temp->m_nodeData)
            onDeleteData((void*&)temp->m_nodeData);
		free(temp);
	}
}

//--------------------------------------------------------------------------
// Sets the maximum level of imbalance allowed before an autobalance
// operation is performed.  Returns the previous setting.  To disable
// autobalance, pass 0.
long tBalTree::SetAutoBalance(long maxImbalance)
{
    long oldValue = m_imbalance;
    if (oldValue == LONG_MAX)
        oldValue = 0;
    ASSERT(maxImbalance > -1);
    if (maxImbalance == 0)
        maxImbalance = LONG_MAX;
    if (maxImbalance > -1)
        m_imbalance = maxImbalance;
    return oldValue;
}

//--------------------------------------------------------------------------
// Performs a full tree rebalance.  If the "force" parameter is nonzero,
// the rebalance is performed regardless of whether it is needed.  If the
// "force" parameter is zero, the rebalance is performed only if the top
// level of the tree is imbalanced; note that it is possible for the top
// level to be in balance while some subtrees are extremely imbalanced.
int tBalTree::Balance(int force)
{
    int didBalance = 0;
    if (m_root)
    {
        if (force || nodeNeedsBalance(m_root))
        {
            STAT_UPDATE(m_nBalManual);
            nodeBalance(m_root);
            didBalance = 1;
        }
    }
    return didBalance;
}

//--------------------------------------------------------------------------
// Set routine for use by derived classes.  Return 1 if success, or 0 if
// memory failure.
int tBalTree::set(void* key, void* data)
{
    STAT_UPDATE(m_nSet);
    if (!m_root)                        // empty tree, create first node
    {
        m_root = nodeCreate(key, data);
        return m_root ? 1 : 0;
    }
    int ok = 1;                         // only out-of-memory causes failure
    tBalTreeNode* added = 0;
    tBalTreeNode* cur = m_root;
    relativeKeyValue where = undefined;
    
    // Find proper location for node and add/replace.
    while (where != equal)
    {
        where = onCompareKeys(key, cur->m_nodeKey);
        if (where == equal)                     // this item, replace data
        {
            if (cur->m_nodeData)
                onDeleteData((void*&)cur->m_nodeData);
            onSetData((void*&)cur->m_nodeData, data);
        }
        else if (where == less)
        {
            if (!cur->m_leftSubtree)
            {
                added = nodeCreate(key, data);
                if (added)
                    nodeSetLeftChild(cur, added);
                else
                    ok = 0;                     // allocation failure
                where = equal;
            }
            else
                cur = cur->m_leftSubtree;
        }
        else    // where == greater
        {
            if (!cur->m_rightSubtree)
            {
                added = nodeCreate(key, data);
                if (added)
                    nodeSetRightChild(cur, added);
                else
                    ok = 0;                     // allocation failure
                where = equal;
            }
            else
                cur = cur->m_rightSubtree;
        }
    }
    if (added && cur)
        nodeUpdateBalance(cur);
    return ok;
}

//--------------------------------------------------------------------------
// Balance the subtree below the specified node.  Note that this is a full
// rebalance of the subtree whether it needs it or not.
void tBalTree::nodeBalance(tBalTreeNode* node)
{
    ASSERT(node);
    long nodes = nodeGetCount(node);
    if (nodes > 2)
    {
        // Get the new balance point of the subtree, and set it
        // as the result.
        tBalTreeNode* owner = node->m_parent;
        tBalTreeNode* root = nodeMakeBalancePoint(node);
        if (!owner)
            m_root = root;
        else
        {
            if (owner->m_leftSubtree == node)
                nodeSetLeftChild(owner, root);
            else
                nodeSetRightChild(owner, root);
        }
        // Put any qualifying left or right subtree pointers on the
        // stack.
        tBalTreeNode* stack[sizeof(long) * 8];  // enough for LONG_MAX nodes
        int stackIx = 0;
        if (root->m_leftSubtree && nodeGetCount(root->m_leftSubtree) > 2)
            stack[stackIx++] = root->m_leftSubtree;
        if (root->m_rightSubtree && nodeGetCount(root->m_rightSubtree) > 2)
            stack[stackIx++] = root->m_rightSubtree;
        // Balance from the stack until it becomes empty.
        tBalTreeNode* cur;
        tBalTreeNode* parent;
        tBalTreeNode* temp;
        while (stackIx)
        {
            cur = stack[--stackIx];
            parent = cur->m_parent;
            temp = nodeMakeBalancePoint(cur);
            if (parent->m_leftSubtree == cur)
                nodeSetLeftChild(parent, temp);
            else
                nodeSetRightChild(parent, temp);
            if (temp->m_leftSubtree && nodeGetCount(temp->m_leftSubtree) > 2)
                stack[stackIx++] = temp->m_leftSubtree;
            if (temp->m_rightSubtree && nodeGetCount(temp->m_rightSubtree) > 2)
                stack[stackIx++] = temp->m_rightSubtree;
        }
    }
}

//--------------------------------------------------------------------------
// Remove the specified node from the tree, reorganizing and rebalancing
// the tree as necessary.
void tBalTree::nodeRemove(tBalTreeNode* node)
{
    ASSERT(node);
    if (node)
    {
        tBalTreeNode* parent = node->m_parent;
        tBalTreeNode* child = 0;
        tBalTreeNode* topImbal = 0;
        // Combine the left/right subtrees of item being removed.
        if (node->m_leftSubtree || node->m_rightSubtree)  // not a leaf
        {
            // Heavier subtree becomes parent of lighter subtree.
            if (node->m_leftNodeCount > node->m_rightNodeCount)
            {
                if (node->m_rightSubtree)
                    topImbal = nodeInsertRightmost(node->m_leftSubtree, node->m_rightSubtree);
                child = node->m_leftSubtree;
            }
            else
            {
                if (node->m_leftSubtree)
                    topImbal = nodeInsertLeftmost(node->m_rightSubtree, node->m_leftSubtree);
                child = node->m_rightSubtree;
            }
            if (child)
                child->m_parent = 0;    // no parent link to deleted
        }
        else
            child = 0;                  // removing a leaf
        // "child" now contains combined subtree, insert it in parent.
        if (parent)
        {
            if (parent->m_leftSubtree == node)
                nodeSetLeftChild(parent, child);
            else
                nodeSetRightChild(parent, child);
        }
        else
            m_root = child;     // removing root node, child becomes new root
        // Node removed from tree, now delete it.
        if (node->m_nodeKey)
            onDeleteKey((void*&)node->m_nodeKey);
        if (node->m_nodeData)
            onDeleteData((void*&)node->m_nodeData);
        free(node);
        // Correct any imbalance introduced by removal.
        if (!topImbal)
            topImbal = parent;
        if (topImbal)
            nodeUpdateBalance(topImbal);
    }
}

//--------------------------------------------------------------------------
// Output structure recursively; called by DumpStructure().
void tBalTree::dumpStructure(tBalTreeNode* node, int level)
{
    #ifdef _DEBUG
        if (!level)
            TRACE("..........right..........\n");
        if (node->m_rightSubtree)
            dumpStructure(node->m_rightSubtree, level+1);
        char temp[100];
        for (int i=0; i<__min(level,99); i++)
            temp[i] = ' ';
        temp[__min(level,99)] = 0;
        char* info = getNodeInfo(node);
        TRACE("%06li %s%s\n", GetItemIndex((POSITION)node), temp, info);
        free(info);
        if (node->m_leftSubtree)
            dumpStructure(node->m_leftSubtree, level+1);
        if (!level)
            TRACE("..........left..........\n");
    #endif
}

//--------------------------------------------------------------------------
// Debug support routine called by DumpStructure().
// Note: caller must free() the result when it is no longer needed.
char* tBalTree::getNodeInfo(tBalTreeNode* node)
{
    ASSERT(node);
    char* temp = 0;
    #ifdef _DEBUG
        const char* keyName = 0;
        if (node->m_nodeKey)
            keyName = strdup(onGetKeyName(node->m_nodeKey));
        int len = keyName ? strlen(keyName) : 0;
        const char* parent = "NOPARENT";
        if (node->m_parent)
            if (node->m_parent->m_nodeKey)
                parent = onGetKeyName(node->m_parent->m_nodeKey);
        len += strlen(parent);
        temp = (char*)malloc(len + 30);
        sprintf(temp, "%li,%li=%s (parent=%s)", node->m_leftNodeCount, 
                node->m_rightNodeCount, keyName, parent);
        free((void*)keyName);
    #endif
    return temp;
}

//--------------------------------------------------------------------------
// Return a pointer to ASCII representation of the key's name.  The pointer
// returned may refer to a static buffer.  Used by the DumpStructure() member.
static char nameBuf[12];
const char* tBalTree::onGetKeyName(void* keyPtr)
{
    sprintf(nameBuf, "%p", keyPtr);
    return nameBuf;
}

//--------------------------------------------------------------------------
// Serialize the tree to an ostream.  Returns 1 if success else 0.
int tBalTree::Store(NQ ostream* ostrm)
{
    int ok = 0;
    if (ostrm && ostrm->good())
    {
        // first write empty header and note its position.
        m_bufSize = 0;              // initialize max buffer size required
        NQ streampos posHdr = ostrm->tellp();
        StrmHdr hdr;
        hdr.segLength = 0;
        hdr.segBufReqd = 0;
        ok = streamWrite(ostrm, &hdr, sizeof(hdr));
        
        // now serialize the tree.
        if (ok)
            ok = storeTree((void*)ostrm);
        
        // if successful update the header.
        if (ok)
        {
            hdr.segBufReqd = m_bufSize;
            NQ streampos posEnd = ostrm->tellp();
            hdr.segLength = (long)posEnd - (long)posHdr;
            ostrm->seekp(posHdr);
            ok = streamWrite(ostrm, &hdr, sizeof(hdr));
            ostrm->seekp(posEnd);
        }
    }
    return ok;
}

//--------------------------------------------------------------------------
// For each item in the tree, call the onStore() virtual.
int tBalTree::storeTree(void* where)
{
    int ok = 0;
	if (m_root)
	{
		tBalTreeNode* stack[sizeof(long) * 8];  // enoung for LONG_MAX nodes
		int stackIx = 0;
		stack[stackIx++] = m_root;
		tBalTreeNode* next;
		while (stackIx)
		{
			next = stack[--stackIx];
			ok = onStore(where, (POSITION)next);
			if (!ok)
				break;
			if (next->m_leftSubtree)
				stack[stackIx++] = next->m_leftSubtree;
			if (next->m_rightSubtree)
				stack[stackIx++] = next->m_rightSubtree;
		}
	}
    return ok;
}

//--------------------------------------------------------------------------
// Serialize the tree from an istream.  Returns 1 if success else 0.
int tBalTree::Load(NQ istream* istrm)
{
    RemoveAll();
    long imbalance = SetAutoBalance(0);     // turn off autobalance
    long posHdr = (long)istrm->tellg();
    StrmHdr hdr;
    int ok = streamRead(istrm, (void*)&hdr, sizeof(hdr));
    if (ok)
    {
        m_bufSize = hdr.segBufReqd;
        m_keyBuf = m_dataBuf = 0;
        if (m_bufSize > 0)
        {
            m_keyBuf = malloc((size_t)m_bufSize);
            m_dataBuf = malloc((size_t)m_bufSize);
            if (!m_keyBuf || !m_dataBuf)
                ok = 0;
        }
        if (ok)
        {
            long posEnd = posHdr + hdr.segLength;
            while (ok && (long)istrm->tellg() < posEnd && !istrm->eof())
                ok = onLoad((void*)istrm);
        }
        free(m_keyBuf);
        free(m_dataBuf);
    }
    SetAutoBalance(imbalance);              // resume autobalance
    return ok;
}

//--------------------------------------------------------------------------
// Store one item during serialization.  Returns 1 if success else 0.
int tBalTree::onStore(void* where, POSITION node)
{
    return 0;   // no-op unless overridden in derived class
}

//--------------------------------------------------------------------------
// Load the next item during serialization.  Returns 1 if success else 0.
int tBalTree::onLoad(void* where)
{
    return 0;   // no-op unless overridden in derived class
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Since 1992 Dundas Data Visualization has been helping companies all over the world visualize their data. Dundas products have a global reputation of being the highest quality, and are all designed, built and tested to meet the strictest requirements that developers and business managers demand.

Our showcase product is Dundas Dashboard, an easy-to-integrate digital dashboard software solution. Dundas Dashboard allows for the rapid and collaborative development of performance dashboards, helping companies leverage their business intelligence (BI) solutions.

Our web-based dashboard software comes with wizard interfaces, and a unique Dundas DashFlowTM process, allowing for the simultaneous development of an executive dashboard by business analysts, IT staff and database administrators. It also uses premier charts, maps, gauges and graph controls, letting end-users visualize their data as required.

Dundas also offers superb, world class consulting services for those companies that do not have the in-house expertise to implement their data visualization projects.

The quality of our products in conjunction with our unmatched technical support, numerous awards and years of experience reflect Dundas Data Visualization's commitment to being the best!
This is a Organisation

3 members

Comments and Discussions