二叉树常用算法总结

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

 

  

二叉树常用算法总结
 
   
//
// 二叉树相关算法总结
// 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 ;
}

 

转载于:https://www.cnblogs.com/HappyAngel/archive/2010/10/24/1859875.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值