面试题精选(83):二叉树相关的问题

涉及题目描述:(BST—binary search tree,BT—binary tree)

1、BST构建

2、BT的层次遍历

3、BST的LCA(最近公共祖先)求解

4、BST 转化为有序的double link list

5、BT的LCA求解(将LCA问题转化为RMQ(上一篇blog涉及)问题求解)

 

相关分析:

 

1、2问题简单,不予分析

 

3、

对于BST,它有一个重要的性质:节点的左子树包含且只包含比它小的节点,
右子树包含且只包含比它大的节点。因此LCA的值一定在这两个节点之间。

1. 如果u<root && v<root,则去左子树搜索;

2. 如果u>root && v>root,则去右子树搜索;

3. 否则,root就是LCA

 

4、

左、右子树分别转化为double link list,然后将左子树list、root、右子树list连接成一个list,递归实现

 

5、

一、最近公共祖先(Least Common Ancestors)

对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。
这里给出一个LCA的例子:


对于T=<V,E>
V={1,2,3,4,5}
E={(1,2),(1,3),(3,4),(3,5)}

则有:
LCA(T,5,2)=1
LCA(T,3,4)=3
LCA(T,4,5)=3

 

二、RMQ问题(Range Minimum Query)

RMQ问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小值下标。这时一个RMQ问题的例子:


对数列:5,8,1,3,6,4,9,5,7 有:
RMQ(2,4)=3
RMQ(6,9)=6

RMQ问题与LCA问题的关系紧密,可以相互转换,相应的求解算法也有异曲同工之妙。


下面给出LCA问题向RMQ问题的转化方法。

对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的值存入数组E最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),就等价于求 E[RMQ(D,R[u],R[v])],(R[u]<R[v])。

 

例如,对于第一节的例,求解步骤如下:
数列E[i]为:1,2,1,3,4,3,5,3,1
R[i]为:1,2,4,5,7
D[i]为:0,1,0,1,2,1,2,1,0

于是有:
LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1
LCA(T,3,4) = E[RMQ(D,R[3],R[4])] = E[RMQ(D,4,5)] = E[4] = 3
LCA(T,4,5) = E[RMQ(D,R[4],R[5])] = E[RMQ(D,5,7)] = E[6] = 3

易知,转化后得到的数列长度为树的结点数的两倍加一,所以转化后的RMQ问题与LCA问题的规模同次。

代码:

#include <functional>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <fstream>
#include <vector>


using namespace std;

 

#define pow2(n) (1 << (n))
#define MAXN   10000
#define mmin(seq, a, b)  ((seq[a] < seq[b]) ? (a) : (b))

 

//// DP status
int fij[MAXN][100];

//// for output fij
template <typename T>
void Dp_st_print(T seq[], int n)
{
    //// 输出待RMQ的序列
    for(int i = 0; i < n; i++)
        cout<<seq[i]<<"/t";
    cout<<endl;
    //// 输出状态f[][]
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; i + (1<<j) - 1 < n; j++)
        {
            cout<<"fij["<<i<<", "<<j<<"] = "<<fij[i][j]<<"/t";
        }
        cout<<endl;
    }
    cout<<endl;
}


// RMQ(Range Minimum/Maximum Query)问题:

// 预处理:
// sparse table算法(st)
template <typename T>
void st(T seq[], int n)
{
    memset(fij, 0, 100 * MAXN * sizeof(int));
    int k = (int)(log((double)n) / log(2.0));
    ////初始状态
    for(int i = 0; i < n; i++)
        fij[i][0] = i;
    ////递推计算状态
    for(int j = 1; j <= k; j++)
    {
        for(int i = 0; i + (1 << j) - 1 < n; i++)
        {
            //
            int m = i + (1 << (j - 1));
            //fij[i][j] = seq[fij[i][j - 1]] < seq[fij[m][j - 1]] ? fij[i][j - 1] : fij[m][j - 1];
            fij[i][j] = mmin(seq, fij[i][j - 1], fij[m][j - 1]);
        }
    }
}


