二叉树常用算法总结

  最近在复习算法,做了一下编程之美上面关于二叉树的三道题,感觉顺便就把二叉树总结下算了,于是就有了这篇文章,这些算法有些是自己思考出来的,比如重建二叉树,有些是以前的知识还有些是看了网上其它大牛总结而成,现在贴出来分享,希望大家指正。感觉算法的学习重要的不是算法本身而是算法背后的思想,就是一些本质上的东西,或者说思考的过程,这些才是真正重要的。对于二叉树来说,最重要的是它的递归特性,这是由它的定义决定的。写了这么多二叉树的算法,思考了下,对于二叉树的相关问题,首先要考虑的就是是否能应用它的递归性质。

 

  

二叉树常用算法总结
//////////////////////////////////////////////////////////////////////////
// 二叉树相关算法总结
// 2010.10.22
//
// By HappyAngel
// 这里的算法部分来自自己的思考原创,部分来自对网上的总结,收获不少。
//////////////////////////////////////////////////////////////////////////

#include
<iostream>
#include
<stack>
#include
<cstdlib>
#include
<queue>

using namespace std;


typedef
struct TreeNode
{
TreeNode
* pLeftNode;
TreeNode
* pRightNode;
char nodeName;
bool flag; //for post-order traverse
int level; //for level traverse
}TreeNode, * PTreeNode;


//recurisively create binary tree
PTreeNode CreateBinaryTree(void)
{
char c;
cin
>>c;

PTreeNode pRoot;

if('#' == c)
{
return NULL;
}
else
{
pRoot
= new TreeNode;
pRoot
->nodeName = c;
pRoot
->flag = false;
pRoot
->level = 0;
pRoot
->pLeftNode = CreateBinaryTree();
pRoot
->pRightNode = CreateBinaryTree();
}

return pRoot;
}

void DeleteTree(PTreeNode pRoot)
{
if(NULL != pRoot)
{
DeleteTree(pRoot
->pLeftNode);
DeleteTree(pRoot
->pRightNode);
delete pRoot;
}
}

void access(const TreeNode& node)
{
cout
<<node.nodeName<<" ";
}

//recursely preorder
void PreOrderTraverse(PTreeNode pNode)
{
if(pNode != NULL)
{
access(
*pNode);
PreOrderTraverse(pNode
->pLeftNode);
PreOrderTraverse(pNode
->pRightNode);
}

}

//recursively inorder
void InOrderTraverse(PTreeNode pNode)
{
if(pNode != NULL)
{
InOrderTraverse(pNode
->pLeftNode);
access(
*pNode);
InOrderTraverse(pNode
->pRightNode);
}
}

//recursely postorder
void PostOrderTraverse(PTreeNode pNode)
{
if(pNode != NULL)
{
PostOrderTraverse(pNode
->pLeftNode);
PostOrderTraverse(pNode
->pRightNode);
access(
*pNode);
}
}
//////////////////////////////////////////////////////////////////////////
//非递归前序遍历二叉树
//思路:一样,要用堆栈保存左右子树,保存顺序为先右后左。
//
//////////////////////////////////////////////////////////////////////////
void NonRecursivePreOrder(PTreeNode pNode)
{
stack
<PTreeNode> s;

PTreeNode p
= pNode;
while(NULL != p || !s.empty())
{
access(
*p);
if(NULL != p->pRightNode)
s.push(p
->pRightNode);
if(NULL != p->pLeftNode)
s.push(p
->pLeftNode);

if(!s.empty())
{
p
= s.top();
s.pop();
}
else
p
= NULL;
}

}
//////////////////////////////////////////////////////////////////////////
// 非递归中序遍历二叉树
//思路:首先,由于是去掉了递归,必须保存以前访问过的中间节点,所以必须有堆栈。
//其次根据中序遍历的定义,是左中右,所以要找到最左边的叶子节点,并用堆栈保存根节点。
//访问完左节点后(NULL也算一个左节点),出栈并访问中间节点,最后访问右节点,并循环之。
//结束条件:堆栈为空或者指针为空,之所以加入指针为空是为了在最开始的时候不必把根节点压入堆栈。
//可以对所有节点一视同仁,简化判断。因此在中间要判断堆栈不为空才弹。
//////////////////////////////////////////////////////////////////////////
void NonRecursiveInOrder(PTreeNode pNode)
{
stack
<PTreeNode> s;

PTreeNode p
= pNode;
while(NULL != p || !s.empty())
{
while(NULL != p)
{
s.push(p);
p
= p->pLeftNode;
}

if(!s.empty())
{
p
= s.top();
s.pop();
access(
*p);
p
= p->pRightNode;
}
}
}

//////////////////////////////////////////////////////////////////////////
//非递归后序遍历二叉树
//用一个标记标志是否是从右子树回到中间节点,这样,还是先访问左,再访问右,
//最后访问中间。
//
//
//////////////////////////////////////////////////////////////////////////
void NonRecursivePostOrder(PTreeNode pNode)
{
stack
<PTreeNode> s;

PTreeNode p
= pNode;
while(NULL != p || !s.empty())
{
while(NULL != p)
{
s.push(p);
p
= p->pLeftNode;
}

if(!s.empty())
{
p
= s.top();
if(p->flag)
{
access(
*p);
s.pop();
//访问完了再出栈
p = NULL; //右子树已经访问
}
else
{
p
->flag = true;
p
= p->pRightNode; //访问右子树
}
}
}
}

