您的位置:首页 > 其它

算法学习-二叉查找树

2016-11-22 15:42 120 查看
typedef struct tagSTreeNode  

{  

    int value;  

    tagSTreeNode* pLeft;  

    tagSTreeNode* pRight;  

  

    tagSTreeNode(int v) : value(v), pLeft(NULL), pRight(NULL){}  

}STreeNode;  

  

typedef void (*VISIT)(int value);  // 定义一个  

  

class CBinaryTree  

{  

private:  

    STreeNode* m_pRoot;  

  

private:  

    void Destory(STreeNode* pRoot);  

    bool _Insert(STreeNode*& pRoot, int value);                  // 递归  

    bool _Insert2(int value);                                    // 非递归  

    void _PreOrder(STreeNode* pRoot, VISIT Visit) const;         // 递归  

    void _PreOrder2(VISIT Visit) const;                          // 非递归  

    void _InOrder(STreeNode* pRoot, VISIT Visit) const;          // 递归  

    void _InOrder2(VISIT Visit) const;                           // 非递归  

    void _InOrder3(VISIT Visit) const;                           // 非递归  

    void _PostOrder(STreeNode* pParent, VISIT Visit) const;      // 递归  

    void _PostOrder2(VISIT Visit) const;                         // 非递归  

    void DeleteChildless(STreeNode* pParent, STreeNode* pNode);  // pNode无子  

    void DeleteSingleSon(STreeNode* pParent, STreeNode* pNode);  // pNode是pParent唯一子节点  

public:  

    CBinaryTree();  

    ~CBinaryTree();  

    bool Insert(int value);  

    bool Delete(int value);  

    STreeNode* Find(int value) const;  

    void PreOrder(VISIT Visit) const;  

    void InOrder(VISIT Visit) const;  

    void PostOrder(VISIT Visit) const;  

};  

二叉查找树(二叉搜索树)是满足一下条件的二叉树:

左子树上的所有结点值均小于根结点值,

右子树上的所有结点值均不小于根结点值,

左右子树也满足上述两个条件。

结点的查找

STreeNode* CBinaryTree::Find(int value) const
{
if (!m_pRoot)
{
return NULL;
}

STreeNode* pNode = m_pRoot;
while (pNode)
{
if (value < pNode->value)
{
pNode = pNode->pLeft;
}
else if (value > pNode->value)
{
pNode = pNode->pRight;
}
else
return pNode;
}
return NULL;
}
结点的插入的递归和非递归版本,可以通过插入函数来创建查找二叉树

bool CBinaryTree::Insert(int value)
{
return _Insert(m_pRoot, value);
}

bool CBinaryTree::_Insert(STreeNode*& pRoot, int value)
{
if (!m_pRoot)
{
pRoot = new STreeNode(value);
return true;
}

if (value < pRoot->value)
return _Insert(pRoot->pLeft, value);
if (value > pRoot->value)
return _Insert(pRoot->pRight, value);
return false;
}

bool CBinaryTree::_Insert2(int value)
{
if (m_pRoot)
{
m_pRoot = new STreeNode(value);
return true;
}
STreeNode* pNode = m_pRoot;
STreeNode* pCur = m_pRoot;
while (pNode)
{
pCur = pNode;
if (value < pNode->value)
pNode = pNode->pLeft;
else if (value > pNode->value)
pNode = pNode->pRight;
else
return false;
}
if (value < pCur->value)
pCur->pLeft = new STreeNode(value);
else if (value > pCur->value)
pCur->pRight = new STreeNode(value);
return true;
}


二叉查找树的删除:

记待删除的结点为p,分三种情况进行处理

p为叶子结点
p为单支结点
p的左子树和右子树均不空

p为叶子结点,直接删除该结点,在修改p的父节点的指针



若p为单支结点(即只有左子树或右子树),则将p的子树与p的父亲结点相连,删除p即可



若p的左子树和右子树均不空,则找到p的直接后继d(p的右孩子的最左子孙),因为d一定没有左子树,所以使用删除单支节点的方法删除d,并让d的父亲结点dp成为右子树的父亲节点;同时用d的值代替p的值;(或者可以找到p的直接前驱x(p的左孩子的最右子孙),x一定没有右子树,所以可以删除x,并让x的父节点成为x的左子树的父亲结点。)



// 删除叶子结点 辅助函数
void CBinaryTree::DeleteChildless(STreeNode* pParent, STreeNode* pNode)
{
if (pNode == m_pRoot)
m_pRoot = NULL;
else if (pParent->pLeft == pNode)
pParent->pLeft = NULL;
else
pParent->pRight = NULL;
delete pNode;
}

