📜  按垂直顺序打印二叉树 |设置 2(基于地图的方法)

📅  最后修改于: 2021-10-27 07:15:21             🧑  作者: Mango

给定一个二叉树,垂直打印它。下面的例子说明了垂直顺序遍历。

1
        /    \ 
       2      3
      / \   /   \
     4   5  6   7
               /  \ 
              8   9 
               
              
The output of print this tree vertically will be:
4
2
1 5 6
3 8
7
9

垂直顺序打印二叉树

我们在上一篇文章中讨论了 O(n 2 ) 解决方案。在这篇文章中,讨论了一种基于哈希映射的有效解决方案。我们需要检查所有节点与根的水平距离。如果两个节点具有相同的水平距离 (HD),则它们在同一条垂直线上。高清的想法很简单。根的HD为0,右边缘(连接到右子树的边缘)被认为是+1水平距离,左边缘被认为是-1水平距离。例如,在上面的树中,节点 4 的 HD 为 -2,节点 2 的 HD 为 -1,5 和 6 的 HD 为 0,节点 7 的 HD 为 +2。
我们可以对给定的二叉树进行先序遍历。在遍历树时,我们可以递归计算 HD。我们最初将根的水平距离设为 0。对于左子树,我们将水平距离作为根的水平距离减 1 传递。对于右子树,我们将水平距离作为根的水平距离加 1 传递。对于每个 HD 值,我们在哈希图中维护一个节点列表。每当我们在遍历中看到一个节点时,我们就会转到哈希映射条目并使用 HD 作为映射中的键将该节点添加到哈希映射中。
以下是上述方法的C++实现。感谢 Chirag 提供以下 C++ 实现。

C++
// C++ program for printing vertical order of a given binary tree
#include 
#include 
#include 
using namespace std;
 
// Structure for a binary tree node
struct Node
{
    int key;
    Node *left, *right;
};
 
// A utility function to create a new node
struct Node* newNode(int key)
{
    struct Node* node = new Node;
    node->key = key;
    node->left = node->right = NULL;
    return node;
}
 
// Utility function to store vertical order in map 'm'
// 'hd' is horigontal distance of current node from root.
// 'hd' is initially passed as 0
void getVerticalOrder(Node* root, int hd, map> &m)
{
    // Base case
    if (root == NULL)
        return;
 
    // Store current node in map 'm'
    m[hd].push_back(root->key);
 
    // Store nodes in left subtree
    getVerticalOrder(root->left, hd-1, m);
 
    // Store nodes in right subtree
    getVerticalOrder(root->right, hd+1, m);
}
 
// The main function to print vertical order of a binary tree
// with the given root
void printVerticalOrder(Node* root)
{
    // Create a map and store vertical order in map using
    // function getVerticalOrder()
    map < int,vector > m;
    int hd = 0;
    getVerticalOrder(root, hd,m);
 
    // Traverse the map and print nodes at every horigontal
    // distance (hd)
    map< int,vector > :: iterator it;
    for (it=m.begin(); it!=m.end(); it++)
    {
        for (int i=0; isecond.size(); ++i)
            cout << it->second[i] << " ";
        cout << endl;
    }
}
 
// Driver program to test above functions
int main()
{
    Node *root = newNode(1);
    root->left = newNode(2);
    root->right = newNode(3);
    root->left->left = newNode(4);
    root->left->right = newNode(5);
    root->right->left = newNode(6);
    root->right->right = newNode(7);
    root->right->left->right = newNode(8);
    root->right->right->right = newNode(9);
    cout << "Vertical order traversal is n";
    printVerticalOrder(root);
    return 0;
}


Java
// Java program for printing vertical order of a given binary tree
import java.util.TreeMap;
import java.util.Vector;
import java.util.Map.Entry;
 
public class VerticalOrderBtree
{
    // Tree node
    static class Node
    {
        int key;
        Node left;
        Node right;
         
        // Constructor
        Node(int data)
        {
            key = data;
            left = null;
            right = null;
        }
    }
     
    // Utility function to store vertical order in map 'm'
    // 'hd' is horizontal distance of current node from root.
    // 'hd' is initially passed as 0
    static void getVerticalOrder(Node root, int hd,
                                TreeMap> m)
    {
        // Base case
        if(root == null)
            return;
         
        //get the vector list at 'hd'
        Vector get =  m.get(hd);
         
        // Store current node in map 'm'
        if(get == null)
        {
            get = new Vector<>();
            get.add(root.key);
        }
        else
            get.add(root.key);
         
        m.put(hd, get);
         
        // Store nodes in left subtree
        getVerticalOrder(root.left, hd-1, m);
         
        // Store nodes in right subtree
        getVerticalOrder(root.right, hd+1, m);
    }
     