//////////////////////////////////////////////////////////////////////////
//根据先序和中序遍历的结果重建二叉树 此题原自编程之美3.9
//输入:pPreOrder, pInOrder, nTreeLen
//输出:pRoot
//算法思想:先序集合不变,中序集合是变化的,每次递归做三件事:找根节点 FindRoot ,即查找先序集合中第一个
//同时又在中序集合的节点;第二步,根据根节点切分中序集合 Split ,实质上是把中序分为左右子树集合
//;第三步,递归构建左子树与右子树;
//二叉树最本质的特性就是递归,这是由它的定义决定的,所以递归是解决二叉树问题的神器。
//////////////////////////////////////////////////////////////////////////
const int maxLen = 20;
void Split(char c, char* pInOrder, char** pInOrder1, char** pInOrder2)
{
*pInOrder1 = new char[maxLen];
*pInOrder2= new char[maxLen];
memset(
*pInOrder1,0,sizeof(char)*maxLen);
memset(
*pInOrder2,0,sizeof(char)*maxLen);

int j1=0;
int j2=0;
int i=0;
for(; NULL != pInOrder[i]; i++)
{
if(c != pInOrder[i])
{
(
*pInOrder1)[j1] = pInOrder[i];
j1
++;
}
else
break;
}
i
++;
for(; NULL != pInOrder[i]; i++)
{
(
*pInOrder2)[j2] = pInOrder[i];
j2
++;
}
}

char FindRoot(char* pPreOrder, char* pInOrder)
{
char c = NULL;
for(int i=0; NULL != pPreOrder[i]; i++)
{
for(int j=0; NULL != pInOrder[j]; j++)
{
if(pPreOrder[i] == pInOrder[j])
{
c
= pPreOrder[i];
return c;
}
}
}
return c;
}

PTreeNode CreateNode(
char c)
{
PTreeNode p
= new TreeNode;
p
->nodeName = c;
p
->pLeftNode = NULL;
p
->pRightNode = NULL;

return p;
}

PTreeNode Rebuild2(
char* pPreOrder, char* pInOrder)
{
if(0 == strlen(pInOrder))
{
return NULL;
}

//找根节点
char c = FindRoot(pPreOrder,pInOrder);
PTreeNode p
= CreateNode(c);
char* pInOrder1 = NULL;
char* pInOrder2 = NULL;
//根据根节点切分中序集合
Split(c,pInOrder,&pInOrder1,&pInOrder2);
//重建左子树
p->pLeftNode = Rebuild2(pPreOrder,pInOrder1);
//重建右子树
p->pRightNode = Rebuild2(pPreOrder, pInOrder2);

delete pInOrder1;
delete pInOrder2;
return p;
}

void Rebuild(char* pPreOrder, char* pInOrder, int nTreeLen, PTreeNode* pRoot)
{
*pRoot = Rebuild2(pPreOrder,pInOrder);
}
//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
//层次遍历二叉树
//思路:应用队列即可轻松实现,在实现上保持与其他遍历代码的一致性,主要体现在
//循环终止条件上
//
//////////////////////////////////////////////////////////////////////////
void LevelTraverse(PTreeNode root)
{
queue
<PTreeNode> q;
PTreeNode p
= root;

while(NULL != p || !q.empty())
{
if(!q.empty())
{
p
= q.front();
q.pop();
}
access(
*p);
if(NULL != p->pLeftNode)
{
q.push(p
->pLeftNode);
}
if(NULL != p->pRightNode)
{
q.push(p
->pRightNode);
}
p
= NULL; //to make the program ends after accessing the last node
}
}


//////////////////////////////////////////////////////////////////////////
//非递归打印二叉树中某层次的节点
//
//
//////////////////////////////////////////////////////////////////////////
int PrintNodeAtLevel(PTreeNode root, int level)
{
queue
<PTreeNode> q;
PTreeNode p
= root;

if(root == NULL || level < 0)
return 0;

int l = 0;
while(NULL != p || !q.empty())
{
if(!q.empty())
{
p
= q.front();
q.pop();
}
if(level == p->level)
{
access(
*p);
}
if(NULL != p->pLeftNode)
{
q.push(p
->pLeftNode);
p
->pLeftNode->level = p->level+1;
}
if(NULL != p->pRightNode)
{
q.push(p
->pRightNode);
p
->pRightNode->level = p->level+1;
}
p
= NULL; //to make the program ends after accessing the last node
}
return 1;
}

///////////////////////////////////////////////////////////////////////////
//递归按层打印二叉树,此法把记录层次的任务交给函数本身,乃递归的妙用
//
//////////////////////////////////////////////////////////////////////////
int RecursivePrintAtLevel(PTreeNode root, int level)
{
if(level < 0 || root == NULL)
return 0;

if(level == 0)
{
access(
*root);
return 1;
}

return RecursivePrintAtLevel(root->pLeftNode,level-1) + RecursivePrintAtLevel(root->pRightNode,level-1);
}

//////////////////////////////////////////////////////////////////////////
//递归求二叉树的深度
//
//
//////////////////////////////////////////////////////////////////////////
int RecursiveGetTreeDepth(PTreeNode root)
{
if(NULL == root)
return 0;
else
{
int l = RecursiveGetTreeDepth(root->pLeftNode);
int r = RecursiveGetTreeDepth(root->pRightNode);
return (l > r ? l+1 : r+1);
}
}

int main(void)
{
PTreeNode p
= CreateBinaryTree();


DeleteTree(p);
getchar();
//skip enter
getchar();//pause
return 0;
}

 

posted @ 2010-10-24 20:42  HappyAngel  阅读(3917)  评论(1编辑  收藏  举报