Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

841 lines
23 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
avl.cxx
Abstract:
This module contains the code for generic balanced binary trees (AVL).
Author:
Boaz Feldbaum (BoazF) Apr 5, 1996
Environment:
Kernel mode
Revision History:
--*/
#include "internal.h"
#include "avl.h"
#ifndef MQDUMP
#include "avl.tmh"
#endif
// An enumeration type that indicates side of node.
typedef enum {
Left = 1,
Right
} TREE_SIDE;
#define VISIT_SIDE_FLAG 1
#define IS_VISITED_FLAG 2
#define NODE_FLAGS(n) ((n)->uFlags)
#define GET_VISIT_SIDE(n) ((NODE_FLAGS(n) & VISIT_SIDE_FLAG) ? Left : Right)
#define SET_VISIT_SIDE(n, s) ((s == Left) ? (NODE_FLAGS(n) |= VISIT_SIDE_FLAG) : \
(NODE_FLAGS(n) &= ~VISIT_SIDE_FLAG))
#define IS_VISITED(n) ((NODE_FLAGS(n) & IS_VISITED_FLAG) != 0)
#define SET_VISITED(n, v) ((v) ? (NODE_FLAGS(n) |= IS_VISITED_FLAG) : \
(NODE_FLAGS(n) &= ~IS_VISITED_FLAG))
#define NODE_HEIGHT(pNode) max(((pNode)->ulLeftHeight),((pNode)->ulRightHeight))
// An AVL tree node definition.
struct AVLNODE {
AVLNODE(); // Constructor.
AVLNODE *pParent; // Points to the parent of the node. NULL for root node.
AVLNODE *pLeftChild; // Points to the left hand child node.
AVLNODE *pRightChild; // Points to the right hand child node.
PVOID pData; // Points to the data that the node holds.
UCHAR ulLeftHeight; // Height of left hand sub tree.
UCHAR ulRightHeight; // Height of the right hand sub tree.
USHORT uFlags;
BOOL IsLeaf(void); // TRUE if node is a leaf.
void Attach(TREE_SIDE, AVLNODE *); // Attach a child node.
};
//
// The constructor for a node in the AVL tree.
//
inline AVLNODE::AVLNODE()
{
pParent = pLeftChild = pRightChild = NULL;
pData= NULL;
ulLeftHeight = ulRightHeight = 0;
uFlags = 0;
}
inline BOOL AVLNODE::IsLeaf(void)
{
return !pLeftChild && !pRightChild;
}
//
// Attach a child node.
//
void AVLNODE::Attach(TREE_SIDE Side, PAVLNODE pNode)
{
UCHAR h;
if (pNode) {
h = (UCHAR)(NODE_HEIGHT(pNode) + 1); // Calculate the new height.
pNode->pParent = this; // Make the child point to the parent.
} else {
h = 0;
}
// Point to the child and set the height on that side.
if (Side == Right) {
pRightChild = pNode;
ulRightHeight = h;
} else {
pLeftChild = pNode;
ulLeftHeight = h;
}
}
//
// The default node compare procedure. It compares the pointer values. This
// creates a tree of ULONGs.
//
static int __cdecl DefaultNodeCompProc(PVOID v1, PVOID v2)
{
return ComparePointersAVL(v1, v2);
}
//
// The default object deletion procedure - does nothing.
//
static void __cdecl DefaultNodeDelProc(PVOID /*p*/)
{
return;
}
//
// The default double object instance handler - does nothing.
//
static BOOL __cdecl DefaultNodeDblInstProc(PVOID /*pNew*/, PVOID /*pOld*/, BOOL /*bInsert*/)
{
return TRUE;
}
//
// Initialize the AVL tree object.
//
// Parameters:
// pfnDblInst - A function that deals double instances in the tree. This
// function gets called whenever an item is inserted to the tree
// and it already exist in the tree, and whenever an item is being
// deleted from the tree.
// BOOL DblInstProc(PVOID pNew, PVOID pOld, BOOL bInsert)
// pNew - Points to the data passed to AddNode() or DelNode()..
// pOld - Points to the data in the tree.
// bInsert - Indicates whether the function is called upon item
// insertion or item deletion.
// Return value: Upon item insertion, the return value of
// DblInstProc is the return value of AddNode().
// Upon item deletion, the return value detrmines
// whether the node should be deleted from the tree.
// If DblInstProc returns TRUE, the node is
// deleted.
// pfnCompNode - A function that is called in order to compare a node in the
// tree with a searched data.
// int CompNodeProc(PVOID v1, PVOID v2)
// v1 - Points to the searched data.
// v2 - Points to the data that the node points to.
// Returned value: 0 - If the items are equal.
// <0 - If the searched item is smaller than
// the data in the node.
// >0 - Else.
// pfnDelNode - A function that is called for each of the nodes upon the tree
// distruction. When the tree is being destroyed, the application
// is given a chance to also destroy the data that is being held
// by the nodes in the tree. The function is called once per each
// node.
// void DelNodeProc(PVOID pData)
// pData - Points to the data that is pointed by the node.
// pMutex - An optional pointer to a fast mutex object that is to be used for
// mutexing the tree operations. This is an optional parameter.
//
CAVLTree::CAVLTree(
NODEDOUBLEINSTANCEPROC pfnDblInst,
NODECOMPAREPROC pfnCompNode,
NODEDELETEPROC pfnDelNode
) :
m_Root(NULL)
{
m_pfnCompNode = pfnCompNode ? pfnCompNode : DefaultNodeCompProc;
m_pfnDelNode = pfnDelNode ? pfnDelNode : DefaultNodeDelProc;
m_pfnDblInstNode = pfnDblInst ? pfnDblInst : DefaultNodeDblInstProc;
}
//
// CAVLTree distructors.
//
CAVLTree::~CAVLTree()
{
PAVLNODE pCurr = m_Root;
if (!pCurr)
{
// Empty tree.
return;
}
// Delete left sub-tree, delete right sub-tree, goto parent and delete
// the child you just left.
for(;;)
{
while (pCurr->pLeftChild) {
SET_VISITED(pCurr, FALSE);
SET_VISIT_SIDE(pCurr, Left);
pCurr = pCurr->pLeftChild;
}
SET_VISITED(pCurr, TRUE);
m_pfnDelNode(pCurr->pData);
while (!pCurr->pRightChild) {
do {
pCurr = pCurr->pParent;
if (!pCurr)
break;
if (GET_VISIT_SIDE(pCurr) == Left)
delete pCurr->pLeftChild;
else
delete pCurr->pRightChild;
} while (IS_VISITED(pCurr));
if (pCurr) {
SET_VISITED(pCurr, TRUE);
m_pfnDelNode(pCurr->pData);
} else {
break;
}
}
if (pCurr) {
SET_VISIT_SIDE(pCurr, Right);
pCurr = pCurr->pRightChild;
SET_VISITED(pCurr, FALSE);
} else {
break;
}
}
// Delete the root node.
delete m_Root;
m_Root = NULL;
}
//
// Add a node to the tree.
//
BOOL CAVLTree::AddNode(PVOID pData2Add, CAVLTreeCursor *pCurs)
{
if (!m_Root)
{
//
// Insert the first node in the tree, becomes the root node.
//
m_Root = new (PagedPool) AVLNODE;
if(m_Root == 0)
return FALSE;
m_Root->pData = pData2Add;
pCurs->pCurr = m_Root;
pCurs->pPrev = NULL;
return TRUE;
}
PAVLNODE pNode;
int c = Search(pData2Add, pNode);
if (c == 0)
{
//
// Handle double instance and leave.
//
return m_pfnDblInstNode(pData2Add, pNode->pData, TRUE);
}
//
// Insert a new node.
//
PAVLNODE pNewNode = new (PagedPool) AVLNODE;
if(pNewNode == 0)
return FALSE;
pNewNode->pData = pData2Add;
pNewNode->pParent = pNode;
pCurs->pCurr = pNewNode;
pCurs->pPrev = NULL;
if (c < 0)
{
SET_VISIT_SIDE(pNode, Left);
pNode->pLeftChild = pNewNode;
}
else
{
SET_VISIT_SIDE(pNode, Right);
pNode->pRightChild = pNewNode;
}
// Go up the tree, update heights and balance the tree.
do {
if (GET_VISIT_SIDE(pNode) == Left)
{
pNode->ulLeftHeight = (UCHAR)(NODE_HEIGHT(pNode->pLeftChild) + 1);
}
else
{
pNode->ulRightHeight = (UCHAR)(NODE_HEIGHT(pNode->pRightChild) + 1);
}
Balance(&pNode);
pNode = pNode->pParent;
} while (pNode);
return TRUE;
}
//
// Delete a node from the tree.
//
void CAVLTree::DelNode(PVOID pData2Del)
{
// Empty tree, nothing to do.
if(!m_Root)
return;
// Data was not found, nothing to do.
PAVLNODE pNode;
if(Search(pData2Del, pNode) != 0)
return;
// See if the node should actually be removed from the tree.
if(!m_pfnDblInstNode(pData2Del, pNode->pData, FALSE))
return;
if((pNode == m_Root) && (m_Root->IsLeaf()))
{
// Special handling for a leaf root node.
delete m_Root;
m_Root = NULL;
return;
}
// Remove the node from the tree.
PAVLNODE pOtherNode;
pOtherNode = pNode->pRightChild;
if (pOtherNode)
{
// Go one child to the right and then try to go all the way to the left.
SET_VISIT_SIDE(pNode, Right);
if (pOtherNode->pLeftChild)
{
// Go all the way to the left.
while (pOtherNode->pLeftChild) {
SET_VISIT_SIDE(pOtherNode, Left);
pOtherNode = pOtherNode->pLeftChild;
}
// Switch the data.
pNode->pData = pOtherNode->pData;
// Since the tree in balanced and there is no left child, just drage
// the right sub tree up.
pNode = pOtherNode->pParent;
pOtherNode = pNode->pLeftChild->pRightChild;
delete pNode->pLeftChild;
if((pNode->pLeftChild = pOtherNode) != 0)
{
pOtherNode->pParent = pNode;
}
}
else
{
// No left child, just drag the right sub tree up.
pNode->pData = pOtherNode->pData;
if((pNode->pRightChild = pOtherNode->pRightChild) != 0)
{
pNode->pRightChild->pParent = pNode;
}
delete pOtherNode;
}
}
else
{
// No right child.
pNode = pNode->pParent;
if (!pNode)
{
// Special handling for a root node that doesn't have a right child.
pNode = m_Root;
m_Root = pNode->pLeftChild;
m_Root->pParent = NULL;
delete pNode;
return;
}
else
{
// Delete the node and drag the sub tree up.
if (GET_VISIT_SIDE(pNode) == Left)
{
pOtherNode = pNode->pLeftChild;
if((pNode->pLeftChild = pOtherNode->pLeftChild) != 0)
{
pNode->pLeftChild->pParent = pNode;
}
}
else
{
pOtherNode = pNode->pRightChild;
if((pNode->pRightChild = pOtherNode->pLeftChild) != 0)
{
pNode->pRightChild->pParent = pNode;
}
}
}
delete pOtherNode;
}
do
{
// Go up the tree, update heights and balance the tree.
if (GET_VISIT_SIDE(pNode) == Left)
{
if (pNode->pLeftChild)
pNode->ulLeftHeight = (UCHAR)(NODE_HEIGHT(pNode->pLeftChild) + 1);
else
pNode->ulLeftHeight = 0;
}
else
{
if (pNode->pRightChild)
{
pNode->ulRightHeight = (UCHAR)(NODE_HEIGHT(pNode->pRightChild) + 1);
}
else
{
pNode->ulRightHeight = 0;
}
}
Balance(&pNode);
pNode = pNode->pParent;
} while (pNode);
}
//
// Search the tree for some specific data.
//
int CAVLTree::Search(PVOID pData2Search, PAVLNODE &pNode)
{
int c;
ASSERT(m_Root);
pNode = m_Root;
if (pNode == 0) {
// Empty tree.
return 0;
}
for(;;)
{
// Compare the searched data with the data in the node.
c = m_pfnCompNode(pData2Search, pNode->pData);
if (c) {
if (c < 0) {
// Continue the search in the left sub tree.
SET_VISIT_SIDE(pNode, Left);
if (pNode->pLeftChild)
pNode = pNode->pLeftChild;
else
return c;
} else {
// Continue the search in the right sub tree.
SET_VISIT_SIDE(pNode, Right);
if (pNode->pRightChild)
pNode = pNode->pRightChild;
else
return c;
}
} else {
// The node is found.
return c;
}
}
}
//
// See if the sub tree is unbalanced and balance it, if necessary.
//
void CAVLTree::Balance(PAVLNODE *pNode)
{
PAVLNODE A, B, C, y, z;
PAVLNODE pParent;
int d;
A = *pNode;
if (!A) {
// Empty tree.
return;
}
pParent = A->pParent;
d = (int)A->ulLeftHeight - (int)A->ulRightHeight;
if (abs(d) < 2) {
// The sub tree is balanced.
return;
}
if (d < 0) {
B = A->pRightChild;
if (B->ulLeftHeight < B->ulRightHeight) {
/*
* RR
* A B
* / \ / \
* x B A C
* / \ -----> / \ / \
* y C x y z w
* / \
* z w
*/
y = B->pLeftChild;
*pNode = B;
A->Attach(Right, y);
B->Attach(Left, A);
} else {
/*
* RL
* A C
* / \ / \
* x B A B
* / \ -----> / \ / \
* C w x y z w
* / \
* y z
*/
C = B->pLeftChild;
y = C->pLeftChild;
z = C->pRightChild;
*pNode = C;
A->Attach(Right, y);
B->Attach(Left, z);
C->Attach(Left, A);
C->Attach(Right, B);
}
} else {
B = A->pLeftChild;
if (B->ulLeftHeight < B->ulRightHeight) {
/*
* LR
* A C
* / \ / \
* B w B A
* / \ -----> / \ / \
* x C x y z w
* / \
* y z
*/
C = B->pRightChild;
y = C->pLeftChild;
z = C->pRightChild;
*pNode = C;
A->Attach(Left, z);
B->Attach(Right, y);
C->Attach(Right, A);
C->Attach(Left, B);
} else {
/*
* LL
* A B
* / \ / \
* B w C A
* / \ -----> / \ / \
* C z x y z w
* / \
* x y
*/
z = B->pRightChild;
*pNode = B;
A->Attach(Left, z);
B->Attach(Right, A);
}
}
(*pNode)->pParent = pParent;
if (!pParent) {
// Update the pointer to the root node.
m_Root = *pNode;
} else {
// Update the child pointer of the parent node.
if (GET_VISIT_SIDE(pParent) == Left) {
pParent->pLeftChild = (*pNode);
} else {
pParent->pRightChild = (*pNode);
}
}
}
#define NEXT_CHILD(bAcc) ((bAcc) ? pCurr->pLeftChild : pCurr->pRightChild)
#ifdef UNUSED
//
// Enumerate all the nodes in the tree in accending or decending order.
//
// Parameters:
// bAccending - TRUE accending order, FALSE decending order.
// pfnEnumProc - The enumeration callback procedure:
// EnumNodesProc(PVOID pData, PVOID pContext, int iHeight)
// pData - A pointer to the node's data.
// pContext - A pointer to a context buffer.
// iHeight - The height of the node in the tree.
// Returned value: TRUE to continue the enumeration, else FALSE.
// pvContext - A pointer to a context buffer that is passed to the enumeration
// procedure.
//
BOOL CAVLTree::EnumNodes(BOOL bAccending, NODEENUMPROC pfnEnumProc,
PVOID pvContext)
{
PAVLNODE pCurr;
int h = 0;
BOOL bRet = TRUE;
if (!m_Root) {
// Empty tree.
bRet = FALSE;
goto ret;
}
pCurr = m_Root;
// Enumerate left sub tree, call the enumeration procedure and enumerate the
// right sub tree. Do in oposite order in case of decending enumeration.
do {
while (NEXT_CHILD(bAccending)) {
SET_VISITED(pCurr, FALSE);
pCurr = NEXT_CHILD(bAccending);
h++;
}
SET_VISITED(pCurr, TRUE);
if (!pfnEnumProc(pCurr->pData, pvContext, h))
goto ret;
while (!NEXT_CHILD(!bAccending)) {
do {
pCurr = pCurr->pParent;
h--;
} while (pCurr && IS_VISITED(pCurr));
if (pCurr) {
SET_VISITED(pCurr, TRUE);
if (!pfnEnumProc(pCurr->pData, pvContext, h))
goto ret;
} else
break;
}
if (pCurr) {
pCurr = NEXT_CHILD(!bAccending);
h++;
SET_VISITED(pCurr, FALSE);
}
} while (pCurr);
ret:;
return bRet;
}
#endif // UNUSED
//
// Find a node in the tree.
//
// Parameter:
// pData2Find - A pointer that is passed to the node compare function.
//
// Returned value:
// A pointer to the data in the tree. NULL, if the data was not found.
//
PVOID CAVLTree::FindNode(PVOID pData2Find)
{
PVOID pRet = NULL;
PAVLNODE pNode;
if (m_Root && (Search(pData2Find, pNode) == 0))
pRet = pNode->pData;
return pRet;
}
//
// Set a cursor to point to some node in the tree.
//
// Parameters:
// pData2Point - A pointer to the data that should be pointed by the cursor.
// There are two special case values:
// POINT_TO_SMALLEST - Point to the smallest value in the tree.
// POINT_TO_LARGEST - Point to the largest value in the tree.
// pCursor - A pointer to a cursor structure. The cursor structure is filled
// by this function.
// pData - After calling SetCursor(), this pointer will point to the data that
// the cursor points to.
//
// Comments:
// After changing the contents of the tree (adding and/or deleting nodes),
// it is not possible to use the cursor anymore.
//
BOOL CAVLTree::SetCursor(PVOID pData2Point, CAVLTreeCursor *pCursor, PVOID *pData)
{
BOOL bRet = FALSE;
PAVLNODE pNode = NULL;
// Search the item.
if (m_Root) {
switch ((ULONG_PTR)pData2Point) {
case (ULONG_PTR)POINT_TO_SMALLEST:
pNode = m_Root;
while (pNode->pLeftChild)
pNode = pNode->pLeftChild;
bRet = TRUE;
break;
case (ULONG_PTR)POINT_TO_LARGEST:
pNode = m_Root;
while (pNode->pRightChild)
pNode = pNode->pRightChild;
bRet = TRUE;
break;
default:
if (Search(pData2Point, pNode) == 0)
bRet = TRUE;
break;
}
}
// Point to the item.
if (bRet) {
*pData = pNode->pData;
} else {
*pData = NULL;
}
// Fill the cursor structure.
pCursor->pCurr = pNode;
pCursor->pPrev = NULL;
return bRet;
}
//
// Get the next data in the tree that has the next higher value in the tree.
//
// Parameters:
// pCursor - A pointer to a cursor structure. The cursor structure must be
// first filled by the SetCursor().
// pData - After calling GetNext(), this pointer will point to the data that
// the cursor points to.
//
// Comments:
// After changing the contents of the tree (adding and/or deleting nodes),
// it is not possible to use the cursor anymore.
//
// GetNext() fails if called when the cursor points to the node with the
// highest value in the tree.
//
// After changing the contents of the tree (adding and/or deleting nodes),
// it is not possible to use the cursor anymore.
//
BOOL CAVLTree::GetNext(CAVLTreeCursor *pCursor, PVOID *pData)
{
BOOL bRet;
PAVLNODE pNode;
pNode = pCursor->pCurr;
if (!pNode->pRightChild || pNode->pRightChild == pCursor->pPrev) {
do {
pNode = pNode->pParent;
pCursor->pPrev = pCursor->pCurr;
pCursor->pCurr = pNode;
} while(pNode && (pNode->pRightChild == pCursor->pPrev));
} else {
pNode = pNode->pRightChild;
while(pNode->pLeftChild)
pNode = pNode->pLeftChild;
pCursor->pPrev = NULL;
pCursor->pCurr = pNode;
}
// Point to the item.
if (pNode) {
*pData = pNode->pData;
bRet = TRUE;
} else {
*pData = NULL;
bRet = FALSE;
}
return bRet;
}
//
// Get the next data in the tree that has the next lower value in the tree.
//
// Parameters:
// pCursor - A pointer to a cursor structure. The cursor structure must be
// first filled by the SetCursor().
// pData - After calling GetPrev(), this pointer will point to the data that
// the cursor points to.
//
// Comments:
// After changing the contents of the tree (adding and/or deleting nodes),
// it is not possible to use the cursor anymore.
//
// GetPrev() fails if called when the cursor points to the node with the
// lowest value in the tree.
//
// After changing the contents of the tree (adding and/or deleting nodes),
// it is not possible to use the cursor anymore.
//
BOOL CAVLTree::GetPrev(CAVLTreeCursor *pCursor, PVOID *pData)
{
BOOL bRet;
PAVLNODE pNode;
pNode = pCursor->pCurr;
if (!pNode->pLeftChild || (pNode->pLeftChild == pCursor->pPrev)) {
do {
pNode = pNode->pParent;
pCursor->pPrev = pCursor->pCurr;
pCursor->pCurr = pNode;
} while(pNode && (pNode->pLeftChild == pCursor->pPrev));
} else {
pNode = pNode->pLeftChild;
while(pNode->pRightChild != NULL)
pNode = pNode->pRightChild;
pCursor->pPrev = NULL;
pCursor->pCurr = pNode;
}
// Point to the item.
if (pNode) {
*pData = pNode->pData;
bRet = TRUE;
} else {
*pData = NULL;
bRet = FALSE;
}
return bRet;
}