递归:
1 前序
struct node {
int data;
struct node* left;
struct node* right;
};
void visit(int data)
{
printf("%d ", data);
}
2 中序
void preOrder(struct node* root)
{
if (root == NULL)
return;
visit(root->data);
preOrder(root->left);
preOrder(root->right);
}
3 后序
void postOrder(struct node* root)
{
if (root == NULL)
return;
postOrder(root->left);
postOrder(root->right);
visit(root->data);
}
4, 层序(宽度优先)
void printLevel(struct node *p, int level)
{
if (!p) return;
if (level == 1) {
visit(p->data);
} else {
printLevel(p->left, level-1);
printLevel(p->right, level-1);
}
}
void printLevelOrder(struct node *root)
{
int height = maxHeight(root); //maxHeight计算二叉树高度,如二叉树实例高度为3
for (int level = 1; level <= height; level++) {
printLevel(root, level);
printf("\n");
}
}
当二叉树高度为N时,此时递归层序遍历为最坏情况,时间复杂度为O(N^2)。当二叉树左右子树基本平衡时,时间复杂度为O(N),分析如下:
设访问第K层时间为T(k),则T(k)存在如下的递归公式:
T(k) = 2T(k-1) + c = 2k-1 T(1) + c = 2k-1 + c
当二叉树平衡时,则高度为O(lgN),则总时间为:
T(1) + T(2) + ... + T(lg N)
= 1 + 2 + 22 + ... + 2lg N-1 + c
= O(N)
非递归版本 用栈(队列)实现回溯 (访问完左子树再访问右子树,用栈或者队列记录已经访问过的节点访问信息,方便后来回溯)
1 先序//与二叉树镜像的做法类似
-
void preOrder1(TNode* root)
-
{
-
Stack S;
-
while ((root != NULL) || !S.empty())
-
{
-
if (root != NULL)
-
{
-
Visit(root);
-
S.push(root);
-
root = root->left;
-
}
-
else
-
{
-
root = S.pop();
-
root = root->right;
-
}
-
}
-
}
-
-
-
void preOrder2(TNode* root)
-
{
-
if ( root != NULL)
-
{
-
Stack S;
-
S.push(root);
-
while (!S.empty())
-
{
-
TNode* node = S.pop();
-
-
Visit(node);
-
S.push(node->right);
-
S.push(node->left);
-
}
-
}
-
}
2 中序
-
-
void InOrder1(TNode* root)
-
{
-
Stack S;
-
while ( root != NULL || !S.empty() )
-
{
-
while( root != NULL )
-
{
-
S.push(root);
-
root = root->left;
-
}
-
if ( !S.empty() )
-
{
-
root = S.pop();
-
Visit(root->data);
-
root = root->right;
-
}
-
}
-
}
3 后序
因为中序遍历节点序列有一点的连续性,而后续遍历则感觉有一定的跳跃性。先左,再右,最后才中间节点;访问左子树后,需要跳转到右子树,右子树访问完毕了再回溯至根节点并访问之。这种序列的不连续造成实现前面先序与中序类似的第1个与第3个版本比较困难。但是按照第2个思想,直接来模拟递归还是非常容易的。如下:
-
-
void PostOrder(TNode* root)
-
{
-
Stack S;
-
if( root != NULL )
-
{
-
S.push(root);
-
}
-
while ( !S.empty() )
-
{
-
TNode* node = S.pop();
-
if ( node->bPushed )
-
{
-
Visit(node);
-
}
-
else
-
{
-
-
node-?bPushed=true;
-
s.push(node);
-
if ( node->right != NULL )
-
{
-
node->right->bPushed = false;
-
S.push(node->right);
-
}
-
if ( node->left != NULL )
-
{
-
node->left->bPushed = false;
-
S.push(node->left);
-
}
-
}
-
}
-
}
和中序遍历的第2个版本比较,仅仅只是把左孩子入栈和根节点入栈顺序调换一下;这种差别就跟递归版本的中序与后序一样。
4.层序遍历
这个很简单,就不说。
-
-
void LevelOrder(TNode *root)
-
{
-
Queue Q;
-
Q.push(root);
-
-
while (!Q.empty())
-
{
-
node = Q.front();
-
Visit(node);
-
-
if (NULL != node->left)
-
{
-
Q.push(node->left);
-
}
-
if (NULL != node->right)
-
{
-
Q.push(node->right);
-
}
-
}
-
}
结一下:
用栈来实现比增加指向父节点指针回溯更方便;
采用第一个思想,就是跟踪指针移动 用栈保存中间结果的实现方式,先序与中序难度一致,后序很困难。先序与中序只需要修改一下访问的位置即可。
采用第二个思想,直接用栈来模拟递归,先序非常简单;而中序与后序难度一致。先序简单是因为节点可以直接访问,访问完毕后无需记录。而中序与后序时,节点在弹栈后还不能立即访问,还需要等其他节点访问完毕后才能访问,因此节点需要设置标志位来判定,因此需要额外的O(n)空间。
阅读(819) | 评论(0) | 转发(0) |