// 查询:
template <typename T>
int RMQ(T seq[], int i, int j)
{
    //// 用对2去对数的方法求出k
    int k = (int)(log(double(j - i + 1)) / log(2.0));
    ////
    //int t = seq[fij[i][k]] < seq[fij[j - (1 << k) + 1][k]] ? fij[i][k] : fij[j - (1 << k) + 1][k];
    int t = mmin(seq, fij[i][k], fij[j - (1 << k) + 1][k]);
    return t;
    //cout<<"RMQ("<<i<<", "<<j<<") = "<<seq[t]<<" @ pos("<<t<<")/n";
}

 

////////---------------------------------------------------

 

typedef struct node
{
    int key;
    node* left;
    node* right;
}Node, *pNode;

 

// normal output
void inorder_output(pNode root)
{
    if(root != NULL)
    {
        inorder_output(root->left);
        cout<<root->key<<"  ";
        inorder_output(root->right);
    }
}

 

// layer output
void layer_output(pNode root)
{
    queue<pNode> tq;
    pNode p;
    tq.push(root);
    tq.push(NULL);
    while(!tq.empty())
    {
        p = tq.front();
        tq.pop();
        if(NULL == p)
        {
            cout<<endl;
            if(!tq.empty())
               tq.push(NULL);
        }
        else
        {
            cout<<p->key<<" ";
            if(p->left != NULL)
                tq.push(p->left);
            if(p->right != NULL)
                tq.push(p->right);
        }
    }
}

 

//
int getheight_tree(pNode root)
{
    if(NULL == root)
        return 0;
    int lh = getheight_tree(root->left);
    int rh = getheight_tree(root->right);
    return lh > rh ? lh + 1 : rh + 1;
}

//


//
void insert_BST(pNode *root, int value)
{
    pNode tmp;
    if(NULL == *root)
    {
        tmp = (pNode)malloc(sizeof(Node));
        tmp->key = value;
        tmp->left = NULL;
        tmp->right = NULL;
        *root = tmp;
    }
    else
    {
        if(value <= (*root)->key)
            insert_BST(&(*root)->left, value);
        else
            insert_BST(&(*root)->right, value);
    }
}

 

// LCA of BST
// 对于BST,它有一个重要的性质:节点的左子树包含且只包含比它小的节点,
// 右子树包含且只包含比它大的节点。因此LCA的值一定在这两个节点之间。
//
// 1. 如果u<root && v<root,则去左子树搜索;
//
// 2. 如果u>root && v>root,则去右子树搜索;
//
// 3. 否则,root就是LCA
pNode search_LCA_BST(pNode root, pNode u, pNode v)
{
    if(NULL == root)
        return NULL;
    if(root->key >= u->key && root->key >= v->key)
        search_LCA_BST(root->left, u, v);
    else if(root->key < u->key && root->key < v->key)
        search_LCA_BST(root->right, u, v);
    else
        return root;
}

 

// append linklist2 to linklist1
pNode appendLL(pNode LL1, pNode LL2)
{
    if(NULL == LL1)
        return LL2;
    if(NULL == LL2)
        return LL1;
    ////tail
    pNode tail1 = LL1->left;
    pNode tail2 = LL2->left;

    ////join head and tail
    tail1->right = LL2;
    LL2->left = tail1;

    tail2->right = LL1;
    LL1->left = tail2;

    return LL1;
}

 

// convert BST to sorted link list
pNode BST_to_sortedlinklist(pNode root)
{
    if(NULL == root)
        return NULL;
    pNode leftLL = BST_to_sortedlinklist(root->left);
    pNode rightLL = BST_to_sortedlinklist(root->right);

    //
    root->left = root;
    root->right = root;

    //append leftLL ,root ,rightLL
    leftLL = appendLL(leftLL, root);
    leftLL = appendLL(leftLL, rightLL);

    return leftLL;
}

 

// travel link list
void list_output(pNode root)
{
    if(NULL == root)
        return;
    pNode p = root;
    cout<<p->key<<"  ";
    p = p->right;
    while(p != root)
    {
        cout<<p->key<<"  ";
        p = p->right;
    }
}

 

