二叉树经典题目

1.判断一个节点是否在一棵二叉树中
先判断是否在左子树,若在,则不再去右子树中寻找;若不在,再去右子树中寻找。要注意递归的条件判断

    bool _IsInTree(Node *root, Node *node)
    {
        if (root == NULL || node == NULL)
            return false;
        else if (root == node)
            return true;
        else if (_IsInTree(root->_left, node))
            return true;
        else
            _IsInTree(root->_right, node);
    }

    bool IsInTree(Node *node)
    {
        return _IsInTree(_root, node);
    }

2.判断一棵二叉树是否是平衡二叉树
一颗二叉树是平衡二叉树,则它的左子树是平衡二叉树,且它的右子树也是平衡二叉树。平衡二叉树的条件为:左子树与右子树的高度差为1、0、-1。

    bool _IsBalance(Node *root)
    {
        if (root == NULL)
            return true;

        size_t leftDepth = _Depth(root->_left);
        size_t rightDepth = _Depth(root->_right);
        size_t sub = leftDepth - rightDepth;
        if (sub < 2 && sub >-2)
            return true;

        return _IsBalance(root->_left) && _IsBalance(root->_right);
    }

    bool IsBalance()
    {
        return _IsBalance(_root);
    }

上述方法会多次重复遍历树中的节点,效率太低。我们可以在遍历节点的同时保存节点的深度,就不必重复遍历同一个节点了,效率得到了提高。

    bool _IsBalance(Node *root, int* depth)
    {
        if (root == NULL)
        {
            *depth = 0;
            return true;
        }

        int leftDepth;
        int rightDepth;
        if (_IsBalance(root->_left, &leftDepth) && _IsBalance(root->_right, &rightDepth))
        {
            int sub = leftDepth - rightDepth;
            if (sub < 2 && sub >-2)
            {
                *depth = leftDepth > rightDepth ? leftDepth+1 : rightDepth+1;
                return true;
            }
            else
                return false;
        }
    }

    bool IsBalance()
    {
        int depth = 0;
        return _IsBalance(_root, &depth);
    }

3.求二叉树的镜像
二叉树的镜像,顾名思义,一颗二叉树在镜子里的成像。即二叉树中每一个节点的左右子树都互相交换了。

    void _MirrorTree(Node *root)
    {
        if (root == NULL)
            return;
        else if (root->_left == NULL && root->_right == NULL)
            return;
        else
        {
            Node* temp = root->_left;
            root->_left = root->_right;
            root->_right = temp;
        }

        if (root->_left)
            _MirrorTree(root->_left);
        if (root->_right)
            _MirrorTree(root->_right);
    }

    void MirrorTree()
    {
        _MirrorTree(_root);
    }

4.求两个节点的最近公共祖先
1)二叉树每个节点有parent指针(三叉链)
二叉树的节点结构

struct Node1
{
    int _data;
    Node1* _parent;
    Node1* _left;
    Node1* _right;

    Node1(const int& data)
        :_data(data)
        , _parent(NULL)
        , _left(NULL)
        , _right(NULL)
    {}
};

这里给出两种方法

//三叉链 O(N^2)
Node1* LastAncestor(Node1* node1, Node1* node2)
{
    if (node1 == NULL && node2 == NULL)
        return NULL;
    if (node1 == node2)
        return node1;

    Node1* cur1 = NULL;
    while (node2)
    {
        cur1 = node1;
        while (cur1)
        {
            if (cur1->_parent != node2->_parent)
                cur1 = cur1->_parent;
            else
                return cur1->_parent;
        }
        node2 = node2->_parent;
    }
    return NULL;
}

//三叉链,类似求两个链表的交点 O(N)
int GetLength(Node1* node)
{
    if (node == NULL)
        return 0;

    int length = 0;
    while (node)
    {
        length++;
        node = node->_parent;
    }
    return length;
}

Node1* LastAncestor1(Node1* node1, Node1* node2)
{
    if (node1 == NULL && node2 == NULL)
        return NULL;

    int len1 = GetLength(node1);
    int len2 = GetLength(node2);

    while (len1 > len2)
    {
        node1 = node1->_parent;
        len1--;
    }
    while (len2 > len1)
    {
        node2 = node2->_parent;
        len2--;
    }

    while (node1 && node2 && node1 != node2)
    {
        node1 = node1->_parent;
        node2 = node2->_parent;
    }
    if (node1 == node2)
        return node1;

    return NULL;
}

2)二叉树是搜索二叉树 O(N)
搜索二叉树的特点是:左子树的节点全都小于根节点,右子树的节点全都大于根节点,并且其左子树和右子树也是搜索二叉树。
这样,就可以通过比较两个节点与其根节点的大小来求得它们的最近公共节点:若一个节点大于根节点,一个节点小于根节点,则根节点为它们的最近公共节点;若两个节点都小于根节点,则去根节点的左子树中寻找;若两个节点都大于根节点,则去根节点的右子树中寻找

typedef struct SearchBinaryTreeNode
{
    int _key;
    SearchBinaryTreeNode* _left;
    SearchBinaryTreeNode* _right;

    SearchBinaryTreeNode(const int& key)
        :_key(key)
        , _left(NULL)
        , _right(NULL)
    {}
}Node2;

