Fork me on GitHub
二叉树

二叉树系列

 

心浮气躁,纵然才俊终仲永。最近看了不少面试题,总觉得题解太过粗糙,在此试解几题,打发无聊。内容包括各种关于二叉树的面试题:

    图1为二叉树示例图,二叉树的数据结构如下:

typedef struct _BinaryTree{
int value;
struct _BinaryTree *left;
struct _BinaryTree *right;
}BinaryNode, *BinaryTree;
 

image_thumb3

图1 二叉树

    二叉树的遍历基本上是解决所有二叉树面试题的源,在解下列题时,都将极力不使用递归(或给出非递归解),相信递归之简单与之弊病谁都了然,本文将从遍历开始……

1 二叉树的先序遍历

    对于示例图1,有输出:1 2 4 8 9 5 3 6 7。

/**
* @Synopsis pre_order_traverse 非递归先序遍历二叉树
*
* @Param T 二叉树根指针
* @Param visit
*
* @Returns 0成功
*/
int pre_order_traverse(BinaryTree T, void (*visit)(BinaryTree)){
if(NULL == T){
return 0;
}
stack<BinaryTree> st;
BinaryTree bt = NULL;
st.push(T);
while(!st.empty()){
bt = st.top();
st.pop();
visit(bt);//访问根
if(NULL != bt->right){
st.push(bt->right);//先入右节点
}
if(NULL != bt->left){
st.push(bt->left);//后入左节点
}
}
return 0;
}

2 二叉树的中序遍历

    对于示例图1,输出结果为:8 4 9 2 5 1 6 3 7。

/**
* @Synopsis in_order_traverse 非递归中序遍历二叉树
*
* @Param T 二叉树根指针
* @Param visit
*
* @Returns 0成功
*/
int in_order_traverse(BinaryTree T, void (*visit)(BinaryTree)){
if(NULL == T){
return 0;
}
stack<BinaryTree> st;
st.push(T);
BinaryTree bt = T->left;
while(!st.empty() || bt != NULL){
//先访问完左子树
while(bt != NULL){
st.push(bt);
bt = bt->left;
}
//访问栈底节点
bt = st.top();
visit(bt);
st.pop();
bt = bt->right;
}
}

3 二叉树的后序遍历

    对于示例图1,输出结果为:8 9 4 5 2 6 7 3 1。

/**
* @Synopsis post_order_traverse 非递归后序遍历二叉树
*
* @Param T 二叉树根指针
* @Param visit
*
* @Returns 0成功
*/
int post_order_traverse(BinaryTree T, void (*visit)(BinaryTree)){
if(NULL == T){
return 0;
}
stack<BinaryTree> st;
st.push(T);
BinaryTree bt = T->left;
BinaryTree visited = NULL;//已经访问了的最后节点
while(!st.empty()){
while(bt != NULL){//穷尽左子树
st.push(bt);
bt = bt->left;
}
bt = st.top();
if(NULL == bt->right || visited == bt->right){
visit(bt);
st.pop();
visited = bt;
bt = NULL;
}
else{
bt = bt->right;
}
}
return 0;
}

4 二叉树深度优先遍历

/**
* @Synopsis depth_traverse 二叉树深度优先遍历
*
* @Param T 树根
* @Param visit
*
* @Returns 0成功,其它失败
*/
int depth_traverse(BinaryTree T, void (*visit)(BinaryTree)){
if(NULL == T){
return 0;
}
stack<BinaryTree> st;
st.push(T);
BinaryTree node;
while(!st.empty()){
node = st.top();
visit(node);
st.pop()
//右子树先入栈
if(node->right != NULL){
st.push(node->right);
}
if{
st.push(node->left);
}
}
return 0;
}

5 二叉树宽度优先遍历