// 删除单支结点 辅助函数
void CBinaryTree::DeleteSingleSon(STreeNode* pParent, STreeNode* pNode)
{
STreeNode* pGrandSon = pNode->pLeft ? pNode->pLeft : pNode->pRight;
if (pNode == m_pRoot)
m_pRoot = pGrandSon;
else if (pParent->pLeft == pNode)
pParent->pLeft = pGrandSon;
else
pParent->pRight = pGrandSon;
delete pNode;
}

// 删除结点
bool CBinaryTree::Delete(int value)
{
if (!m_pRoot)
{
return false;
}
STreeNode* pNode = m_pRoot;
STreeNode* pParent = NULL;
while (pNode)
{
if (value < pNode->value)
{
pParent = pNode;
pNode = pNode->pLeft;
}
else if (value > pNode->value)
{
pParent = pNode;
pNode = pNode->pRight;
}
else  // 找到待删除值
break;
}
if (!pNode) // 没有找到
{
return false;
}
if (!pParent)
{
pParent = m_pRoot;
}
if (!pNode->pLeft && !pNode->pRight)
{
DeleteChildless(pParent, pNode);
}
else if (!pNode->pLeft || !pNode->pRight)
{
DeleteSingleSon(pParent, pNode);
}
else
{
STreeNode* pCur = pNode; // 暂存待删除结点
pParent = pNode;
pNode = pNode->pLeft;
while (pNode->pRight)
{
pParent = pNode;
pNode = pNode->pRight;
}
pCur->value = pNode->value;
if (!pNode->pLeft)
{
DeleteChildless(pParent, pNode);
}
else
{
DeleteSingleSon(pParent, pNode);
}
}
return true;
}

二叉树的遍历:

前序遍历

void CBinaryTree::PreOrder(VISIT Visit) const
{
_PreOrder(m_pRoot, Visit);
}

void CBinaryTree::_PreOrder(STreeNode* pRoot, VISIT Visit) const
{
if (pRoot)
{
Visit(pRoot->value);
_PreOrder(pRoot->pLeft, Visit);
_PreOrder(pRoot->pRight, Visit);
}
}

void CBinaryTree::_PostOrder2(VISIT Visit) const
{
if (!m_pRoot)
{
return;
}
std::stack<STreeNode*> s;
s.push(m_pRoot);
STreeNode* pCur;
while (!s.empty())
{
pCur = s.top();
s.pop();
Visit(pCur->value);
if (pCur->pRight)
s.push(pCur->pRight);
if (pCur->pLeft)
s.push(pCur->pLeft);
}
}


中序遍历:二叉树查找树的中序遍历,即为数据的升序过程

void CBinaryTree::InOrder(VISIT Visit) const
{
_InOrder(m_pRoot, Visit);
//_InOrder2(Visit);
}

void CBinaryTree::_InOrder(STreeNode* pRoot, VISIT Visit) const
{
if (pRoot)
{
_InOrder(pRoot->pLeft, Visit);
Visit(pRoot->value);
_InOrder(pRoot->pRight, Visit);
}
}

void CBinaryTree::_InOrder2(VISIT Visit) const
{
std::stack<STreeNode*> s;
STreeNode* pCur = m_pRoot;
while (pCur || !s.empty())
{
while(pCur)
{
s.push(pCur);
pCur = pCur->pLeft;
}
if (!s.empty())
{
pCur = s.top();
s.pop();
Visit(pCur->value);
pCur = pCur->pRight;
}
}
}

void CBinaryTree::_InOrder3(VISIT Visit) const
{
if (!m_pRoot)
{
return;
}

std::stack<std::pair<STreeNode*, int>> s;
s.push(std::make_pair(m_pRoot, 0));
int times;
STreeNode* pCur;
while (!s.empty())
{
pCur = s.top().first;
times = s.top().second;
s.pop();
if (times == 0) // 第一次压栈
{
if (pCur->pRight)
s.push(std::make_pair(pCur->pRight, 0));
s.push(std::make_pair(pCur, 1)); // 第二次压栈
if (pCur->pLeft)
s.push(std::make_pair(pCur->pLeft, 0));
}
else
{
Visit(pCur->value);
}
}
}

后续遍历

void CBinaryTree::PostOrder(VISIT Visit) const
{
_PostOrder(m_pRoot, Visit);
//_PostOrder2(Visit);
}

void CBinaryTree::_PostOrder(STreeNode* pParent, VISIT Visit) const
{
if (pParent)
{
_PostOrder(pParent->pLeft, Visit);
_PostOrder(pParent->pRight, Visit);
Visit(pParent->value);
}
}

void CBinaryTree::_PostOrder2(VISIT Visit) const
{
if (!m_pRoot)
return;
std::stack<std::pair<STreeNode*, int>> s;
s.push(std::make_pair(m_pRoot, 0));
int times;
STreeNode* pCur;
while (!s.empty())
{
pCur = s.top().first;
times = s.top().second;
s.pop();
if (times == 0)
{
s.push(std::make_pair(pCur, 1));
if (pCur->pRight)
{
s.push(std::make_pair(pCur->pRight, 0));
}
if (pCur->pLeft)
{
s.push(std::make_pair(pCur->pLeft, 0));
}
}
else
{
Visit(pCur->value);
}
}
}
根据前序中序,计算后序

