【面试题】-二叉树

二叉树的结点个数

1、如果二叉树为空,节点个数为0
2、不为空,节点个数 = 左子树节点个数 + 右子树节点个数 + 1

/*
二叉树的节点个数
1、如果二叉树为空,节点个数为0
2、不为空,节点个数 = 左子树节点个数 + 右子树节点个数 + 1
*/
int  GetTreeNum(pTree T)
{
    if(T == NULL)
        return 0;

    return GetTreeNum(T->Left) + GetTreeNum(T->Right) + 1;
}

二叉树的深度

1、如果二叉树为空,深度为0
2、不为空,深度为 = max(左子树深度,右子树深度) + 1

/*
二叉树的深度
1、如果二叉树为空,深度为0
2、不为空,深度为 = max(左子树深度,右子树深度)  + 1
*/

int GetTreeDepth(pTree T)
{
    if(T == NULL)
        return 0;
    return max(GetTreeDepth(T->Left), GetTreeDepth(T->Right)) + 1;
}

二叉树的遍历

前序遍历递归实现

1、二叉树为空,返回
2、不为空,先访问根节点,然后遍历左子树,最后遍历右子树

/*
前序遍历(递归解法)
1、二叉树为空,返回
2、不为空,先访问根节点,然后遍历左子树,最后遍历右子树
*/
void PreOrderTraverse(pTree T)
{
    if(T == NULL)
        return;
    cout << T->value;
    PreOrderTraverse(T->Left);
    PreOrderTraverse(T->Right);
}
前序遍历非递归实现(栈模拟)

先访问,再入栈;
1、如果T非空,访问栈顶元素,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。

/*
前序遍历(非递归解法——栈模拟递归实现)
先访问,再入栈;
1、如果T非空,访问栈顶元素,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。
*/
void PreOrderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;

    while(T || !Stk.empty())
    {
        if(T)
        {
            cout << T->value;
            Stk.push(T);
            T = T->Left;
        }
        else
        {
            T = Stk.top();
            Stk.pop();
            T = T->Right;
        }
    }
}
中序遍历递归实现

1、二叉树为空,返回
2、不为空,先遍历左子树,然后访问根节点,最后遍历右子树

/*
中序遍历(递归解法)
1、二叉树为空,返回
2、不为空,先遍历左子树,然后访问根节点,最后遍历右子树
*/
void InOrderTraverse(pTree T)
{
    if(T == NULL)
        return;
    InOrderTraverse(T->Left);
    cout << T->value;
    InOrderTraverse(T->Right);
}

中序遍历非递归实现(栈模拟)

先入栈,后访问;
1、如果T非空,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,进行访问,然后T指向右儿子。

/*
中序遍历(非递归解法——栈模拟递归实现)
先入栈,后访问;
1、如果T非空,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,进行访问,然后T指向右儿子。
*/
void InOrderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;

    while(T || !Stk.empty())
    {
        if(T)
        {
            Stk.push(T);
            T = T->Left;
        }
        else
        {
            T = Stk.top();
            Stk.pop();
            cout << T->value;
            T = T->Right;
        }
    }
}
后序遍历递归实现

1、二叉树为空,返回
2、不为空,先遍历左子树,然后遍历右子树,最后访问根节点

/*
后序遍历(递归解法)
1、二叉树为空,返回
2、不为空,先遍历左子树,然后遍历右子树,最后访问根节点
*/
void PostOrderTraverse(pTree T)
{
    if(T == NULL)
        return;
    PostOrderTraverse(T->Left);

    PostOrderTraverse(T->Right);

    cout << T->value;
}
后序遍历非递归实现(双栈模拟)

采用两个栈来实现,不过就是先压入当前节点的左子树,后压入当前节点的右子树。得到的顺序是根节点-右子树-左子树,刚好是后序遍历的逆序,所以再利用一个栈解决就可以倒过来。