    // The main function to print vertical order of a binary tree
    // with the given root
    static void printVerticalOrder(Node root)
    {
        // Create a map and store vertical order in map using
        // function getVerticalOrder()
        TreeMap> m = new TreeMap<>();
        int hd =0;
        getVerticalOrder(root,hd,m);
         
        // Traverse the map and print nodes at every horigontal
        // distance (hd)
        for (Entry> entry : m.entrySet())
        {
            System.out.println(entry.getValue());
        }
    }
     
    // Driver program to test above functions
    public static void main(String[] args) {
 
        // TO DO Auto-generated method stub
        Node root = new Node(1);
        root.left = new Node(2);
        root.right = new Node(3);
        root.left.left = new Node(4);
        root.left.right = new Node(5);
        root.right.left = new Node(6);
        root.right.right = new Node(7);
        root.right.left.right = new Node(8);
        root.right.right.right = new Node(9);
        System.out.println("Vertical Order traversal is");
        printVerticalOrder(root);
    }
}
// This code is contributed by Sumit Ghosh


Python
# Python program for printing vertical order of a given
# binary tree
 
# A binary tree node
class Node:
    # Constructor to create a new node
    def __init__(self, key):
        self.key = key
        self.left = None
        self.right = None
 
# Utility function to store vertical order in map 'm'
# 'hd' is horizontal distance of current node from root
# 'hd' is initially passed as 0
def getVerticalOrder(root, hd, m):
 
    # Base Case
    if root is None:
        return
     
    # Store current node in map 'm'
    try:
        m[hd].append(root.key)
    except:
        m[hd] = [root.key]
     
    # Store nodes in left subtree
    getVerticalOrder(root.left, hd-1, m)
     
    # Store nodes in right subtree
    getVerticalOrder(root.right, hd+1, m)
 
# The main function to print vertical order of a binary
#tree ith given root
def printVerticalOrder(root):
     
    # Create a map and store vertical order in map using
    # function getVerticalORder()
    m = dict()
    hd = 0
    getVerticalOrder(root, hd, m)
     
    # Traverse the map and print nodes at every horizontal
    # distance (hd)
    for index, value in enumerate(sorted(m)):
        for i in m[value]:
            print i,
        print
 
 
# Driver program to test above function
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
root.right.left = Node(6)
root.right.right = Node(7)
root.right.left.right = Node(8)
root.right.right.right = Node(9)
print "Vertical order traversal is"
printVerticalOrder(root)
 
# This code is contributed by Nikhil Kumar Singh(nickzuck_007)


C++14
// C++ program for printing
// vertical order of a given binary
// tree
#include 
using namespace std;
 
struct Node {
    int data;
    Node *left, *right;
};
 
struct Node* newNode(int data)
{
    struct Node* node = new Node;
    node->data = data;
    node->left = node->right = NULL;
    return node;
}
 
// Store vertical order
// in map "m", hd = horizontal
// distance, vd = vertical distance
void preOrderTraversal(Node* root,
                       long long int hd,
                       long long int vd,
                       map >& m)
{
    if (!root)
        return;
    // key = horizontal
    // distance (30 bits) + vertical
    // distance (30 bits) map
    // will store key in sorted
    // order. Thus nodes having same
    // horizontal distance
    // will sort according to
    // vertical distance.
    long long val = hd << 30 | vd;
 
    // insert in map
    m[val].push_back(root->data);
 
    preOrderTraversal(root->left, hd - 1, vd + 1, m);
    preOrderTraversal(root->right, hd + 1, vd + 1, m);
}
 
void verticalOrder(Node* root)
{
    // map to store all nodes in vertical order.
    // keys will be horizontal + vertical distance.
    map > mp;
 
    preOrderTraversal(root, 0, 1, mp);
 
    // print map
    int prekey = INT_MAX;
    map >::iterator it;
    for (it = mp.begin(); it != mp.end(); it++) {
        if (prekey != INT_MAX
            && (it->first >> 30) != prekey) {
            cout << endl;
        }
        prekey = it->first >> 30;
        for (int j = 0; j < it->second.size(); j++)
            cout << it->second[j] << " ";
    }
}
 
