zoukankan      html  css  js  c++  java
  • 二叉树

    二叉树系列

     

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

        图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面试集
    标签: 面试二叉树
  • 相关阅读:
    推荐系统中的Graph Model
    Stanford机器学习课程之一——引言
    Sigmoid函数
    高斯RBF核函数中Sigma取值和SVM分离面的影响
    交叉验证(cross validation)
    匿名函数lambda和map函数
    ActionsChains类鼠标事件和Keys类键盘事件
    类、继承和反射
    WebDriverWait类以及类常用的方法
    frame的处理
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3554713.html
Copyright © 2011-2022 走看看