/*
后序遍历(非递归解法)
采用两个栈来实现,不过就是先压入当前节点的左子树,后压入当前节点的右子树。
得到的顺序是根节点-右子树-左子树,刚好是后序遍历的逆序,所以再利用一个栈解决就可以倒过来。
*/
void PostOrderTraverse2(pTree T)
{
    if(T == NULL)
        return;

    stack<pTree> Stk1,Stk2;

    Stk1.push(T);

    while(!Stk1.empty())
    {
        pTree p = Stk1.top();
        Stk1.pop();
        Stk2.push(p);

        if(p->Left)
            Stk1.push(p->Left);
        if(p->Right)
            Stk1.push(p->Right);
    }

    while(!Stk2.empty())
    {
        cout << Stk2.top()->value;
        Stk2.pop();
    }
}
层序遍历队列实现

层序遍历就是从上到下从左到右访问树的节点。采用FIFO(先进先出)的数据结构-队列先将根节点入队,当队列不为空时,弹出一个节点,然后访问,若左子树或右子树不为空,将其入队

/*
层序遍历(队列)
层序遍历就是从上到下从左到右访问树的节点。采用FIFO(先进先出)的数据结构-队列
先将根节点入队,当队列不为空时,弹出一个节点,然后访问,若左子树或右子树不为空,
将其入队
*/
void LevelTraverse(pTree T)
{
    if(T == NULL)
        return;

    queue<pTree> que;
    que.push(T);

    while(!que.empty())
    {
        pTree p = que.front();
        que.pop();
        cout << p->value;
        if(p->Left)
            que.push(p->Left);
        if(p->Right)
            que.push(p->Right);
    }
}

二叉搜索树转换双向链表

http://blog.csdn.net/zwhlxl/article/details/46622981

二叉树第K层的结点个数

递归解法:
1、如果二叉树为空且k<1 则返回0;
2、如果二叉树不为空且k=1,返回1;
3、如果二叉树不为空且k>1,返回左子树中k-1层的结点个数与右子树中K-1层的结点个数之和

/*
二叉树第K层的结点个数
1、如果二叉树为空且k<1 则返回0;
2、如果二叉树不为空且k=1,返回1;
3、如果二叉树不为空且k>1,返回左子树中k-1层的结点个数与右子树中K-1层的结点个数之和

*/

int GetKthLevelNode(pTree T, int k)
{
    if(T == NULL || k < 0)
        return 0;
    if(k == 1)
        return 1;

    int leftnum = GetKthLevelNode(T->Left, k - 1);
    int rightnum = GetKthLevelNode(T->Right, k - 1);

    return leftnum + rightnum;
}

非递归解法:
利用队列解决

int GetKthLevelNode2(pTree T, int k)
{
    if(T == NULL || k < 0)
        return 0;
    int curLevel = 0;
    int curLevelNode = 0;
    queue<pTree> que;
    que.push(T);

    while(!que.empty())
    {
        curLevel ++;
        curLevelNode = que.size(); // 当前层的结点个数
        if(curLevel == k)  // 如果当前层数等于所求的层数,跳出循环
            break;
        int cntNode = 0 ;
        while(cntNode < curLevelNode)  //如果当前层数还没到求的层数,将当前层结点弹出,下一层结点入栈
        {
            cntNode ++;
            T = que.front();
            que.pop();
            if(T->Left != NULL)
                que.push(T->Left);
            if(T->Right != NULL)
                que.push(T->Right);
        }

    }

    while(!que.empty())
        que.pop();
    if(curLevel == k)
        return curLevelNode;
    return 0;  //k 值大于树的深度,返回0
}

寻找二叉树两个节点的最低公共祖先

http://www.acmerblog.com/lca-lowest-common-ancestor-5574.html

#include <iostream>
#include <vector>

using namespace std;

struct Node
{
    int key;
    struct Node *left;
    struct Node *right;
};