// Driver code
int main()
{
    Node* root = newNode(1);
    root->left = newNode(2);
    root->right = newNode(3);
    root->left->left = newNode(4);
    root->left->right = newNode(5);
    root->right->left = newNode(6);
    root->right->right = newNode(7);
    root->right->left->right = newNode(8);
    root->right->right->right = newNode(9);
    cout << "Vertical order traversal :- " << endl;
    verticalOrder(root);
    return 0;
}


Java
// Java Program for above approach
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
 
class Node {
    int data;
    Node left, right;
 
    Node(int item)
    {
        data = item;
        left = right = null;
    }
}
 
public class BinaryTree {
 
    Node root;
 
    // Values class
    class Values {
        int max, min;
    }
 
    // Program to find vertical Order
    public void verticalOrder(Node node)
    {
        Values val = new Values();
 
        // Create TreeMap
        Map > map
            = new TreeMap >();
 
        // Function Call to findHorizonatalDistance
        findHorizonatalDistance(node, val, val, 0, map);
 
        // Iterate over map.values()
        for (List list : map.values()) {
            System.out.println(list);
        }
 
        // Print "done"
        System.out.println("done");
    }
 
    // Program to find Horizonatal Distance
    public void findHorizonatalDistance(
        Node node, Values min, Values max, int hd,
        Map > map)
    {
 
        // If node is null
        if (node == null)
            return;
 
        // if hd is less than min.min
        if (hd < min.min)
            min.min = hd;
 
        // if hd is greater than min.min
        if (hd > max.max)
            max.max = hd;
 
        // Using computeIfAbsent
        map.computeIfAbsent(hd,
                            k -> new ArrayList())
            .add(node.data);
 
        // Function Call with hd equal to hd - 1
        findHorizonatalDistance(node.left, min, max, hd - 1,
                                map);
 
        // Function Call with hd equal to hd + 1
        findHorizonatalDistance(node.right, min, max,
                                hd + 1, map);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
 
        BinaryTree tree = new BinaryTree();
 
        /* Let us construct the tree shown
                             in above diagram */
        tree.root = new Node(1);
        tree.root.left = new Node(2);
        tree.root.right = new Node(3);
        tree.root.left.left = new Node(4);
        tree.root.left.right = new Node(5);
        tree.root.right.left = new Node(6);
        tree.root.right.right = new Node(7);
        tree.root.right.left.right = new Node(8);
        tree.root.right.right.right = new Node(9);
 
        System.out.println("vertical order traversal is :");
 
        // Function Call
        tree.verticalOrder(tree.root);
    }
}


基于散列的解决方案的时间复杂度可以被认为是 O(n),前提是我们有良好的散列函数,允许在 O(1) 时间内进行插入和检索操作。在上面的 C++ 实现中,使用了 STL 的映射。 STL 中的映射通常使用自平衡二叉搜索树实现,其中所有操作都需要 O(Logn) 时间。因此上述实现的时间复杂度为 O(nLogn)。
请注意,上述解决方案可能不会以与树中出现的相同垂直顺序打印节点。例如,上面的程序在 9 之前打印 12。有关示例运行,请参见此处。

1
          /    \ 
         2       3
        /      /  \
       4    5  6    7
                  /  \
                 8 10  9 
                     \
                     11
                       \
                        12

有关基于级别顺序遍历的解决方案,请参阅下面的帖子。下面的帖子确保垂直线的节点按照它们在树中出现的顺序打印。
按垂直顺序打印二叉树 |设置 3(使用水平顺序遍历)

使用先序遍历方法,保持节点的顺序与它们在树中出现的垂直顺序相同:

我们还可以按照它们出现在树中的相同垂直顺序来维护节点的顺序。具有相同水平距离的节点将根据水平顺序打印。

例如,在下图中,9 和 12 具有相同的水平距离。我们可以确保如果像 12 这样的节点在同一条垂直线上,它会打印在像 9 这样的节点之后

思路:我们不使用水平距离作为地图中的key,而是使用水平距离+垂直距离作为key。我们知道在二叉树中节点的数量不能超过整数范围。

我们将使用前 30 位密钥作为水平距离 [MSB 到 LSB],并将使用后 30 位作为垂直距离。因此,密钥将根据我们的要求存储在地图中。

下面是上述方法的实现。

C++14

// C++ program for printing
// vertical order of a given binary
// tree
#include 
using namespace std;
 
struct Node {
    int data;
    Node *left, *right;
};
 