/**
* @Synopsis width_traverse 二叉树宽度优先搜索
*
* @Param T 树根
* @Param visit
*
* @Returns 0成功,其它失败
*/
int width_traverse(BinaryTree T, void (*visit)(BinaryTree)){
if(NULL == T){
return 0;
}
queue<BinaryTree> qu;
qu.push(T);
BinaryTree node;
while(!qu.empty()){
node = qu.front();
visit(node);
qu.pop();
if(node->left != NULL){
qu.push(node->left);
}
if(node->right != NULL){
qu.push(node->right);
}
}
return 0;
}

6 二叉树节点标签

    为树上的每一个结点打上标签,打标签的原则是形状一模一样的子树的根结点的标签需要相同,如下图2所示。

image_thumb

图2 二叉树节点标签
应用后序遍历,后序遍历的每个访问的每个节点,均已经知道该节点的类型。
/**
* @Synopsis mark_node节点标记
*
* @Param bt
* @Param mark_map 去重map
*/
void mark_node(BinaryTree bt, map<string, int> &mark_map){
int left = 0;
int right = 0;
if(bt->left != NULL){
left = bt->left->value;
}
if(bt->right != NULL){
right = bt->right->value;
}
char markStr[20];
snprintf(markStr, 20, "%d_%d", left, right);
string s(markStr);
map<string, int>::iterator it = mark_map.find(s);
if(it != mark_map.end()){
bt->value = it->second;
}
else{
int mark_value = mark_map.size() + 1;
mark_map.insert(pair<string, int>(s, mark_value));
bt->value = mark_value;
}
}

/**
* @Synopsis mark_binary_tree 二叉树节点类型标记, 非递归后序遍历
*
* @Param T 根节点指针
* @Param mark_node 标记函数指针
*
* @Returns 0成功
*/
int mark_binary_tree(BinaryTree T, void (*mark_node)(BinaryTree, map<string, int> &mark_map)){
if(NULL == T){
return 0;
}
map<string, int> mark_map;
stack<BinaryTree> st;
st.push(T);
BinaryTree bt = T->left;
BinaryTree visited = NULL;//已经访问了的最后节点
while(!st.empty()){
while(bt != NULL){//穷尽左子树
st.push(bt);
bt = bt->left;
}
bt = st.top();
if(NULL == bt->right || visited == bt->right){
mark_node(bt, mark_map);
st.pop();
visited = bt;
bt = NULL;
}
else{
bt = bt->right;
}
}
return 0;
}

    算法复杂度近似为后序遍历二叉树的复杂度o(n),std::map将引致o(mlog(m))的类标记查找复杂度(m为树的类型数),可用__gnu_cxx::hash_map替代。

7 二叉查找树转有序列表。

    输入一棵二叉查找树,将该二叉查找树转换成一个排序的双向链表,如下图1-1示:

image_thumb1图1-1

    由于二叉查找树的中序遍历即有序,则此题可完全依中序遍历解。

typedef struct _BinaryTree{
int value;
struct _BinaryTree *left;
struct _BinaryTree *right;
}BinaryNode, *BinaryTree;



/**
* @Synopsis tree_to_list 二叉排序树转双向链表

* @Param T 二叉查找树根

* @Returns 双向链表头
*/
BinaryTree tree_to_list(BinaryTree T){
if(NULL == T){
return T;
}
BinaryTree node = NULL;
BinaryTree current_node = T;
BinaryTree head = NULL;
std::stack<BinaryTree> st;
st.push(T);
while(!st.empty()){
while(NULL != current_node->left){
st.push(current_node->left);
current_node = current_node->left;
}
current_node = st.top();
if(NULL == node){
head = current_node;
node = current_node;
}
else{
node->right = current_node;
current_node->left = node;
node = current_node;
}
st.pop();
current_node = current_node->right;
}
return head;
}

只能永远把艰辛的劳动看作是生命的必要;即使没有收获的指望,也能心平气和的继续耕种。
 
分类: IT面试集
标签: 面试二叉树
posted on 2014-02-18 18:17  HackerVirus  阅读(308)  评论(0编辑  收藏  举报