Node* newNode(int k)
{
    Node* tmp = new Node;
    tmp->key = k;
    tmp->left = NULL;
    tmp->right = NULL;
}

bool FindPath(Node* root, vector<int> &path, int key)
{
    if(root == NULL)
        return false;
    path.push_back(root->key);
    if(root->key == key)
        return true;

    bool found = (FindPath(root->left, path, key) | FindPath(root->right, path, key));

    /*
    bool found = false;


    found = FindPath(root->left, path, key);

    if(!found)
        found = FindPath(root->right, path, key);
*/
    if(!found)
        path.pop_back();

    return found;

}

int FindLCA(Node* root, int key1, int key2)
{
    vector<int> path1, path2;
    int ans;
    bool found1 = FindPath(root, path1, key1);
    bool found2 = FindPath(root, path2, key2);

    if(found1 && found2)
    {
        for(int i = 0; i < path1.size(); i ++)
        {
            if(path1[i] != path2[i])
                break;
            else
                ans = path1[i];
        }

        return ans;
    }

    return -1;
}

int main(int argc, char const *argv[])
{
    Node* Tree = newNode(1);
    Tree->left = newNode(2);
    Tree->right = newNode(3);
    Tree->left->left = newNode(4);
    Tree->left->right = newNode(5);
    Tree->right->left = newNode(6);
    Tree->right->right= newNode(7);
    cout << "LCA(4,5)" << FindLCA(Tree, 4, 5) << endl;
    cout << "LCA(4,6)" << FindLCA(Tree, 4, 6) << endl;
    return 0;
}

测试程序

分别有求二叉树的节点个数,深度,和前序,中序,后序的递归和非递归实现

#include <iostream>
#include <stack>
#include <queue>


/*
          A
      B       C
    D   E   F    G
  H       I         J
    K


*/
using namespace std;

#define OK 1
#define ERROR 0
#define TRUE  1
#define FALSE 0

typedef char ElementType;


typedef struct TreeNode
{
    ElementType value;
    struct TreeNode *Left;
    struct TreeNode *Right;
}TreeNode, *pTree;

int index = 0;
char str[] = "ABDH#K###E##CFI###G#J##";

void DeleteTree(pTree *T)
{
    if(*T)
    {
        if((*T)->Left)
            DeleteTree(&(*T)->Left);
        if((*T)->Right)
            DeleteTree(&(*T)->Right);
        free(*T);
        *T = NULL;
    }
}

void CreatTree(pTree *T)
{
    ElementType ch;
    ch = str[index ++];
    if(ch == '#')
        *T = NULL;
    else
    {
        //(pTree) *T = new TreeNode;
        *T = (pTree)malloc(sizeof(TreeNode));
        if(*T == NULL)
            exit(ERROR);

        (*T)->value = ch;
        CreatTree(&(*T)->Left);
        CreatTree(&(*T)->Right);

    }
}



/*
二叉树的节点个数
1、如果二叉树为空,节点个数为0
2、不为空,节点个数 = 左子树节点个数 + 右子树节点个数 + 1
*/
int  GetTreeNum(pTree T)
{
    if(T == NULL)
        return 0;

    return GetTreeNum(T->Left) + GetTreeNum(T->Right) + 1;
}

/*
二叉树的深度
1、如果二叉树为空,深度为0
2、不为空,深度为 = max(左子树深度,右子树深度)  + 1
*/

int GetTreeDepth(pTree T)
{
    if(T == NULL)
        return 0;
    return max(GetTreeDepth(T->Left), GetTreeDepth(T->Right)) + 1;
}

/*
前序遍历(递归解法)
1、二叉树为空,返回
2、不为空,先访问根节点,然后遍历左子树,最后遍历右子树
*/
void PreOrderTraverse(pTree T)
{
    if(T == NULL)
        return;
    cout << T->value;
    PreOrderTraverse(T->Left);
    PreOrderTraverse(T->Right);
}

