1082 lines
21 KiB
C
1082 lines
21 KiB
C
/*
|
|
* Process Hacker -
|
|
* AVL tree
|
|
*
|
|
* Copyright (C) 2010-2016 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <phbase.h>
|
|
|
|
/**
|
|
* Initializes an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param CompareFunction A function used to compare tree elements.
|
|
*/
|
|
VOID PhInitializeAvlTree(
|
|
_Out_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_TREE_COMPARE_FUNCTION CompareFunction
|
|
)
|
|
{
|
|
Tree->Root.Parent = NULL;
|
|
Tree->Root.Left = NULL;
|
|
Tree->Root.Right = NULL;
|
|
Tree->Root.Balance = 0;
|
|
Tree->Count = 0;
|
|
|
|
Tree->CompareFunction = CompareFunction;
|
|
}
|
|
|
|
/**
|
|
* Finds an element in an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element The element to find.
|
|
* \param Result The result of the search.
|
|
*/
|
|
FORCEINLINE PPH_AVL_LINKS PhpFindElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_LINKS Element,
|
|
_Out_ PLONG Result
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
LONG result;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
|
|
if (!links)
|
|
{
|
|
*Result = 1;
|
|
return &Tree->Root;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
result = Tree->CompareFunction(Element, links);
|
|
|
|
if (result == 0)
|
|
{
|
|
*Result = 0;
|
|
return links;
|
|
}
|
|
else if (result < 0)
|
|
{
|
|
if (links->Left)
|
|
{
|
|
links = links->Left;
|
|
}
|
|
else
|
|
{
|
|
*Result = -1;
|
|
return links;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (links->Right)
|
|
{
|
|
links = links->Right;
|
|
}
|
|
else
|
|
{
|
|
*Result = 1;
|
|
return links;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FORCEINLINE VOID PhpRotateLeftAvlLinks(
|
|
_Inout_ PPH_AVL_LINKS *Root
|
|
)
|
|
{
|
|
PPH_AVL_LINKS P;
|
|
PPH_AVL_LINKS Q;
|
|
|
|
// P
|
|
// | |
|
|
// A Q
|
|
// | |
|
|
// B C
|
|
//
|
|
// becomes
|
|
//
|
|
// Q
|
|
// | |
|
|
// P C
|
|
// | |
|
|
// A B
|
|
//
|
|
// P and Q must exist.
|
|
// B may not exist.
|
|
// A and C are not affected.
|
|
|
|
P = *Root;
|
|
Q = P->Right;
|
|
|
|
// The new root is Q
|
|
|
|
*Root = Q;
|
|
Q->Parent = P->Parent;
|
|
|
|
// P.Right = Q.Left (transfer B)
|
|
|
|
P->Right = Q->Left;
|
|
|
|
if (P->Right)
|
|
P->Right->Parent = P;
|
|
|
|
// Q.Left = P
|
|
|
|
Q->Left = P;
|
|
P->Parent = Q;
|
|
}
|
|
|
|
FORCEINLINE VOID PhpRotateLeftTwiceAvlLinks(
|
|
_Inout_ PPH_AVL_LINKS *Root
|
|
)
|
|
{
|
|
PPH_AVL_LINKS P;
|
|
PPH_AVL_LINKS Q;
|
|
PPH_AVL_LINKS R;
|
|
|
|
// P
|
|
// | |
|
|
// A Q
|
|
// | |
|
|
// R D
|
|
// | |
|
|
// B C
|
|
//
|
|
// becomes
|
|
//
|
|
// R
|
|
// | |
|
|
// P Q
|
|
// | | | |
|
|
// A B C D
|
|
//
|
|
// P, Q, and R must exist.
|
|
// B and C may not exist.
|
|
// A and D are not affected.
|
|
|
|
// PhpRotateRightAvlLinks(&(*Root)->Right);
|
|
// PhpRotateLeftAvlLinks(Root);
|
|
|
|
// P is the current root
|
|
// Q is P.Right
|
|
// R is Q.Left (P.Right.Left)
|
|
|
|
P = *Root;
|
|
Q = P->Right;
|
|
R = Q->Left;
|
|
|
|
// The new root is R
|
|
|
|
*Root = R;
|
|
R->Parent = P->Parent;
|
|
|
|
// Q.Left = R.Right (transfer C)
|
|
|
|
Q->Left = R->Right;
|
|
|
|
if (Q->Left)
|
|
Q->Left->Parent = Q;
|
|
|
|
// R.Right = Q
|
|
|
|
R->Right = Q;
|
|
Q->Parent = R;
|
|
|
|
// P.Right = R.Left (transfer B)
|
|
|
|
P->Right = R->Left;
|
|
|
|
if (P->Right)
|
|
P->Right->Parent = P;
|
|
|
|
// R.Left = P
|
|
|
|
R->Left = P;
|
|
P->Parent = R;
|
|
}
|
|
|
|
FORCEINLINE VOID PhpRotateRightAvlLinks(
|
|
_Inout_ PPH_AVL_LINKS *Root
|
|
)
|
|
{
|
|
PPH_AVL_LINKS Q;
|
|
PPH_AVL_LINKS P;
|
|
|
|
// Q
|
|
// | |
|
|
// P C
|
|
// | |
|
|
// A B
|
|
//
|
|
// becomes
|
|
//
|
|
// P
|
|
// | |
|
|
// A Q
|
|
// | |
|
|
// B C
|
|
//
|
|
// Q and P must exist.
|
|
// B may not exist.
|
|
// A and C are not affected.
|
|
|
|
Q = *Root;
|
|
P = Q->Left;
|
|
|
|
// The new root is P
|
|
|
|
*Root = P;
|
|
P->Parent = Q->Parent;
|
|
|
|
// Q.Left = P.Right (transfer B)
|
|
|
|
Q->Left = P->Right;
|
|
|
|
if (Q->Left)
|
|
Q->Left->Parent = Q;
|
|
|
|
// P.Right = Q
|
|
|
|
P->Right = Q;
|
|
Q->Parent = P;
|
|
}
|
|
|
|
FORCEINLINE VOID PhpRotateRightTwiceAvlLinks(
|
|
_Inout_ PPH_AVL_LINKS *Root
|
|
)
|
|
{
|
|
PPH_AVL_LINKS P;
|
|
PPH_AVL_LINKS Q;
|
|
PPH_AVL_LINKS R;
|
|
|
|
// P
|
|
// | |
|
|
// Q D
|
|
// | |
|
|
// A R
|
|
// | |
|
|
// B C
|
|
//
|
|
// becomes
|
|
//
|
|
// R
|
|
// | |
|
|
// Q P
|
|
// | | | |
|
|
// A B C D
|
|
//
|
|
// P, Q, and R must exist.
|
|
// B and C may not exist.
|
|
// A and D are not affected.
|
|
|
|
// PhpRotateLeftAvlLinks(&(*Root)->Left);
|
|
// PhpRotateRightAvlLinks(Root);
|
|
|
|
// P is the current root
|
|
// Q is P.Left
|
|
// R is Q.Right (P.Left.Right)
|
|
|
|
P = *Root;
|
|
Q = P->Left;
|
|
R = Q->Right;
|
|
|
|
// The new root is R
|
|
|
|
*Root = R;
|
|
R->Parent = P->Parent;
|
|
|
|
// Q.Right = R.Left (transfer B)
|
|
|
|
Q->Right = R->Left;
|
|
|
|
if (Q->Right)
|
|
Q->Right->Parent = Q;
|
|
|
|
// R.Left = Q
|
|
|
|
R->Left = Q;
|
|
Q->Parent = R;
|
|
|
|
// P.Left = R.Right (transfer C)
|
|
|
|
P->Left = R->Right;
|
|
|
|
if (P->Left)
|
|
P->Left->Parent = P;
|
|
|
|
// R.Right = P
|
|
|
|
R->Right = P;
|
|
P->Parent = R;
|
|
}
|
|
|
|
ULONG PhpRebalanceAvlLinks(
|
|
_Inout_ PPH_AVL_LINKS *Root
|
|
)
|
|
{
|
|
PPH_AVL_LINKS P;
|
|
PPH_AVL_LINKS Q;
|
|
PPH_AVL_LINKS R;
|
|
|
|
P = *Root;
|
|
|
|
if (P->Balance == -1)
|
|
{
|
|
Q = P->Left;
|
|
|
|
if (Q->Balance == -1)
|
|
{
|
|
// Left-left
|
|
|
|
PhpRotateRightAvlLinks(Root);
|
|
|
|
P->Balance = 0;
|
|
Q->Balance = 0;
|
|
|
|
return 1;
|
|
}
|
|
else if (Q->Balance == 1)
|
|
{
|
|
// Left-right
|
|
|
|
R = Q->Right;
|
|
|
|
PhpRotateRightTwiceAvlLinks(Root);
|
|
|
|
if (R->Balance == -1)
|
|
{
|
|
P->Balance = 1;
|
|
Q->Balance = 0;
|
|
}
|
|
else if (R->Balance == 1)
|
|
{
|
|
P->Balance = 0;
|
|
Q->Balance = -1;
|
|
}
|
|
else
|
|
{
|
|
P->Balance = 0;
|
|
Q->Balance = 0;
|
|
}
|
|
|
|
R->Balance = 0;
|
|
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
// Special (only occurs when removing)
|
|
|
|
// D
|
|
// | |
|
|
// B E
|
|
// | |
|
|
// A C
|
|
//
|
|
// Removing E results in:
|
|
//
|
|
// D
|
|
// |
|
|
// B
|
|
// | |
|
|
// A C
|
|
//
|
|
// which is unbalanced. Rotating right at B results in:
|
|
//
|
|
// B
|
|
// | |
|
|
// A D
|
|
// |
|
|
// C
|
|
//
|
|
// The same applies for the mirror case.
|
|
|
|
PhpRotateRightAvlLinks(Root);
|
|
|
|
Q->Balance = 1;
|
|
|
|
return 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Q = P->Right;
|
|
|
|
if (Q->Balance == 1)
|
|
{
|
|
// Right-right
|
|
|
|
PhpRotateLeftAvlLinks(Root);
|
|
|
|
P->Balance = 0;
|
|
Q->Balance = 0;
|
|
|
|
return 1;
|
|
}
|
|
else if (Q->Balance == -1)
|
|
{
|
|
// Right-left
|
|
|
|
R = Q->Left;
|
|
|
|
PhpRotateLeftTwiceAvlLinks(Root);
|
|
|
|
if (R->Balance == -1)
|
|
{
|
|
P->Balance = 0;
|
|
Q->Balance = 1;
|
|
}
|
|
else if (R->Balance == 1)
|
|
{
|
|
P->Balance = -1;
|
|
Q->Balance = 0;
|
|
}
|
|
else
|
|
{
|
|
P->Balance = 0;
|
|
Q->Balance = 0;
|
|
}
|
|
|
|
R->Balance = 0;
|
|
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
// Special (only occurs when removing)
|
|
|
|
PhpRotateLeftAvlLinks(Root);
|
|
|
|
Q->Balance = -1;
|
|
|
|
return 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an element to an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element The element to add.
|
|
*
|
|
* \return NULL if the element was added, or an existing element.
|
|
*/
|
|
PPH_AVL_LINKS PhAddElementAvlTree(
|
|
_Inout_ PPH_AVL_TREE Tree,
|
|
_Out_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
LONG result;
|
|
PPH_AVL_LINKS P;
|
|
PPH_AVL_LINKS root;
|
|
LONG balance;
|
|
|
|
P = PhpFindElementAvlTree(Tree, Element, &result);
|
|
|
|
if (result < 0)
|
|
P->Left = Element;
|
|
else if (result > 0)
|
|
P->Right = Element;
|
|
else
|
|
return P;
|
|
|
|
Element->Parent = P;
|
|
Element->Left = NULL;
|
|
Element->Right = NULL;
|
|
Element->Balance = 0;
|
|
|
|
// Balance the tree.
|
|
|
|
P = Element;
|
|
root = PhRootElementAvlTree(Tree);
|
|
|
|
while (P != root)
|
|
{
|
|
// In this implementation, the balance factor is the right height minus left height.
|
|
|
|
if (P->Parent->Left == P)
|
|
balance = -1;
|
|
else
|
|
balance = 1;
|
|
|
|
P = P->Parent;
|
|
|
|
if (P->Balance == 0)
|
|
{
|
|
// The balance becomes -1 or 1. Rotations are not needed
|
|
// yet, but we should keep tracing upwards.
|
|
|
|
P->Balance = balance;
|
|
}
|
|
else if (P->Balance != balance)
|
|
{
|
|
// The balance is opposite the new balance, so it now
|
|
// becomes 0.
|
|
|
|
P->Balance = 0;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
PPH_AVL_LINKS *ref;
|
|
|
|
// The balance is the same as the new balance, meaning
|
|
// it now becomes -2 or 2. Rotations are needed.
|
|
|
|
if (P->Parent->Left == P)
|
|
ref = &P->Parent->Left;
|
|
else
|
|
ref = &P->Parent->Right;
|
|
|
|
PhpRebalanceAvlLinks(ref);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Tree->Count++;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Removes an element from an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element An element already present in the tree.
|
|
*/
|
|
VOID PhRemoveElementAvlTree(
|
|
_Inout_ PPH_AVL_TREE Tree,
|
|
_Inout_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS newElement;
|
|
PPH_AVL_LINKS *replace;
|
|
PPH_AVL_LINKS P;
|
|
PPH_AVL_LINKS root;
|
|
LONG balance;
|
|
|
|
if (!Element->Left || !Element->Right)
|
|
{
|
|
newElement = Element;
|
|
}
|
|
else if (Element->Balance >= 0) // Pick the side depending on the balance to minimize rebalances
|
|
{
|
|
newElement = Element->Right;
|
|
|
|
while (newElement->Left)
|
|
newElement = newElement->Left;
|
|
}
|
|
else
|
|
{
|
|
newElement = Element->Left;
|
|
|
|
while (newElement->Right)
|
|
newElement = newElement->Right;
|
|
}
|
|
|
|
if (newElement->Parent->Left == newElement)
|
|
{
|
|
replace = &newElement->Parent->Left;
|
|
balance = -1;
|
|
}
|
|
else
|
|
{
|
|
replace = &newElement->Parent->Right;
|
|
balance = 1;
|
|
}
|
|
|
|
if (!newElement->Right)
|
|
{
|
|
*replace = newElement->Left;
|
|
|
|
if (newElement->Left)
|
|
newElement->Left->Parent = newElement->Parent;
|
|
}
|
|
else
|
|
{
|
|
*replace = newElement->Right;
|
|
newElement->Right->Parent = newElement->Parent; // we know Right exists
|
|
}
|
|
|
|
P = newElement->Parent;
|
|
root = &Tree->Root;
|
|
|
|
while (P != root)
|
|
{
|
|
if (P->Balance == balance)
|
|
{
|
|
// The balance is cancelled by the remove operation and becomes 0.
|
|
// Rotations are not needed yet, but we should keep tracing upwards.
|
|
|
|
P->Balance = 0;
|
|
}
|
|
else if (P->Balance == 0)
|
|
{
|
|
// The balance is 0, so it now becomes -1 or 1.
|
|
|
|
P->Balance = -balance;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
PPH_AVL_LINKS *ref;
|
|
|
|
// The balance is the same as the new balance, meaning
|
|
// it now becomes -2 or 2. Rotations are needed.
|
|
|
|
if (P->Parent->Left == P)
|
|
ref = &P->Parent->Left;
|
|
else
|
|
ref = &P->Parent->Right;
|
|
|
|
// We can stop tracing if we have a special case rotation.
|
|
if (PhpRebalanceAvlLinks(ref) == 3)
|
|
break;
|
|
|
|
P = P->Parent;
|
|
}
|
|
|
|
if (P->Parent->Left == P)
|
|
balance = -1;
|
|
else
|
|
balance = 1;
|
|
|
|
P = P->Parent;
|
|
}
|
|
|
|
if (newElement != Element)
|
|
{
|
|
// Replace the subject with the new subject.
|
|
|
|
*newElement = *Element;
|
|
|
|
if (Element->Parent->Left == Element)
|
|
newElement->Parent->Left = newElement;
|
|
else
|
|
newElement->Parent->Right = newElement;
|
|
|
|
if (newElement->Left)
|
|
newElement->Left->Parent = newElement;
|
|
if (newElement->Right)
|
|
newElement->Right->Parent = newElement;
|
|
}
|
|
|
|
Tree->Count--;
|
|
}
|
|
|
|
/**
|
|
* Finds an element in an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element An element to find.
|
|
*
|
|
* \return The element, or NULL if it could not be found.
|
|
*/
|
|
PPH_AVL_LINKS PhFindElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
LONG result;
|
|
|
|
links = PhpFindElementAvlTree(Tree, Element, &result);
|
|
|
|
if (result == 0)
|
|
return links;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Finds the first element in an AVL tree that is greater than or equal to the specified element.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element The element to find.
|
|
*
|
|
* \return The bound element, or NULL if the tree is empty.
|
|
*/
|
|
PPH_AVL_LINKS PhLowerBoundElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
PPH_AVL_LINKS closest;
|
|
LONG result;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
closest = NULL;
|
|
|
|
while (links)
|
|
{
|
|
result = Tree->CompareFunction(Element, links);
|
|
|
|
if (result > 0)
|
|
{
|
|
links = links->Right;
|
|
}
|
|
else
|
|
{
|
|
closest = links;
|
|
links = links->Left;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
/**
|
|
* Finds the first element in an AVL tree that is greater than the specified element.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element The element to find.
|
|
*
|
|
* \return The bound element, or NULL if the tree is empty.
|
|
*/
|
|
PPH_AVL_LINKS PhUpperBoundElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
PPH_AVL_LINKS closest;
|
|
LONG result;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
closest = NULL;
|
|
|
|
while (links)
|
|
{
|
|
result = Tree->CompareFunction(Element, links);
|
|
|
|
if (result >= 0)
|
|
{
|
|
links = links->Right;
|
|
}
|
|
else
|
|
{
|
|
closest = links;
|
|
links = links->Left;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
/**
|
|
* Finds the last element in an AVL tree that is less than the specified element.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element The element to find.
|
|
*
|
|
* \return The bound element, or NULL if the tree is empty.
|
|
*/
|
|
PPH_AVL_LINKS PhLowerDualBoundElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
PPH_AVL_LINKS closest;
|
|
LONG result;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
closest = NULL;
|
|
|
|
while (links)
|
|
{
|
|
result = Tree->CompareFunction(Element, links);
|
|
|
|
if (result > 0)
|
|
{
|
|
closest = links;
|
|
links = links->Right;
|
|
}
|
|
else
|
|
{
|
|
links = links->Left;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
/**
|
|
* Finds the last element in an AVL tree that is less than or equal to the specified element.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Element The element to find.
|
|
*
|
|
* \return The bound element, or NULL if the tree is empty.
|
|
*/
|
|
PPH_AVL_LINKS PhUpperDualBoundElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
PPH_AVL_LINKS closest;
|
|
LONG result;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
closest = NULL;
|
|
|
|
while (links)
|
|
{
|
|
result = Tree->CompareFunction(Element, links);
|
|
|
|
if (result >= 0)
|
|
{
|
|
closest = links;
|
|
links = links->Right;
|
|
}
|
|
else
|
|
{
|
|
links = links->Left;
|
|
}
|
|
}
|
|
|
|
return closest;
|
|
}
|
|
|
|
/**
|
|
* Finds the smallest element in an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
*
|
|
* \return An element, or NULL if the tree is empty.
|
|
*/
|
|
PPH_AVL_LINKS PhMinimumElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
|
|
if (!links)
|
|
return NULL;
|
|
|
|
while (links->Left)
|
|
links = links->Left;
|
|
|
|
return links;
|
|
}
|
|
|
|
/**
|
|
* Finds the biggest element in an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
*
|
|
* \return An element, or NULL if the tree is empty.
|
|
*/
|
|
PPH_AVL_LINKS PhMaximumElementAvlTree(
|
|
_In_ PPH_AVL_TREE Tree
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
|
|
links = PhRootElementAvlTree(Tree);
|
|
|
|
if (!links)
|
|
return NULL;
|
|
|
|
while (links->Right)
|
|
links = links->Right;
|
|
|
|
return links;
|
|
}
|
|
|
|
/**
|
|
* Finds the next element in an AVL tree.
|
|
*
|
|
* \param Element The element.
|
|
*
|
|
* \return The next element, or NULL if there are no more elements.
|
|
*/
|
|
PPH_AVL_LINKS PhSuccessorElementAvlTree(
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
|
|
if (Element->Right)
|
|
{
|
|
Element = Element->Right;
|
|
|
|
while (Element->Left)
|
|
Element = Element->Left;
|
|
|
|
return Element;
|
|
}
|
|
else
|
|
{
|
|
// Trace back to the next vertical level. Note
|
|
// that this code does in fact return NULL when there
|
|
// are no more elements because of the way the root
|
|
// element is constructed.
|
|
|
|
links = Element->Parent;
|
|
|
|
while (links && links->Right == Element)
|
|
{
|
|
Element = links;
|
|
links = links->Parent;
|
|
}
|
|
|
|
return links;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds the previous element in an AVL tree.
|
|
*
|
|
* \param Element The element.
|
|
*
|
|
* \return The previous element, or NULL if there are no more elements.
|
|
*/
|
|
PPH_AVL_LINKS PhPredecessorElementAvlTree(
|
|
_In_ PPH_AVL_LINKS Element
|
|
)
|
|
{
|
|
PPH_AVL_LINKS links;
|
|
|
|
if (Element->Left)
|
|
{
|
|
Element = Element->Left;
|
|
|
|
while (Element->Right)
|
|
Element = Element->Right;
|
|
|
|
return Element;
|
|
}
|
|
else
|
|
{
|
|
links = Element->Parent;
|
|
|
|
while (links && links->Left == Element)
|
|
{
|
|
Element = links;
|
|
links = links->Parent;
|
|
}
|
|
|
|
if (links)
|
|
{
|
|
// We need an additional check because the tree root is
|
|
// stored in Root.Right, not Left.
|
|
if (!links->Parent)
|
|
return NULL; // reached Root, so no more elements
|
|
}
|
|
|
|
return links;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enumerates the elements in an AVL tree.
|
|
*
|
|
* \param Tree The tree.
|
|
* \param Order The enumeration order.
|
|
* \param Callback The callback function.
|
|
* \param Context A user-defined value to pass to the callback function.
|
|
*/
|
|
VOID PhEnumAvlTree(
|
|
_In_ PPH_AVL_TREE Tree,
|
|
_In_ PH_TREE_ENUMERATION_ORDER Order,
|
|
_In_ PPH_ENUM_AVL_TREE_CALLBACK Callback,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
// The maximum height of an AVL tree is around 1.44 * log2(n).
|
|
// The maximum number of elements in this implementation is 2^32, so the maximum height is
|
|
// around 46.08.
|
|
PPH_AVL_LINKS stackBase[47];
|
|
PPH_AVL_LINKS *stack;
|
|
PPH_AVL_LINKS links;
|
|
|
|
stack = stackBase;
|
|
|
|
switch (Order)
|
|
{
|
|
case TreeEnumerateInOrder:
|
|
links = PhRootElementAvlTree(Tree);
|
|
|
|
while (links)
|
|
{
|
|
*stack++ = links;
|
|
links = links->Left;
|
|
}
|
|
|
|
while (stack != stackBase)
|
|
{
|
|
links = *--stack;
|
|
|
|
if (!Callback(Tree, links, Context))
|
|
break;
|
|
|
|
links = links->Right;
|
|
|
|
while (links)
|
|
{
|
|
*stack++ = links;
|
|
links = links->Left;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case TreeEnumerateInReverseOrder:
|
|
links = PhRootElementAvlTree(Tree);
|
|
|
|
while (links)
|
|
{
|
|
*stack++ = links;
|
|
links = links->Right;
|
|
}
|
|
|
|
while (stack != stackBase)
|
|
{
|
|
links = *--stack;
|
|
|
|
if (!Callback(Tree, links, Context))
|
|
break;
|
|
|
|
links = links->Left;
|
|
|
|
while (links)
|
|
{
|
|
*stack++ = links;
|
|
links = links->Right;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|