如:已知某二叉树的遍历结果如下,求它的后序遍历序列

前序遍历:GDAFEMHZ

中序遍历:ADEFGHMZ

两个步骤:

根据前序中序,构造二叉树

后序遍历二叉树

由前序知道G为二叉树的根结点,根据中序可以得到ADEF为二叉树的左子树,HMZ为二叉树的右子树,下面的依次递归可得到

代码如下

void InPre2Post(const char* pInOrder, const char* pPreOrder, int nLength, char* pPostOrder, int& nIndex)
{
if (nLength <= 0)
return;
if (nLength == 1)
{
pPostOrder[nIndex] = *pPreOrder;
nIndex++;
return;
}
char root = *pPreOrder;
int nRoot = 0;
for (; nRoot < nLength; nRoot++)
{
if (pInOrder[nRoot] == root)
break;
}
InPre2Post(pInOrder, pPreOrder + 1, nRoot, pPostOrder, nIndex);
InPre2Post(pInOrder + nRoot + 1, pPreOrder + nRoot + 1, nLength - (nRoot + 1), pPostOrder, nIndex);
pPostOrder[nIndex] = root;
nIndex++;
}

int _tmain(int argc, _TCHAR* argv[])
{
char pPreOrder[] = "GDAFEMHZ";
char pInOrder[] = "ADEFGHMZ";
int size = sizeof(pInOrder) / sizeof(char);
char* pPostOrder = new char[size];
int nIndex = 0;
InPre2Post(pInOrder, pPreOrder, size - 1, pPostOrder, nIndex);
pPostOrder[size - 1] = 0;
std::cout<<pPostOrder<<std::endl;
delete[] pPostOrder;
system("pause");
return 0;
}
还可以通过中序后序求前序,代码如下
void InPost2Pre(const char* pInOrder, const char* pPostOrder, int nLength, char* pPreOrder, int& nIndex)
{
if (nLength <=0)
return;
if (nLength == 1)
{
pPreOrder[nIndex] = *pPostOrder;
nIndex++;
return;
}
char root = pPostOrder[nLength - 1];
pPreOrder[nIndex] = root;
nIndex++;
int nRoot = 0;
for (; nRoot < nLength; nRoot++)
{
if (pInOrder[nRoot] == root)
break;
}
InPost2Pre(pInOrder, pPostOrder, nRoot, pPreOrder, nIndex);
InPost2Pre(pInOrder + nRoot + 1, pPostOrder + nRoot, nLength - (nRoot + 1), pPreOrder, nIndex);
}

int _tmain(int argc, _TCHAR* argv[])
{
char pPostOrder[] = "AEFDHZMG";
char pInOrder[] = "ADEFGHMZ";
int size = sizeof(pInOrder) / sizeof(char);
char* pPreOrder = new char[size];
int nIndex = 0;
InPre2Post(pInOrder, pPostOrder, size - 1, pPreOrder, nIndex);
pPreOrder[size - 1] = 0;
std::cout<<pPreOrder<<std::endl;
delete[] pPreOrder;
system("pause");
return 0;
}

注:必须知道中序才可求别的,只知道前序后序是无法求出中序的,但是特殊情况是可以

思考:

给定整数数组,判断该数组有无可能是一颗二叉查找树后序遍历的结果。假定数组中没有重复元素,编程如何实现

以下序列中不可能是一颗二叉查找树后序遍历结果的是哪个?答案:B

A、1,2,3,4,5

B、3,5,1,4,2

C、1,2,5,4,3

D、5,4,3,2,1

因为中序的遍历结果就是升序排序所以可以得到中序后进行分析即可得出答案

编程实现如下

bool CanPostOrder(const int* a, int size)
{
if (size <= 1)
return true;

int root = a[size - 1];
int nLeft = 0;
while (nLeft < size - 1)
{
if (a[nLeft] > root)
break;
nLeft++;
}
int nRight = size - 2; // size - 1是根
while (nRight >= 0)
{
if (a[nRight] < root)
break;
nRight--;
}
if (nRight != nLeft - 1)   // 无法根据root分成两部分
return false;

return CanPostOrder(a, nLeft)                          // 左子树
&& CanPostOrder(a + nLeft, size - nLeft - 1);      // 右子树
}

int _tmain(int argc, _TCHAR* argv[])
{
int a[] = {1,2,5,4,3};
bool b = CanPostOrder(a, sizeof(a) / sizeof(int));
std::cout<<b<<endl;
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: