【问题标题】:Print bottom view of a binary tree打印二叉树的底视图
【发布时间】:2014-03-19 10:06:49
【问题描述】:

对于二叉树,我们定义水平距离如下:

    Horizontal distance(hd) of root = 0
    If you go left then hd = hd(of its parent)-1, and 
    if you go right then hd = hd(of its parent)+1.

然后树的底部视图由树的所有节点组成,其中没有具有相同hd 和更高级别的节点。 (对于给定的hd 值,可能有多个这样的节点。在这种情况下,它们都属于底部视图。)我正在寻找一种输出树底部视图的算法。


例子:

假设二叉树是:

         1
        /  \
       2    3
      / \  / \
     4   5 6  7
            \
             8

树的底视图是:4 2 5 6 8 7

    Ok so for the first example,
    Horizontal distance of node with value 1: 0, level = 1
    Horizontal distance of node with value 2: 0 - 1 = -1, level = 2
    Horizontal distance of node with value 3: 0 + 1 = 1, level = 2
    Horizontal distance of node with value 4: -1 - 1 = -2, level = 3
    Horizontal distance of node with value 5: -1 + 1 = 0, level = 3
    Horizontal distance of node with value 6: 1 - 1 = 0, level = 3
    Horizontal distance of node with value 7: 1 + 1 = 2, level = 3
    Horizontal distance of node with value 8: 0 + 1 = 1, level = 4

    So for each vertical line that is for hd=0, print those nodes which appear in the last level of that line.
    So for hd = -2, print 4
    for hd = -1, print 2
    for hd = 0, print 5 and 6 because they both appear in the last level of that vertical line
    for hd = 1, print 8
    for hd = 2, print 7

再举一个例子供参考:

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

所以输出将是: 8 4 9 10 12 5 6 11 13 14 7 15

Similarly for this example
hd of node with value 1: 0, , level = 1
hd of node with value 2: -1, level = 2
hd of node with value 3: 1, level = 2
hd of node with value 4: -2, level = 3
hd of node with value 5: 0, , level = 3
hd of node with value 6: 0, level = 3
hd of node with value 7: 2, level = 3
hd of node with value 8: -3, level = 4
hd of node with value 9: -1, level = 4
hd of node with value 10: -1, level = 4
hd of node with value 11: 1, level = 4
hd of node with value 12: -1, level = 4
hd of node with value 13: 1, level = 4
hd of node with value 14: 1, level = 4
hd of node with value 15: 3, level = 4

So, the output will be:
hd = -3, print 8
hd = -2, print 4
hd = -1, print 9 10 12
hd = 0, print 5 6
hd = 1, print 11 13 14
hd = 2, print 7
hd = 3, print 15 

So the ouput will be:
8 4 9 10 12 5 6 11 13 14 7 15

我已经知道一种方法,我可以使用大量额外空间(一个地图和一个用于存储该垂直线中最后一个元素的级别的一维数组)并且时间复杂度为 $O (N \log N)$。 这是这个方法的实现:

#include <iostream>
#include <cstdio>
#include <map>
#include <vector>

using namespace std;

struct Node{
       int data;
       struct Node *left, *right;
};

Node* newNode(int data)
{
    Node *temp = new Node;
    temp->data = data;
    temp->left = temp->right = NULL;
    return temp;
}

int height(Node *node)
{
    if(node == NULL)
            return 0;
    else{
         int lh = height(node->left);
         int rh = height(node->right);

         if(lh > rh)
               return (lh+1);
         else
               return (rh+1);
    }
}

void printBottom(Node *node, int level, int hd, int min, map< int, vector<int> >& visited, int lev[], int l)
{
     if(node == NULL)
             return;
     if(level == 1){
              if(lev[hd-min] == 0 || lev[hd-min] == l){
                      lev[hd-min] = l;
                      visited[hd-min].push_back(node->data);
              }
     }
     else if(level > 1)
     {
          printBottom(node->left, level-1, hd-1, min, visited, lev, l);
          printBottom(node->right, level-1, hd+1, min, visited, lev, l);
     }
}

void findMinMax(Node *node, int *min, int *max, int hd)
{
     if(node == NULL)
             return;

     if(hd < *min)
          *min = hd;
     else if(hd > *max)
          *max = hd;

     findMinMax(node->left, min, max, hd-1);
     findMinMax(node->right, min, max, hd+1);
}

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->left->left->left = newNode(8);
    root->left->left->right = newNode(9);
    root->left->right->left = newNode(10);
    root->left->right->right = newNode(11);
    root->right->left->left = newNode(12);
    root->right->left->right = newNode(13);
    root->right->right->left = newNode(14);
    root->right->right->right = newNode(15);

    int min = 0, max = 0;

    findMinMax(root, &min, &max, 0);

    int lev[max-min+1];
    map < int, vector<int> > visited;
    map< int,vector<int> > :: iterator it;

    for(int i = 0; i < max-min+1; i++)
            lev[i] = 0;

    int h = height(root);

    for (int i=h; i>0; i--){
        printBottom(root, i, 0, min, visited, lev, i);
    }

    for(it = visited.begin() ; it != visited.end() ; it++) {
        for(int i=0 ; i < it->second.size() ; i++) {
            cout << it->second[i] << " ";
        }
    }

    return 0;
}

我正在寻求帮助以更优化的方式执行此操作,从而使用更少的空间或时间。有没有其他有效的方法来解决这个问题?

【问题讨论】:

  • 为什么最后一个例子的输出不是8 4 9 10 5 11 12 6 13 14 7 15?还是顺序不重要?
  • 顺序很重要。我现在已经解释了这些例子,希望它变得清晰。
  • 我可以知道投反对票的原因吗?问题很明确。

标签: c++ algorithm tree binary-tree


【解决方案1】:

首先,您可以将时间复杂度降低到 O(n),同时保持相同的空间复杂度。您可以通过在 printBottom 的一次运行中填写 visited 来做到这一点:

void printBottom(Node *node, int level, int hd, int min, map< int, vector<int> >& visited, int lev[])
{
     if(node == NULL)
             return;
     if(lev[hd-min] < level){
         lev[hd-min] = level;
         visited[hd-min] = new vector<int>; //erase old values, they are hidden by the current node
     }
     if(lev[hd-min] <= level){
         visited[hd-min].push_back(node->data);
     }
     printBottom(node->left, level+1, hd-1, min, visited, lev);
     printBottom(node->right, level+1, hd+1, min, visited, lev);
}

初始呼叫printBottom(root, 1, 0, min, visited, lev);

如果你坚持以hd的值递增的顺序输出节点,我认为你不能提高空间消耗。然而,如果你允许不同的输出顺序,你可以去掉visited,首先确定'hd'的每个值,应该输出哪个级别,然后再进行一次传递,打印匹配的值:

void fillLev(Node *node, int level, int hd, int min, int lev[])
{
     if(node == NULL)
             return;
     if(lev[hd-min] < level){
         lev[hd-min] = level;
     }
     fillLev(node->left, level+1, hd-1, min, lev);
     fillLev(node->right, level+1, hd+1, min, lev);
}
void printBottom(Node *node, int level, int hd, int min, int lev[])
{
     if(node == NULL)
             return;
     if(lev[hd-min] == level){
         cout << node->data;
     }
     printBottom(node->left, level+1, hd-1, min, lev);
     printBottom(node->right, level+1, hd+1, min, lev);
}

调用fillLev(root, 1, 0, min, lev);printBottom(root, 1, 0, min, lev);

【讨论】:

    【解决方案2】:
    void bottomView(node *root)
    {
        if(!root)
            return ;
        bottomView(root->left);
        if(!root->left || !root->right)
            cout<<"\t"<<root->data;
        if ((root->right && !root->right->left) && (root->left &&!root->left->right))
            cout<<"\t"<<root->data;
        bottomView(root->right);
    }
    

    【讨论】:

      【解决方案3】:

      下面给出java中的解决方案,

      初始调用是,

      boolean obstructionFromLeftSide = printBottomViewOrderOfTree(root.left, true);
      boolean obstructionFromRightSide = printBottomViewOrderOfTree(root.right, false);
      if (!(obstructionFromLeftSide || obstructionFromRightSide))
          out.println(root.data + " ");
      

      这里给出了功能,

          boolean printBottomViewOrderOfTree(Node root, boolean fromLeftSide)
         {
          if (root == null)
              return false;
          boolean obstructionFromLeftSide = printBottomViewOrderOfTree(root.left, true);
          boolean obstructionFromRightSide = printBottomViewOrderOfTree(root.right, false);
          if (!(obstructionFromLeftSide || obstructionFromRightSide))
              out.println(root.data + " ");
          if (fromLeftSide)
          {
              return root.right != null;
          }
          else
          {
              return root.left != null;
          }
      }
      

      【讨论】:

        【解决方案4】:

        你考虑过使用基于水平距离和水平的HashMap吗? 在 C++ 中,我们可以有一个像这样的 HashMap:

        map<int,map<int,vector<int>>> HM;    
        // Let Horizontal Distance = HD,Level = L  
        // HM[HD][L] -> Vector tracking every node for a given HD,L
        

        这种方法具有 Time=O(n) 并且在删除笨拙的 fillLev() 函数方面对您的代码进行了改进。我们在这里所做的只是单树遍历和单哈希图遍历。这是代码:

        void getBottomView(struct node *tree,int HD,int L,map<int,map<int,vector<int>>> &HM)
        {
            if(tree==NULL)
                return;
        
            HM[HD][L].push_back(tree->data);
            getBottomView(tree->left,HD-1,L+1,HM);
            getBottomView(tree->right,HD+1,L+1,HM);
        }
        
        void printBottomViewbyMap(map<int,map<int,vector<int>>> &HM)
        {
            map<int,map<int,vector<int>>>::iterator i;
        
            for(i=HM.begin() ; i!=HM.end() ; i++)
            {
                if(i->second.size()==1)
                {
                    map<int,vector<int>>::iterator mapi;
                    mapi = i->second.begin();
                    for(int j=0 ; j<= mapi->second.size()-1 ; j++)
                        cout<<mapi->second[j]<<" ";
                }
                else
                {
                    map<int,vector<int>>::reverse_iterator mapi;
                    mapi = i->second.rbegin();
                    for(int j=0 ; j<= mapi->second.size()-1 ; j++)
                        cout<<mapi->second[j]<<" ";
                }
            }
        } 
        
        void printBottomView(struct node *tree)
        {
            map<int,map<int,vector<int>>> HM;
            getBottomView(tree,0,0,HM);
            printBottomViewbyMap(HM);
        }
        

        【讨论】:

          【解决方案5】:

          如果您仔细观察算法,您只会逐渐到达更高水平级别的节点。如果我们有一个数组(我们不能因为负水平距离),我们只需要做 A[horizo​​ntalDistance] = node。

          然后遍历这个数组打印底视图。

          这会起作用,因为数组将存储特定水平距离的最底部元素,因为我们正在执行级别顺序遍历。

          现在要解决负索引问题,创建一个名为 BiDirectionalList 的类。在 java 中可以使用两个 ArrayList,在 C++ 中可以使用两个 std::vector。

          我已经把代码贴在这里了:http://tech.prakyg.com/2017/07/14/printing-bottom-view-of-binary-tree-in-on-time-without-using-a-map/

          但这里是您需要编码的 BiDirectionalList:

          public class BiDirectionalList<T> {
          List<T> forward;
          List<T> reverse;
          
          public BiDirectionalList() {
              forward = new ArrayList<>();
              reverse = new ArrayList<>();
              reverse.add(null); //0 index of reverse list will never be used
          }
          
          public int size() {
              return forward.size() + reverse.size()-1;
          }
          
          public boolean isEmpty() {
              if (forward.isEmpty() && reverse.size() == 1) return true;
              return false;
          }
          
          public T get(int index) {
              if (index < 0) {
                  reverse.get(-index);
              } 
              return forward.get(index);
          }
          
          /**
           * Sets an element at given index only if the index <= size.
           * i.e. either overwrites an existing element or increases the size by 1
           * @param index 
           * @param element
           */
          public void set(int index, T element) {
              if (index < 0) {
                  index = -index;
                  if (index  > reverse.size()) throw new IllegalArgumentException("Index can at max be equal to size");
                  else if (reverse.size() == index ) reverse.add(index, element);
                  else reverse.set(index, element);
          
              } else {
                  if (index > forward.size()) throw new IllegalArgumentException("Index can at max be equal to size");
                  else if (forward.size() == index ) forward.add(index, element);
                  else forward.set(index, element);
              }
          }
          }
          

          【讨论】:

            【解决方案6】:

            这里我们要记录树的层次和到根节点的水平距离。

             void printBottom(Node *root,int dif,int level,map<int, pair<int,int> > &map){
                if(root==NULL)return;
                if(map.find(dif)==map.end() || level>= map[dif].second){
                    map[dif] = {root->data,level};
                }
                printBottom(root->left,dif-1,level+1,map);
                printBottom(root->right,dif+1,level+1,map);
            }
            
            void bottomView(Node *root){
                   map<int ,pair<int,int> > map;
                   printBottom(root,0,0,map);
                   for(auto it : map){
                       printf("%d ",it.second.first);
                   }
                }
            

            【讨论】:

              【解决方案7】:

              c#实现:

                      public class node
                      {
                          public int data;
                          public node left, right;
                          public int hd;
                      }
              
                      static node newNode(int item)
                      {
                          node temp = new node();
                          temp.data = item;
                          temp.left = null;
                          temp.right = null;
                          return temp;
                      }
              
                      static void rightViewOfBT(node root)
                      {
                          Queue<node> queue = new Queue<node>();
                          SortedDictionary<int, int> treeMap = new SortedDictionary<int, int>();
                          int hd = 0;
                          root.hd = hd;
                          queue.Enqueue(root);
                          while(queue.Count > 0)
                          {
                              node temp = queue.Peek();
                              queue.Dequeue();
              
                              if (treeMap.ContainsKey(temp.hd))
                              {
                                  treeMap[temp.hd] = temp.data;
                              }
                              else
                              {
                                  treeMap.Add(temp.hd, temp.data);
                              }
              
                              if (temp.left != null)
                              {
                                  temp.left.hd = temp.hd - 1;
                                  queue.Enqueue(temp.left);
                              }
              
                              if (temp.right != null)
                              {
                                  temp.right.hd = temp.hd + 1;
                                  queue.Enqueue(temp.right);
                              }
                          }
                          foreach (var tree in treeMap)
                              Console.Write(tree.Value);
                      }
                      static void Main(string[] args)
                      {
                          node root = newNode(1);
                          root.left = newNode(2);
                          root.right = newNode(3);
                          root.left.left = newNode(4);
                          root.left.right = newNode(5);
                          root.left.left.right = newNode(8);
                          root.left.right.left = newNode(9);
                          root.right.left = newNode(6);
                          root.right.right = newNode(7);
                          root.right.right.right = newNode(11);
                          root.right.left.right = newNode(10);
                          rightViewOfBT(root);
                      }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-04-15
                • 1970-01-01
                • 1970-01-01
                • 2012-06-29
                • 1970-01-01
                • 1970-01-01
                • 2016-01-22
                相关资源
                最近更新 更多