Hdu 3999 复习二叉树的先序遍历 递归+非递归

搜索二叉树,BST, 是二叉树的一种。特点是,

(1).左孩子关键字比根小,右孩子关键字比根大。

(2).根的左孩子和右孩子,如果不为空,也都是搜索二叉树。

( 我一般这么想的,如有错误,敬请指正。)

左孩子比根小,右孩子比根大,好处就是很方便查找,思想就是二分,和折半查找(二分)一样。查找是计算机中十分重要的一个操作,在海量的数据面前,如果一个一个遍历就太麻烦了,但是利用二叉树可以降低查找的复杂度,1000000的数据,储存在完美的二叉树中,也就20层的样子,大大降低了查找的复杂度。

而且搜索二叉树的中序遍历就可以得到一个有序的序列,因为一直保持着 左孩子  <  根  < 右孩子 ,按照这个顺序递归。

下面来看一道搜索二叉树的例子:

As we know,the shape of a binary search tree is greatly related to the order of keys we insert. To be precisely: 
1.  insert a key k to a empty tree, then the tree become a tree with 
only one node; 
2.  insert a key k to a nonempty tree, if k is less than the root ,insert 
it to the left sub-tree;else insert k to the right sub-tree. 
We call the order of keys we insert “the order of a tree”,your task is,given a oder of a tree, find the order of a tree with the least lexicographic order that generate the same tree.Two trees are the same if and only if they have the same shape. 
Input
There are multiple test cases in an input file. The first line of each testcase is an integer n(n <= 100,000),represent the number of nodes.The second line has n intergers,k1 to kn,represent the order of a tree.To make if more simple, k1 to kn is a sequence of 1 to n. 
Output
One line with n intergers, which are the order of a tree that generate the same tree with the least lexicographic. 
Sample Input
4

1 3 4 2
Sample Output
1 3 2 4

这道题目,给出建立一棵搜索二叉树的序列,要求出建立这个二叉树的,按照字典序的最小序列。我们在建立搜索二叉树的时候,左右孩子的插入顺序是不确定的,可以先插左子树,也可以先插右子树,这就导致了序列不一定按照字典序。如果要按照字典序,得到一模一样的二叉树,就需要先插入左子树,再插入右子树,因为左子树一定小于右子树。这就是二叉树的先序遍历,回顾一下二叉树的基础。

#include <iostream>
#include <cstdio>
using namespace std ;
struct Node{
	int weight ;      // 关键字,很多时候是权重
	Node *lchild , *rchild ;
	Node ( int _weight ){
		weight = _weight ;
		lchild = rchild = NULL ;
	}
	void Destroy( Node *&root ){ // 每次重建一棵二叉树,释放原来的内存。
		if( ! root )  return ; 
		if( root->lchild ) Destroy( root->lchild ) ;
		if( root->rchild ) Destroy( root->rchild ) ;
		delete root ; root = NULL ;
	}
} *root , *stack[100005] ;

void insert ( Node *&root , int weight ){    // 建立搜索二叉树
	if( !root ) { root = new Node( weight ) ; return ; }
	if( weight < root->weight ) insert( root->lchild , weight ) ; 
	else insert( root->rchild , weight ) ;
}

void Display( Node *cur ){            // 先序遍历的递归形式
	if( !cur ) return ;
	printf( cur == root ? "%d" : " %d" , cur->weight ) ; // 这里注意根的前面,没有空格,否则会 Presentation Error
	if( cur->lchild ) Display( cur->lchild ) ;
	if( cur->rchild ) Display( cur->rchild ) ;
}

int main(){
	int n , weight , i ;
	root = NULL ;
	while( ~scanf( "%d" , &n ) ){
		for( i = 1 ; i <= n ; ++i )
			scanf( "%d" , &weight ) , insert( root , weight ) ;
		Display( root ) ;   // 打印先序序列
		cout << endl ;
		root->Destroy( root ) ;
	}
	root = NULL ;
	return 0 ;
}
在这里也回顾一下,二叉树先序遍历的非递归形式:

利用一个栈,从根节点往左子树一路打印左子树,最左边的访问完了,就检查最左边的节点是否有右孩子,有右孩子就访问右孩子;访问结束之后,pop , 再检查当前栈顶节点(之前被压栈的左子树)的右子树;重复 pop , 直到栈为空 而且 二叉树访问完毕。

void Pre_visit( Node *root ){
	Node *ptr = root ;
	while( top || ptr ){    // 之前压栈的节点的右子树尚未访问 ;以当前访问的节点的树还没访问完
		while( ptr ){        
			stack[top++] = ptr ;    // stack 是一个 Node* 类型的数组,做栈,以下都是
			printf( ptr == root ? "%d" : " %d" , ptr->weight ) ;
			ptr = ptr->lchild ;
		}
		if( top )  ptr = stack[--top] , ptr = ptr->rchild ;   // 当前栈未空,之前压栈的左子树的右孩子还没访问
	}
	printf( "\n" ) ;
	ptr = NULL ;
}
二叉树中序遍历的非递归形式:

上面的先序遍历是遇到左子树就打印,一路到底,因为先序遍历的访问顺序是 根 -> 左子树 -> 右子树 ,从根节点开始,每一个压栈的节点所 “掌管” 的树都是以这个节点为根,之前已经打印过了,左子树也刚访问完,再检验右子树是否可以访问;

中序遍历的顺序是 左子树 -> 根 -> 右子树,之前压栈的节点都是某些 "树" 的根节点,不能一开始就访问,要先走到最左边的节点,然后逐渐 pop , 再访问原来压栈的众多“根节点” , 然后和先序遍历一样,检验右子树是否访问过了。

void Mid_visit( Node *root ){
	Node *ptr = root ;
	while( top || ptr ){
		while( ptr )  stack[top++] = ptr , ptr = ptr->lchild ;
		if( top ){
			ptr = stack[--top] ;
			printf( ptr == root ? "%d" : " %d" , ptr->weight ) ;
			ptr = ptr->rchild ;
		}
	}
	printf( "\n" ) ;
	ptr = NULL ;
}
二叉树后序遍历的非递归形式:

后序遍历是 左孩子 -> 右孩子 -> 根。也就是在访问之前压栈的“根节点”之前要检验它的左孩子和右孩子是否都访问过了。和中序遍历一样的是,先访问到最左边,然后逐渐 pop , 就能保证最先访问左子树,然后 pop 的过程中,当前的栈顶元素就是“根” , 要先检验它的右子树是否之前访问过了。这里我用一个 *Pre 指针保存上一次访问的节点。检验当前节点的右子树,有两种情况:

1. 没有右子树,就是右子树为空。

2. 右子树在之前访问过了,就可以访问当前节点了。

void Post_visit( Node *root ){
	Node *ptr = root , *Pre = NULL , *cur = NULL ;
	while( top || ptr ){
		while( ptr )  stack[top++] = ptr , ptr = ptr->lchild ;  // 一路保存左边的节点
		if( top ){
			cur = stack[top-1] ;    // 这里要先取栈顶,也就是当前元素 , top-1 是因为栈的节点从 0 开始存储的
			if( cur->rchild == NULL || cur->rchild == Pre ){   // 右子树为空 ;右子树刚才访问过
				printf( cur == root ? "%d" : "%d " , cur->weight ) ;   // 才可以访问当前结点
				Pre = cur ;         // 更新  Pre 
				top-- ;             // 当前结点访问过了,pop
			}
			else ptr = cur->rchild ; // 右子树存在而且没访问过 , 存储当前元素的指针 ptr 移向右孩子
		}
	}
	printf( "\n" ) ;
	ptr = Pre = cur = NULL ;
}
如有错误,敬请指正。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值