void Insert1(Node2* root, const int& key)
{
    if (root == NULL)
    {
        root = new Node2(key);
        return;
    }
    else
    {
        Node2* parent = NULL;
        Node2* cur = root;
        while (cur)
        {
            if (cur->_key < key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return;
            }
        }
        if (parent->_key < key)
        {
            parent->_right = new Node2(key);
        }
        if (parent->_key > key)
        {
            parent->_left = new Node2(key);
        }
    }
}

Node2* LastAncestor2(Node2* root, Node2* node1, Node2* node2)
{
    if (root == NULL || (node1 == NULL && node2 == NULL))
        return NULL;

    while (root)
    {
        if (node1 == root || node2 == root)
            return root;

        if (node1->_key < root->_key && node2->_key < root->_key)
        {
            root = root->_left;
        }
        else if (node1->_key > root->_key && node2->_key > root->_key)
        {
            root = root->_right;
        }
        else
        {
            return root;
        }
    }
    return NULL;
}

3)二叉树为普通二叉树

typedef struct Node3
{
    int _data;
    Node3* _left;
    Node3* _right;

    Node3(const int& data)
        :_data(data)
        , _left(NULL)
        , _right(NULL)
    {}
};

Node3* CreateTree(int *arr, size_t n, const int& invalid, size_t& index)
{
    Node3 *root = NULL;
    if (index < n && arr[index] != invalid)
    {
        root = new Node3(arr[index]);
        index++;
        root->_left = CreateTree(arr, n, invalid, index);
        index++;
        root->_right = CreateTree(arr, n, invalid, index);
    }
    return root;
}

//1.递归查找 O(N^2)
Node3* LastAncestor3(Node3* root, Node3* node1, Node3* node2)
{
    if (root == NULL || (node1 == NULL && node2 == NULL))
        return NULL;
    if (node1 == root || node2 == root)
    {
        return root;
    }
    if ((root->_left == node1 && root->_right == node2) || (root->_left == node2 && root->_right == node1))
    {
        return root;
    }

    Node3* left = LastAncestor3(root->_left, node1, node2);
    Node3* right = LastAncestor3(root->_right, node1, node2);
    if (left && right)
        return root;
    if (left == NULL)
        return right;
    else
        return left;
}

//2.借助辅助空间栈,然后类似于求两条链表的交点 时间复杂度O(N),空间复杂度O(N*lgN) 
bool GetNodePath(Node3* root, Node3* node, stack& path)
{
    if (root == NULL || node == NULL)
        return NULL;

    Node3* cur = root;
    path.push(cur);
    if (path.top() == node)
        return true;
    if (GetNodePath(root->_left, node, path))
        return true;
    if (GetNodePath(root->_right, node, path))
        return true;

    path.pop();
    return false;
}

Node3* LastAncestor4(Node3* root, Node3* node1, Node3* node2)
{
    if (root == NULL || (node1 == NULL && node2 == NULL))
        return NULL;

    stack path1;
    stack path2;
    if (GetNodePath(root, node1, path1) == false)
        return NULL;
    if (GetNodePath(root, node2, path2) == false)
        return NULL;

    while (path1.size() != path2.size())
    {
        if (path1.size() > path2.size())
            path1.pop();
        else
            path2.pop();
    }

    while (path1.top() != path2.top())
    {
        path1.pop();
        path2.pop();
    }
    if (path1.top() == path2.top())
        return path1.top();
}

以上所有方法的测试用例

void test5()
{
    Node1* root1 = new Node1(1);

    Node1* cur = root1;
    queue q;
    Node1* top = NULL;

    q.push(root1);
    for (int i = 2; i <= 7; i++)
    {
        if (!q.empty())
        {
            top = q.front();
            if (cur == top->_left)
            {
                cur = new Node1(i);
                top->_right = cur;
                cur->_parent = top;
                q.pop();
            }
            else
            {
                cur = new Node1(i);
                top->_left = cur;
                cur->_parent = top;
            }
            q.push(cur);
        }
    }
    Node1* node1 = root1->_left->_left;
    Node1* node2 = root1->_right;
    cout << LastAncestor(node1, node2)->_data << endl;
    cout << LastAncestor1(node1, node2)->_data << endl;

    Node2* root2 = new Node2(10);
    Insert1(root2, 8);
    Insert1(root2, 15);
    Insert1(root2, 5);
    Insert1(root2, 9);
    Insert1(root2, 13);
    Insert1(root2, 18);

    Node2* node3 = root2->_left->_left;
    Node2* node4 = root2->_left->_right;
    cout << LastAncestor2(root2, node3, node4)->_key << endl;

    int array[] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 };
    size_t index = 0;
    Node3* root3 = CreateTree(array, sizeof(array) / sizeof(array[0]), '#', index);
    Node3* node5 = root3->_left->_left;
    Node3* node6 = root3->_right->_left;
    cout << LastAncestor3(root3, node5, node6)->_data << endl;

    cout << LastAncestor4(root3, node5, node6)->_data << endl;
}