//// LCA --> RMQ
// 对树进行深度优先遍历,每当“进入”或回溯到某个结点时,
// 将这个结点的值存入数组E最后一位。同时记录结点i在数组中
// 第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。
// 如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),
// 就等价于求 E[RMQ(D,R[u],R[v])],(R[u]<R[v])。
// 例如,对于第一节的例一,求解步骤如下:
// 数列
// E[i]为:1,2,1,3,4,3,5,3,1
// R[i]为:1,2,4,5,7
// D[i]为:0,1,0,1,2,1,2,1,0
//
// 于是有:
// LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1
// LCA(T,3,4) = E[RMQ(D,R[3],R[4])] = E[RMQ(D,4,5)] = E[4] = 3
// LCA(T,4,5) = E[RMQ(D,R[4],R[5])] = E[RMQ(D,5,7)] = E[6] = 3
void prepare_for_LCA(pNode root, int h, vector<int>& E, vector<int>& R, vector<int>& D)
{
    if(NULL == root)
        return;
    E.push_back(root->key);
    D.push_back(h);
    R.push_back((int)E.size());
    if(root->left != NULL)
    {
        prepare_for_LCA(root->left, h + 1, E, R, D);
        E.push_back(root->key);
        D.push_back(h);
    }
    if(root->right != NULL)
    {
        prepare_for_LCA(root->right, h + 1, E, R, D);
        E.push_back(root->key);
        D.push_back(h);
    }
}

//// get LCA by RMQ
int get_LCA_by_RMQ(int i, int j, int* E, int* R, int* D, int n)
{
    if(i > j)
        swap(i, j);
    ////fij
    st(D, n);

    ////
    return(E[RMQ(D, R[i - 1] - 1, R[j - 1] - 1)]);
}

 

int _tmain(int argc, _TCHAR* argv[])
{
    ////BST root
    pNode root_BST = NULL;
    pNode p;

    int A[] = {1,2,2,3,4,5,6,7,8,9};
    int N = sizeof(A) / sizeof(int);
    random_shuffle(A, A + N);

    ////construct BST tree
    for(int i = 0; i < N; i++)
        insert_BST(&root_BST, A[i]);
    cout<<endl<<"inorder travel:"<<endl;
    inorder_output(root_BST);
   
    cout<<endl<<"layer output:"<<endl;
    layer_output(root_BST);
   
    ////
    pNode u = (pNode)malloc(sizeof(Node));
    u->key = 9;
    pNode v = (pNode)malloc(sizeof(Node));
    v->key = 4;
    p = search_LCA_BST(root_BST, u, v);
    if(NULL == p)
        cout<<cout<<endl<<"节点 "<<u->key<<" 与节点 "<<v->key<<" 无LCA节点"<<endl;
    else
        cout<<endl<<"节点 "<<u->key<<" 与节点 "<<v->key<<" 的LCA是节点 "<<p->key<<endl;


    ////
    p = BST_to_sortedlinklist(root_BST);
    cout<<endl<<"convert to sorted link list :/n";
    list_output(p);
    cout<<endl;


    //// LCS --> RMQ
    pNode testT = (pNode)malloc(sizeof(Node));
    pNode node2 = (pNode)malloc(sizeof(Node));
    pNode node3 = (pNode)malloc(sizeof(Node));
    pNode node4 = (pNode)malloc(sizeof(Node));
    pNode node5 = (pNode)malloc(sizeof(Node));
    testT->key = 1;
    node2->key = 2;
    node3->key = 3;
    node4->key = 4;
    node5->key = 5;
    testT->left = node2;
    testT->right = node3;
    node2->left = node2->right = NULL;
    node3->left = node4;
    node3->right = node5;
    node4->left = node4->right = NULL;
    node5->left = node5->right = NULL;

    layer_output(testT);

    vector<int> E, R, D;
    prepare_for_LCA(testT, 0, E, R, D);

    int *arrE = new int[(int)E.size()];
    int *arrR = new int[(int)R.size()];
    int *arrD = new int[(int)D.size()];
    copy(E.begin(), E.end(), arrE);
    copy(R.begin(), R.end(), arrR);
    copy(D.begin(), D.end(), arrD);

    cout<<endl<<get_LCA_by_RMQ(4, 5, arrE, arrR, arrD, (int)D.size())<<endl;

    system("pause");
 return 0;
}

你可能感兴趣的:(面试,tree,null,search,insert,output)