struct Node* newNode(int data)
{
    struct Node* node = new Node;
    node->data = data;
    node->left = node->right = NULL;
    return node;
}
 
// Store vertical order
// in map "m", hd = horizontal
// distance, vd = vertical distance
void preOrderTraversal(Node* root,
                       long long int hd,
                       long long int vd,
                       map >& m)
{
    if (!root)
        return;
    // key = horizontal
    // distance (30 bits) + vertical
    // distance (30 bits) map
    // will store key in sorted
    // order. Thus nodes having same
    // horizontal distance
    // will sort according to
    // vertical distance.
    long long val = hd << 30 | vd;
 
    // insert in map
    m[val].push_back(root->data);
 
    preOrderTraversal(root->left, hd - 1, vd + 1, m);
    preOrderTraversal(root->right, hd + 1, vd + 1, m);
}
 
void verticalOrder(Node* root)
{
    // map to store all nodes in vertical order.
    // keys will be horizontal + vertical distance.
    map > mp;
 
    preOrderTraversal(root, 0, 1, mp);
 
    // print map
    int prekey = INT_MAX;
    map >::iterator it;
    for (it = mp.begin(); it != mp.end(); it++) {
        if (prekey != INT_MAX
            && (it->first >> 30) != prekey) {
            cout << endl;
        }
        prekey = it->first >> 30;
        for (int j = 0; j < it->second.size(); j++)
            cout << it->second[j] << " ";
    }
}
 
// Driver code
int main()
{
    Node* root = newNode(1);
    root->left = newNode(2);
    root->right = newNode(3);
    root->left->left = newNode(4);
    root->left->right = newNode(5);
    root->right->left = newNode(6);
    root->right->right = newNode(7);
    root->right->left->right = newNode(8);
    root->right->right->right = newNode(9);
    cout << "Vertical order traversal :- " << endl;
    verticalOrder(root);
    return 0;
}
输出

Vertical order traversal :- 
4 
2 
1 5 6 
3 8 
7 
9

上述实现的时间复杂度为 O(n Log n)。

辅助空间:O(n)

另一种使用 computeIfAbsent 方法的方法:

我们可以用更简洁的方式编写代码,通过在Java使用map的computeIfAbsent方法,并使用树图进行基于键的自然排序。

下面是上述方法的实现。

Java

// Java Program for above approach
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
 
class Node {
    int data;
    Node left, right;
 
    Node(int item)
    {
        data = item;
        left = right = null;
    }
}
 
public class BinaryTree {
 
    Node root;
 
    // Values class
    class Values {
        int max, min;
    }
 
    // Program to find vertical Order
    public void verticalOrder(Node node)
    {
        Values val = new Values();
 
        // Create TreeMap
        Map > map
            = new TreeMap >();
 
        // Function Call to findHorizonatalDistance
        findHorizonatalDistance(node, val, val, 0, map);
 
        // Iterate over map.values()
        for (List list : map.values()) {
            System.out.println(list);
        }
 
        // Print "done"
        System.out.println("done");
    }
 
    // Program to find Horizonatal Distance
    public void findHorizonatalDistance(
        Node node, Values min, Values max, int hd,
        Map > map)
    {
 
        // If node is null
        if (node == null)
            return;
 
        // if hd is less than min.min
        if (hd < min.min)
            min.min = hd;
 
        // if hd is greater than min.min
        if (hd > max.max)
            max.max = hd;
 
        // Using computeIfAbsent
        map.computeIfAbsent(hd,
                            k -> new ArrayList())
            .add(node.data);
 
        // Function Call with hd equal to hd - 1
        findHorizonatalDistance(node.left, min, max, hd - 1,
                                map);
 
        // Function Call with hd equal to hd + 1
        findHorizonatalDistance(node.right, min, max,
                                hd + 1, map);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
 
        BinaryTree tree = new BinaryTree();
 
        /* Let us construct the tree shown
                             in above diagram */
        tree.root = new Node(1);
        tree.root.left = new Node(2);
        tree.root.right = new Node(3);
        tree.root.left.left = new Node(4);
        tree.root.left.right = new Node(5);
        tree.root.right.left = new Node(6);
        tree.root.right.right = new Node(7);
        tree.root.right.left.right = new Node(8);
        tree.root.right.right.right = new Node(9);
 
        System.out.println("vertical order traversal is :");
 
        // Function Call
        tree.verticalOrder(tree.root);
    }
}

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程