5.求二叉树中最远的两个节点的距离
求以当前节点为根的二叉树中最远两个节点的距离,先求出以当前节点的左孩子为根的二叉树中最远两个节点的距离和以当前节点的右孩子为根的二叉树中最远两个节点的距离。

int _GetMaxDistance(Node3* node, int& distance)
{
    if (node == NULL)
        return 0;

    int l = _GetMaxDistance(node->_left, distance);
    int r = _GetMaxDistance(node->_right, distance);

    if (l + r > distance)
    {
        distance = l + r;
    }

    return l > r ? l + 1 : r + 1;
}

int GetMaxDistance(Node3* root)
{
    int distance = 0;
    _GetMaxDistance(root, distance);
    return distance;
}

6.由前序遍历和中序遍历重建二叉树
如:前序序列:1 2 3 4 5 6 - 中序序列:3 2 4 1 6 5

    Node* RebuildBinaryTree1(int* prev, int* in, int n)
    {
        if (n <= 0)  //左右序列有可能为空!!!
            return NULL;
        Node* root = (Node*)malloc(sizeof(Node));
        root->_data = prev[0];
        root->_left = root->_right = NULL;
        int* p = in;
        int k = 0;
        while (*p != *prev)
        {
            p++;
        }
        k = p - in;
        root->_left = RebuildBinaryTree1(prev + 1, in, k);
        root->_right = RebuildBinaryTree1(prev + k + 1, p + 1, n - k - 1);

        return root;
    }

7.由后序遍历和中序遍历重建二叉树
如:后序序列:3 4 2 6 5 1 - 中序序列:3 2 4 1 6 5

    Node* RebuildBinaryTree2(int* post, int* in, int n)
    {
        if (n <= 0)
            return NULL;
        Node* root = (Node*)malloc(sizeof(Node));
        root->_data = *(post + n - 1);
        root->_left = root->_right = NULL;
        int* p = in;
        int k = 0;
        while (*p != *(post + n - 1))
        {
            p++;
        }
        k = p - in;
        root->_left = RebuildBinaryTree2(post, in, k);
        root->_right = RebuildBinaryTree2(post + k, p + 1, n - k - 1);

        return root;
    }

8.判断一棵树是否是完全二叉树
完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点,即有右节点一定有左节点。
设置标志的方法要考虑的情况太多,很容易出错。这里介绍一种更加简便可靠的方法:借助辅助存储空间队列,将二叉树中所有的节点,包括空节点以及叶子节点的左右孩子节点按照层序遍历的方法入队列并弹出相应节点。若为完全二叉树,则最后队列中剩下的全部都是空节点,否则不是完全二叉树。

bool IsComplexBinaryTree(TreeNode* root)
{
    queue q;
    TreeNode* cur = root;
    if (root == NULL)
        return true;
    q.push(cur);
    while (cur)
    {
        q.push(cur->_left);
        q.push(cur->_right);
        q.pop();
        cur = q.front();
    }
    while (!q.empty())
    {
        if (q.front() != NULL)
            return false;
        q.pop();
    }
    return true;
}

9.将二叉搜索树转换成一个排序的双向链表。
要求不能创建任何新的结点,只能调整树中结点指针的指向。可以考虑把left当作prev,right当作next

    //先把左子树转换成双向链表,再和根节点链接起来,最后把右子树也转换成双向链表
    void _ConvertNode(Node* root, Node*& tailOfList)
    {
        if (root == NULL)
            return;
        Node* cur = root;
        if (cur->_left)
            _ConvertNode(cur->_left, tailOfList);

        cur->_left = tailOfList;
        if (tailOfList != NULL)
            tailOfList->_right = cur;
        tailOfList = cur;

        if (cur->_right)
            _ConvertNode(cur->_right, tailOfList);
    }

    Node* Convert()
    {
        Node* tailOfList = NULL;
        _ConvertNode(_root, tailOfList);

        Node* headOfList = tailOfList;
        while (headOfList && headOfList->_left)
        {
            headOfList = headOfList->_left;
        }

        return headOfList;
    }

10.判断一颗二叉树是是否是另一颗树的子树

//1.判断tree1中是否存在与tree2的根节点相同的结点
bool DoseTree1HasTree2(TreeNode *root1, TreeNode *root2);

bool IsSubTree(TreeNode* root1, TreeNode* root2)
{
    if (root1 == NULL || root2 == NULL)
        return false;

    bool ret = false;
    if (DoseTree1HasTree2(root1, root2))
        ret = true;
    if (!ret)
    {
        ret = IsSubTree(root1->_left, root2);
    }
    if (!ret)
    {
        ret = IsSubTree(root1->_right, root2);
    }

    return ret;
}

//2.判断tree1中以root1为根节点的子树是否与tree2有相同的结构
bool DoseTree1HasTree2(TreeNode *root1, TreeNode *root2)
{
    if (root2 == NULL)
        return true;
    if (root1 == NULL)
        return false;
    if (root1->_data != root2->_data)
        return false;

    return DoseTree1HasTree2(root1->_left, root2->_left) && DoseTree1HasTree2(root1->_right, root2->_right);
}

你可能感兴趣的:(数据结构)