二叉樹経典テーマ

34128 ワード

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.2つのノードの最近の共通の祖先の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)を検索して二叉樹を検索する特徴として、左サブツリーのノードはいずれもルートノードより小さく、右サブツリーのノードは全てルートノードより大きく、左サブツリーと右サブツリーも二叉樹を検索することができる.このようにして、2つのノードとそのルートノードのサイズを比較することによって、それらの最近の共通ノードを求めることができる.1つのノードがルートノードより大きい場合、1つのノードがルートノードより小さい場合、ルートノードはそれらの一番近い共通ノードである.両方のノードがルートノードより小さい場合、ルートノードの左のサブツリーから探す.両方のノードがルートノードより大きい場合、ルートノードの右サブツリーから探す.
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-中のシーケンス:3 4 6
    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 1-中序シーケンス:3 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.一本の木が完全に二叉の木かどうかを判断する:最後の層を除いて、各層のノード数が最大値に達する;最後の層では、右のいくつかの結点だけが欠けています.つまり、右のノードが必ず左のノードを持っています.マークの設定方法を考えると多すぎて、間違えやすいです.ここでは、空間キューを補助的に記憶することによって、空ノードおよび葉っぱノードを含む2つのツリーのすべてのノードを、階層的に巡回する方法でキューに入れ、対応するノードをイジェクトするより簡便で信頼できる方法を紹介する.完全に二叉の木であれば、最後の列に残っているのは全部空のノードです.そうでなければ、完全に二叉の木ではありません.
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);
}