算法学习-二叉查找树

  1. typedef struct tagSTreeNode  
  2. {  
  3.     int value;  
  4.     tagSTreeNode* pLeft;  
  5.     tagSTreeNode* pRight;  
  6.   
  7.     tagSTreeNode(int v) : value(v), pLeft(NULL), pRight(NULL){}  
  8. }STreeNode;  
  9.   
  10. typedef void (*VISIT)(int value);  // 定义一个  
  11.   
  12. class CBinaryTree  
  13. {  
  14. private:  
  15.     STreeNode* m_pRoot;  
  16.   
  17. private:  
  18.     void Destory(STreeNode* pRoot);  
  19.     bool _Insert(STreeNode*& pRoot, int value);                  // 递归  
  20.     bool _Insert2(int value);                                    // 非递归  
  21.     void _PreOrder(STreeNode* pRoot, VISIT Visit) const;         // 递归  
  22.     void _PreOrder2(VISIT Visit) const;                          // 非递归  
  23.     void _InOrder(STreeNode* pRoot, VISIT Visit) const;          // 递归  
  24.     void _InOrder2(VISIT Visit) const;                           // 非递归  
  25.     void _InOrder3(VISIT Visit) const;                           // 非递归  
  26.     void _PostOrder(STreeNode* pParent, VISIT Visit) const;      // 递归  
  27.     void _PostOrder2(VISIT Visit) const;                         // 非递归  
  28.     void DeleteChildless(STreeNode* pParent, STreeNode* pNode);  // pNode无子  
  29.     void DeleteSingleSon(STreeNode* pParent, STreeNode* pNode);  // pNode是pParent唯一子节点  
  30. public:  
  31.     CBinaryTree();  
  32.     ~CBinaryTree();  
  33.     bool Insert(int value);  
  34.     bool Delete(int value);  
  35.     STreeNode* Find(int value) const;  
  36.     void PreOrder(VISIT Visit) const;  
  37.     void InOrder(VISIT Visit) const;  
  38.     void PostOrder(VISIT Visit) const;  
  39. };  


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

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

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

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

结点的查找

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,分三种情况进行处理

  1. p为叶子结点
  2. p为单支结点
  3. 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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值