/*
中序遍历(递归解法)
1、二叉树为空,返回
2、不为空,先遍历左子树,然后访问根节点,最后遍历右子树
*/
void InOrderTraverse(pTree T)
{
    if(T == NULL)
        return;
    InOrderTraverse(T->Left);
    cout << T->value;
    InOrderTraverse(T->Right);
}

/*
后序遍历(递归解法)
1、二叉树为空,返回
2、不为空,先遍历左子树,然后遍历右子树,最后访问根节点
*/
void PostOrderTraverse(pTree T)
{
    if(T == NULL)
        return;
    PostOrderTraverse(T->Left);

    PostOrderTraverse(T->Right);

    cout << T->value;
}

/*
前序遍历(非递归解法——栈模拟递归实现)
先访问,再入栈;
1、如果T非空,访问栈顶元素,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。
*/
void PreOrderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;

    while(T || !Stk.empty())
    {
        if(T)
        {
            cout << T->value;
            Stk.push(T);
            T = T->Left;
        }
        else
        {
            T = Stk.top();
            Stk.pop();
            T = T->Right;
        }
    }
}

/*
中序遍历(非递归解法——栈模拟递归实现)
先入栈,后访问;
1、如果T非空,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,进行访问,然后T指向右儿子。
*/
void InOrderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;

    while(T || !Stk.empty())
    {
        if(T)
        {
            Stk.push(T);
            T = T->Left;
        }
        else
        {
            T = Stk.top();
            Stk.pop();
            cout << T->value;
            T = T->Right;
        }
    }
}


/*
后序遍历(非递归解法)
采用两个栈来实现,不过就是先压入当前节点的左子树,后压入当前节点的右子树。
得到的顺序是根节点-右子树-左子树,刚好是后序遍历的逆序,所以再利用一个栈解决就可以倒过来。
*/
void PostOrderTraverse2(pTree T)
{
    if(T == NULL)
        return;

    stack<pTree> Stk1,Stk2;

    Stk1.push(T);

    while(!Stk1.empty())
    {
        pTree p = Stk1.top();
        Stk1.pop();
        Stk2.push(p);

        if(p->Left)
            Stk1.push(p->Left);
        if(p->Right)
            Stk1.push(p->Right);
    }

    while(!Stk2.empty())
    {
        cout << Stk2.top()->value;
        Stk2.pop();
    }
}

/*
层序遍历(队列)
层序遍历就是从上到下从左到右访问树的节点。采用FIFO(先进先出)的数据结构-队列
先将根节点入队,当队列不为空时,弹出一个节点,然后访问,若左子树或右子树不为空,
将其入队
*/
void LevelTraverse(pTree T)
{
    if(T == NULL)
        return;

    queue<pTree> que;
    que.push(T);

    while(!que.empty())
    {
        pTree p = que.front();
        que.pop();
        cout << p->value;
        if(p->Left)
            que.push(p->Left);
        if(p->Right)
            que.push(p->Right);
    }
}

/*
二叉树第K层的结点个数
1、如果二叉树为空且k<1 则返回0;
2、如果二叉树不为空且k=1,返回1;
3、如果二叉树不为空且k>1,返回左子树中k-1层的结点个数与右子树中K-1层的结点个数之和

*/

int GetKthLevelNode(pTree T, int k)
{
    if(T == NULL || k < 1)
        return 0;
    if(k == 1)
        return 1;

    int leftnum = GetKthLevelNode(T->Left, k - 1);
    int rightnum = GetKthLevelNode(T->Right, k - 1);

    return leftnum + rightnum;
}

