平衡二叉树
平衡二叉树,相对于二叉查找树多了旋转操作,保证了查找元素时每一次比较都能做到折半。
头文件列出了要用到的结构和一些宏
#pragma once
#include <windows.h>
#define NAME_SIZE 20
#define BUFFER_SIZE 40
typedef struct {
TCHAR szHeroName[NAME_SIZE];
int iHeroType;
int iGoldPrice;
int iOtherPrice;
}ITEM;
typedef struct node {
ITEM item;
int iHeight;
struct node * pLeft;
struct node * pRight;
}NODE;
typedef NODE * TREE;
void InitTree(TREE * pTree);
NODE * InsertNode(NODE * pRoot, const ITEM * pItem);
NODE * DeleteNode(NODE * pRoot, NODE * pNode);
NODE * DeleteItem(NODE * pRoot, const ITEM * pItem);
NODE * SearchItem(NODE * pRoot, const ITEM * pItem);
void VisitItem(NODE * pNode);
void PreOrder(NODE * pRoot);
void InOrder(NODE * pRoot);
void PostOrder(NODE * pRoot);
void DestroyTree(NODE * pRoot);
BOOL CheckAVL(NODE * pRoot);
用到的内部函数。旋转之后需要修改原子树根节点和旋转之后子树根节点的高度。
int GetHeight(NODE * pRoot)
{
if (pRoot)
return pRoot->iHeight;
else
return 0;
}
int GetBL(NODE * pRoot)
{
if (pRoot)
return GetHeight(pRoot->pLeft) - GetHeight(pRoot->pRight);
else
return 0;
}
NODE * Retate_LL(NODE * pRoot)
{
NODE * pNode;
pNode = pRoot->pLeft;
pRoot->pLeft = pNode->pRight;
pNode->pRight = pRoot;
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
pNode->iHeight = max(GetHeight(pNode->pLeft), GetHeight(pNode->pRight)) + 1;
return pNode;
}
NODE * Retate_RR(NODE * pRoot)
{
NODE * pNode;
pNode = pRoot->pRight;
pRoot->pRight = pNode->pLeft;
pNode->pLeft = pRoot;
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
pNode->iHeight = max(GetHeight(pNode->pLeft), GetHeight(pNode->pRight)) + 1;
return pNode;
}
NODE * Retate_LR(NODE * pRoot)
{
pRoot->pLeft = Retate_RR(pRoot->pLeft);
return Retate_LL(pRoot);
}
NODE * Retate_RL(NODE * pRoot)
{
pRoot->pRight = Retate_LL(pRoot->pRight);
return Retate_RR(pRoot);
}
NODE * MakeNode(const ITEM * pItem)
{
NODE * pNew;
if (pNew = (NODE *)malloc(sizeof(NODE)))
{
pNew->item = *pItem;
pNew->iHeight = 1;
pNew->pLeft = pNew->pRight = NULL;
}
return pNew;
}
int CheckHeight(NODE * pRoot)
{
if (pRoot == NULL) return 0;
return max(CheckHeight(pRoot->pLeft), CheckHeight(pRoot->pRight)) + 1;
}
有了头文件和内部函数,可以实现接口函数了,插入和删除都需要从修改的位置到根节点检查平衡,这种从插入或删除的位置回溯到根节点的性质
决定了递归是一种比较好实现的方法。
删除结点时并不是真正删除该结点,而是找到该节点左树枝部分最大的元素来代替要删除的结点,这样能保证左右结点和父节点的关系。
那么找到的那个最大元素也需要从它左半部分中找到最大的元素来代替它,直到找到左孩子为NULL的结点将其删除。
void InitTree(TREE * pTree)
{
*pTree = NULL;
}
NODE * InsertNode(NODE * pRoot, const ITEM * pItem)
{
if (pRoot == NULL)
return MakeNode(pItem);
else
{
if (lstrcmp(pRoot->item.szHeroName, pItem->szHeroName) > 0)
{
pRoot->pLeft = InsertNode(pRoot->pLeft, pItem);
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
if (GetBL(pRoot) == 2)
{
if (GetBL(pRoot->pLeft) == 1)
pRoot = Retate_LL(pRoot);
else
pRoot = Retate_LR(pRoot);
}
}
else
{
pRoot->pRight = InsertNode(pRoot->pRight, pItem);
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
if (GetBL(pRoot) == -2)
{
if (GetBL(pRoot->pRight) == -1)
pRoot = Retate_RR(pRoot);
else
pRoot = Retate_RL(pRoot);
}
}
return pRoot;
}
}
NODE * DeleteNode(NODE * pRoot, NODE * pNode)
{
if (pRoot == pNode)
{
NODE * p;
if (pNode->pLeft == NULL)
{
p = pNode->pRight;
free(pNode);
return p;
}
else if (pNode->pRight = NULL)
{
p = pNode->pLeft;
free(pNode);
return p;
}
else
{
p = pNode->pLeft;
while (p->pRight != NULL)
p = p->pRight;
pRoot->item = p->item;
pRoot->pLeft = DeleteNode(pRoot->pLeft, p);
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
if (GetBL(pRoot) == -2)
{
if (GetBL(pRoot->pRight) <= 0)
pRoot = Retate_RR(pRoot);
else
pRoot = Retate_RL(pRoot);
}
return pRoot;
}
}
else if (lstrcmp(pNode->item.szHeroName, pRoot->item.szHeroName) < 0)
{
pRoot->pLeft = DeleteNode(pRoot->pLeft, pNode);
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
if (GetBL(pRoot) == -2)
{
if (GetBL(pRoot->pRight) <= 0)
pRoot = Retate_RR(pRoot);
else
pRoot = Retate_RL(pRoot);
}
return pRoot;
}
else
{
pRoot->pRight = DeleteNode(pRoot->pRight, pNode);
pRoot->iHeight = max(GetHeight(pRoot->pLeft), GetHeight(pRoot->pRight)) + 1;
if (GetBL(pRoot) == 2)
{
if (GetBL(pRoot->pLeft) >= 0)
pRoot = Retate_LL(pRoot);
else
pRoot = Retate_LR(pRoot);
}
return pRoot;
}
}
NODE * SearchItem(NODE * pRoot, const ITEM * pItem)
{
if (pRoot == NULL)
return pRoot;
if (lstrcmp(pRoot->item.szHeroName, pItem->szHeroName) < 0)
return SearchItem(pRoot->pRight, pItem);
else if (lstrcmp(pRoot->item.szHeroName, pItem->szHeroName) > 0)
return SearchItem(pRoot->pLeft, pItem);
else
return pRoot;
}
NODE * DeleteItem(NODE * pRoot, const ITEM * pItem)
{
NODE * pNode = SearchItem(pRoot, pItem);
if (pNode == NULL)
return NULL;
return DeleteNode(pRoot, pNode);
}
void VisitItem(NODE * pNode)
{
wprintf(TEXT("%s"), pNode->item.szHeroName);
wprintf(TEXT(" %d$\n"), pNode->item.iGoldPrice);
}
void PreOrder(NODE * pRoot)
{
if (pRoot != NULL)
{
VisitItem(pRoot);
PreOrder(pRoot->pLeft);
PreOrder(pRoot->pRight);
}
}
void InOrder(NODE * pRoot)
{
if (pRoot != NULL)
{
InOrder(pRoot->pLeft);
VisitItem(pRoot);
InOrder(pRoot->pRight);
}
}
void PostOrder(NODE * pRoot)
{
if (pRoot != NULL)
{
PostOrder(pRoot->pLeft);
PostOrder(pRoot->pRight);
VisitItem(pRoot);
}
}
void DestroyTree(NODE * pRoot)
{
if (pRoot == NULL)
return;
DestroyTree(pRoot->pLeft);
DestroyTree(pRoot->pRight);
free(pRoot);
}
BOOL CheckAVL(NODE * pRoot)
{
if (pRoot == NULL)
return TRUE;
if (pRoot->iHeight != CheckHeight(pRoot))
return FALSE;
if (abs(CheckHeight(pRoot->pLeft) - CheckHeight(pRoot->pRight)) >= 2)
return FALSE;
if (!CheckAVL(pRoot->pLeft))
return FALSE;
if (!CheckAVL(pRoot->pRight))
return FALSE;
return TRUE;
}
这些函数在控制台已经依依运行过了,之后通过win32编程利用这些接口做了一个小程序,一个王者荣耀英雄商店。
平台:windows10 vs2015
完整工程代码下载: