1. 程式人生 > >【2013微軟校招面試題】輸出節點數為n的二叉樹的所有形態

【2013微軟校招面試題】輸出節點數為n的二叉樹的所有形態

 /*
 *  題意,求節點數為n的二叉樹的所有形態,先要想個方式來唯一標示一棵二叉樹
 *
 *  方法一:一個前序+一箇中序,可以還原一棵唯一的二叉樹,故使用【前序輸出的字串+中序輸出的字串】
 *  來唯一標示一棵二叉樹。
 *
 *  方法二:【將一顆二叉樹逐層遍歷,若節點不為空,則記為X,為空記為O,最終得到的序列可以唯一標示一顆二叉樹。】
 *
 *  建樹過程採用遞迴,對已經建成的樹的部分,每次節點逐一判斷其左右兒子是否可以插入,可以的話,則插入,然後遞迴。
 *  直到數的大小達到n,則記錄這棵樹的唯一標示,然後返回!
 *
 *  output:  例如輸入3,
 *  則輸出結果,對應的二叉樹如下所示:  
 *                  X               X                X                  X                     X
 *              X    O         X    O         O    X           O   X              X      X
 *           X   O         O   X                  X   O           O   X        O  O  O  O
 *        O   O               O   O           O   O                  O  O
 */

程式碼如下所示:

#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;

int n;   //二叉樹有n個節點
map<string, int> m;

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

void PreOut(Node * head, string &s)  //先序輸出,根左右
{
 s += "X";       //根
 if(head->left != NULL)   //左
  PreOut(head->left, s);
 else
  s += "O";
 if(head->right != NULL)  //右
  PreOut(head->right, s);
 else
  s += "O";
}

void MidOut(Node * head, string &s)  //中序輸出,左根右
{
 if(head->left != NULL)   //左
  MidOut(head->left, s);
 else
  s += "O";
 s += "X";
 if(head->right != NULL)  //右
  MidOut(head->right, s);
 else
  s += "O";

}

//逐層遍歷二叉樹
void Bfs(Node * head, string &s)
{
 vector<Node *> vec;
 vec.push_back(head);
 for(int i=0; i<vec.size(); i++)
 {
  if(vec[i] != NULL)
  {
   s += "X";
   vec.push_back(vec[i]->left);  //左兒子入隊,為NULL的時候也入隊
   vec.push_back(vec[i]->right); //右兒子入隊
  }
  else
   s += "O";
 }
}

 /*
 * head: 為整棵樹的根節點
 * arr:  為二叉樹節點的陣列
 * now:  為當前已經建立的樹的節點數
 * total:為輸入n,總共的節點數
 * 建樹時間複雜度: 卡特蘭數複雜度  C(2*n,n)/(n+1)
 */
void BuildAllKindsTree(Node * head, Node * arr, int now, int total)
{
 if(now == total)  //成功建立一棵樹
 {
  string s = "";
  //PreOut(head, s);
  //MidOut(head, s);
  Bfs(head, s);
  m[s]++;
  return;
 }

 for(int i=0; i<now; ++i) //當前建立的樹已經有 now 個節點
 {
  if( arr[total - 1 - i].left == NULL ) //該節點左兒子位置可以連結個節點
  {
   arr[total - 1 - i].left = &arr[total - 1 - now];
   BuildAllKindsTree(head, arr, now+1, total);
   arr[total - 1 - i].left = NULL;  //遞歸回溯
  } 
  if( arr[total - 1 - i].right == NULL)  //該節點右兒子位置可以連結個節點
  {
   arr[total - 1 - i].right = &arr[total - 1 - now];
   BuildAllKindsTree(head, arr, now+1, total);
   arr[total - 1 - i].right = NULL;
  }
 }
}

void output()
{
 cout<<"*****************************************************************"<<endl;
 map<string, int>::iterator iter = m.begin();
 for(; iter != m.end(); iter++)
  cout<<iter->first<<endl;
 cout<<n<<"個節點的時候,二叉樹總共有"<<m.size()<<"種情況。"<<endl;
 cout<<"*****************************************************************"<<endl;
}

int main()
{
 while(cin>>n)  
 {
  m.clear();
  Node * a = new Node[n];  //new出n個節點
  for(int i=0; i<n; i++) 
  {
   a[i].left = NULL;
   a[i].right = NULL;  
  }

  BuildAllKindsTree(&a[n-1], a, 1, n); 

  output();
  delete []a;
 }
}