int GetKthLevelNode2(pTree T, int k)
{
    if(T == NULL || k < 0)
        return 0;
    int curLevel = 0;
    int curLevelNode = 0;
    queue<pTree> que;
    que.push(T);

    while(!que.empty())
    {
        curLevel ++;
        curLevelNode = que.size(); // 当前层的结点个数
        if(curLevel == k)  // 如果当前层数等于所求的层数,跳出循环
            break;
        int cntNode = 0 ;
        while(cntNode < curLevelNode)  //如果当前层数还没到求的层数,将当前层结点弹出,下一层结点入栈
        {
            cntNode ++;
            T = que.front();
            que.pop();
            if(T->Left != NULL)
                que.push(T->Left);
            if(T->Right != NULL)
                que.push(T->Right);
        }

    }

    while(!que.empty())
        que.pop();
    if(curLevel == k)
        return curLevelNode;
    return 0;  //k 值大于树的深度,返回0
}

/*
求二叉树的叶结点个数(叶结点指没有左儿子和右儿子的结点)
*/

int GetTreeLeafsNum(pTree T)
{
    if(T == NULL)
        return 0;
    if(T->Left == NULL && T->Right == NULL)
        return 1;
    int leftnum = GetTreeLeafsNum(T->Left);
    int rightnum = GetTreeLeafsNum(T->Right);

    return leftnum + rightnum;
}

bool FindPath(TreeNode* root, vector<ElementType>& path, ElementType ch)
{
    if(root == NULL)
        return false;

    path.push_back(root->value);
    if(ch == root->value)
        return true;

    bool found = false;

    found = (FindPath(root->Left, path, ch) | FindPath(root->Right, path, ch));

    if(!found)
        path.pop_back();

    return found;

}

ElementType FindLCA(TreeNode * root, ElementType ch1, ElementType ch2)
{
    vector<ElementType> path1, path2;
    ElementType ans;
    bool find1 = FindPath(root, path1, ch1);
    bool find2 = FindPath(root, path2, ch2);

    if(find1 && find2)
    {
        for(int i = 0; i < path1.size(); i ++)
        {
            if(path1[i] != path2[i])
                break;

            else 
                ans = path1[i];

        }

        return ans;

    }

    return -1;

}

int main(int argc, char const *argv[])
{
    /* code */
    pTree Tnode = NULL;
    CreatTree(&Tnode);

    cout << "该二叉树节点个数为  " << GetTreeNum(Tnode) << endl;

    cout << "该二叉树深度为  " << GetTreeDepth(Tnode) << endl;

    cout << "前序遍历(递归)" << endl;
    PreOrderTraverse(Tnode);
    cout << endl;

    cout << "前序遍历(非递归)" << endl;
    PreOrderTraverse2(Tnode);
    cout << endl;


    cout << "中序遍历(递归)" << endl;
    InOrderTraverse(Tnode);
    cout << endl;

    cout << "中序遍历(非递归)" << endl;
    InOrderTraverse2(Tnode);
    cout << endl;

    cout << "后序遍历(递归)" << endl;
    PostOrderTraverse(Tnode);
    cout << endl;

    cout << "后序遍历(非递归)" << endl;
    PostOrderTraverse2(Tnode);
    cout << endl;

    cout << "层序遍历(队列)" << endl;
    LevelTraverse(Tnode);
    cout << endl;

    cout << "二叉树第3层的结点个数为(递归): " << GetKthLevelNode(Tnode, 3) << endl;

    cout << "二叉树第3层的结点个数为(非递归): " << GetKthLevelNode2(Tnode, 3) << endl;

    cout << "二叉树叶结点个数为(递归): " << GetTreeLeafsNum(Tnode) << endl;

    cout << "两个结点的最低公共祖先" << endl;


    /*
           A
      B         C
    D   E    F     G
  H        I         J
    K


*/
    cout << "LCA(H,K)" << FindLCA(Tnode, 'H', 'K') << endl;

    cout << "LCA(H,I)" << FindLCA(Tnode, 'H', 'I') << endl;

    cout << "LCA(F,G)" << FindLCA(Tnode, 'F', 'G') << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值