算法学习-二叉查找树
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;
};
二叉查找树(二叉搜索树)是满足一下条件的二叉树:
左子树上的所有结点值均小于根结点值,
右子树上的所有结点值均不小于根结点值,
左右子树也满足上述两个条件。
结点的查找
二叉查找树的删除:
记待删除的结点为p,分三种情况进行处理
p为叶子结点
p为单支结点
p的左子树和右子树均不空
p为叶子结点,直接删除该结点,在修改p的父节点的指针
![](https://img-blog.csdn.net/20161121171514445?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
若p为单支结点(即只有左子树或右子树),则将p的子树与p的父亲结点相连,删除p即可
![](https://img-blog.csdn.net/20161121171632572?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
若p的左子树和右子树均不空,则找到p的直接后继d(p的右孩子的最左子孙),因为d一定没有左子树,所以使用删除单支节点的方法删除d,并让d的父亲结点dp成为右子树的父亲节点;同时用d的值代替p的值;(或者可以找到p的直接前驱x(p的左孩子的最右子孙),x一定没有右子树,所以可以删除x,并让x的父节点成为x的左子树的父亲结点。)
![](https://img-blog.csdn.net/20161121174456876?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
二叉树的遍历:
前序遍历
中序遍历:二叉树查找树的中序遍历,即为数据的升序过程
后续遍历
如:已知某二叉树的遍历结果如下,求它的后序遍历序列
前序遍历:GDAFEMHZ
中序遍历:ADEFGHMZ
两个步骤:
根据前序中序,构造二叉树
后序遍历二叉树
由前序知道G为二叉树的根结点,根据中序可以得到ADEF为二叉树的左子树,HMZ为二叉树的右子树,下面的依次递归可得到
代码如下
注:必须知道中序才可求别的,只知道前序后序是无法求出中序的,但是特殊情况是可以
思考:
给定整数数组,判断该数组有无可能是一颗二叉查找树后序遍历的结果。假定数组中没有重复元素,编程如何实现
以下序列中不可能是一颗二叉查找树后序遍历结果的是哪个?答案: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
因为中序的遍历结果就是升序排序所以可以得到中序后进行分析即可得出答案
编程实现如下
{
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; }
相关文章推荐
- 数据结构和算法分析学习笔记(三)--二叉查找树的懒惰删除(lazy deletion)
- 【算法学习】最优二叉查找树(动态规划)
- 算法学习--二叉查找树
- 【算法学习】最优二叉查找树(动态规划)
- 算法学习 - 平衡二叉查找树实现(AVL树)
- 算法(第四版)学习笔记之java实现二叉查找树
- 数据结构&算法学习笔记 二叉查找树
- 算法学习之数据结构之二叉查找树
- 【算法学习】最优二叉查找树(动态规划)
- 【算法】数据结构与算法分析学习笔记——各类二叉查找树的吐血整理
- 算法学习 - 平衡二叉查找树实现(AVL树)
- 最优二叉查找树详解(算法导论学习笔记)
- 算法学习--二叉查找树
- 【算法学习】最优二叉查找树(动态规划)
- 【算法学习】最优二叉查找树(动态规划)
- 链栈实现算法 - Java 学习笔记(26)
- 学习算法中
- 打造算法学习的实践平台
- (轉貼) STL算法学习 (C/C++) (STL)
- Java 线程同步问题 生产者-消费者 算法实现 -Java学习笔记(29)