二叉树常考面试题

树是n(n>=0)个有限个数据的元素集合,形状像一颗倒过来的树。

结点:结点包含数据和指向其它结点的指针。

结点的度:结点拥有的子节点个数。

叶子节点:没有子节点的节点(度为0)。

父子节点:一个节点father指向另一个节点child,则child为孩子节点,father为父亲结点。

兄弟节点:具有相同父节点的节点互为兄弟节点。

节点的祖先:从根节点开始到该节点所经的所有节点都可以称为该节点的祖先。

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。

树的高度:树中距离根节点最远结点的路径长度。


树的存储结构:

二叉树常考面试题

  • 二叉树的结构

二叉树常考面试题

二叉树:二叉树是一棵特殊的树,二叉树每个节点最多有两个孩子结点,分别称为左孩子和右孩子。

满二叉树:高度为N的满二叉树有2^N - 1个节点的二叉树。

完全二叉树: 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树

前序遍历(先根遍历):

(1):先访问根节点;  

(2):前序访问左子树;

(3):前序访问右子树;

中序遍历:       

(1):中序访问左子树

(2):访问根节点;    

(3):中序访问右子树;  

后序遍历(后根遍历):

(1):后序访问左子树;

(2):后序访问右子树;

(3):访问根节点;     

层序遍历:         

(1):一层层节点依次遍历。

下面是二叉树的具体实现:                                

template

struct BinaryTreeNode

{

           BinaryTreeNode *_left;

           BinaryTreeNode *_right;

           T _data;


};


template

class BinaryTree

{

          Typedef BinaryTreeNode< T> Node;

protected:

          Node *_root;

public:

          BinaryTree() //无参构造函数

                   :_root( NULL)

          {

          }

          BinaryTree( const T *a, size_t size, const T& invalid)

          {

                    size_t index = 0;

                   _root = _CreateTree( a, size , invalid, index);

          }

protected:

          Node *__CreateTree( const T *a, size_t size, const T& invalid, size_t &index )

          {

                   Node *root = NULL;

                    if (index < size && a[index ] != invalid) //是有效值时

                   {

                             root = new Node(a [index]);

                             root->_left = __CreateTree( a, size , invalid, ++ index);

                             root->_right = __CreateTree( a, size , invalid, ++index);

          }

                    return root;

          }


//前序遍历--------递归写法,缺点是:有大量的压栈开销。

           void Prevorder(Node *root )

          {

                    if (root == NULL)

                   {

                              return;

                   }

                    else

                   {

                             cout << root->_data << " " ;

                             _prevorder( root->_left);

                             _prevorder( root->_right);

                   }

          }


//前序遍历------------非递归写法

//前序遍历的非递归写法思想:需要借助栈。

           void PrevOrderRonR()

          {

                    stack s;

                    if (_root == NULL )//根结点为空的话直接return掉即可。

                   {

                              return;

                   }

                    if (_root)

                   {

                             s.push(_root); //根不为空的时候将根结点进行压栈。

                   }

                    while (!s.empty())//判断栈是否为空

                   {

                             Node *top = s.top(); //栈不为空,则取栈顶元素

                             cout << top->_data << " ";//然后进行访问栈顶元素

                             s.pop(); //访问完栈顶元素将其从栈中pop掉。

                              if (top->_right)//要根据栈进行先序遍历,则必须是先访问根节点,再访问左子树,最后访问右子树,因为栈是“后进先出的”,要想先访问左子树,则必须先入右子树,再入左子树。如果栈顶元素的右子树不为空,

                             {

                                      s.push(top->_right); //栈顶的右子树不为空,将其进行压栈。

                             }

                              if (top->_left)

                             {

                                      s.push(top->_left); //栈顶的左子树不为空,将其进行压栈。

                             }

                             

                   }

                   cout << endl;

          }



//中序遍历----------递归写法

           void _Inorder(Node *root )

          {

                    if (root == NULL)

                   {

                              return;

                   }

                    else

                   {

                             _Inorder(Node * root)

                             {

                                       if (root == NULL )

                                      {

                                                 return;

                                      }

                                       else

                                      {

                                                _Inorder(root->_left);

                                                cout << root->_data << " " ;

                                                Inorder(root->_right);

                                      }

                             }

                   }

          }


//中序遍历的非递归写法,思想是:也是借助栈,主要核心是找最左结点,定义一个cur指针,让它最开始指向_root。


           void TnOrderNonR()

          {

                    stack s;

                   Node *cur = _root;

                    while (cur || !s.empty())

                   {

                             whie(cur) //找最左结点

                             {

                                      s.push(cur); //将cur压栈。

                                      cur = cur->_left; //cur指向它的左孩子

                             }

                             Node *top = s.top();

                             cout << top->_data << " ";

                             s.pop();

                             cur = top->_right;

                   }

          }


//后序遍历---------递归写法

           void Postorder(Node *root )

          {

                    if (root == NULL)

                   {

                              return;

                   }

                    else

                   {

                             Postorder( root->_left);

                             Postorder( root->_right);

                             cout << root->_data << " " ;

                   }

          }


//后序遍历----------非递归写法,思想是:先找最左结点,找到后但不能访问最左结点,要先判断最左结点的右子树是否为空,若为空, 则可以访问最左结点,否则不可以访问最左结点,需要访问右子树。

//可以访问根结点的条件:上一层访问的节点为右子树。所以我们需要定义两个指针prev与cur ,cur用来保存当前结点,prev用来保存上一层访问的结点。        

           void PostOrderNonR()

          {

                    stack s;

                   Node *prev = NULL;

                   Node *cur = _root;

                    while (cur || !s.empty())

                   {

                              while (cur)//找最左结点

                             {

                                      s.push(cur);

                                      cur = cur->_left;

                             }

                             Node *top = s.top(); //定义一个栈顶指针,用来指向栈顶元素。

                              if (top->_right == NULL || top->_right == prev)//栈顶节点的右子树为空或者上一次访问的节点为右子树,则可以访问栈顶元素。

                             {

                                      cout << top->_data << " " ;

                                      s.pop();

                                      prev = top;

                             }

                              else

                             {

                                      cur = top->_left;

                             }

                   }

          }

//二叉树的层序遍历(即是一层一层的进行遍历):思想是:需要借助队列,首先取队头,判断它是否为空,若为空直接return;不为空的时候,进行入队操作。

//如何取到队头?入数据还是入指针?最好入指针,需要保存数据或者节点的时候最好入指针。

           void LevelOrder()

          {

                    queue q;

                    if (_root == NULL )

                   {

                              return;

                   }

                   q.push(_root);

                    while (!q.empty())

                   {

                             Node *front = q.front(); //取队头元素

                             q.pop();

                             cout << front->_data<< " ";

                              if (front->_left)//队头元素的左孩子不为空的时候,将它的左孩子压入队列

                             {

                                      q.push(front->_left);

                             }

                              if (front->_right)//队头元素的右孩子不为空的时候,将它的右孩子压入队列

                             {

                                      q.push(front->_right);

                             }

                   }


                   

          }



           size_t _Depth(Node *root )//思想:当前深度=(左子树和右子树中深度较大的一个)+1;

          {

                   if(root == NULL)

                   {

                  return 0;

              }

                    int left = _Depth(root->_left);

                    int right = _Depth(root ->_right);

                    return left > right ? left + 1 : right + 1;

          }


           size_t _GetKLevel(Node *root , size_t K)//取第K层结点,递归写法。

          {

                    if (root == NULL)

                   {

                              return 0;

                   }

                    if (K == 1)

                   {

                              return 1;

                   }

                    return _GetKLevel(root ->_left, K - 1) + _GetKLevel(root->_right, K - 1);

          }

          


          Node* _Find(Node * root, const T& x)//查找结点为x的结点

          {

                    if (root == NULL)

                   {

                              return NULL ;

                   }

                    if (root ->_data == x)

                   {

                              return root ;

                   }

                   Node *ret = _Find( root->_left, x );

                    if (ret)

                   {

                              return ret;

                   }

                    else

                   {

                              return _Find(root ->_right, x);

                   }

          }



           size_t _leafsize(Node *root )//求叶子节点的个数,思想:左子树的叶子结点数目+右子树的叶子结点的数目。

          {

                    if (root == NULL)

                   {

                              return 0;

                   }

                    if (root ->_left == NULL&& root->_right == NULL )

                   {

                              return 1;

                   }

                    return _leafsize(root ->_left) + _leafsize(root->_right);

          }

           //递归即是=子问题+返回条件

           //方法一:

           size_t _size(Node *root )//结点的数目

          {

                    if (root == NULL)

                   {

                              return 0;

                   }

                    return _size(root ->_left) + _size(root->_right) + 1;

          }


           //方法二:遍历法

           size_t _size(Node *root)

          {

                    static size_t sSize = 0;//此句代码会让程序有线程安全问题

                    if (root == NULL )

                   {

                              return sSize;

                   }

                   ++sSize;

                   _size(root->_left);

                   _size(root->_right);

                    return sSize;6

          }


};


新闻名称:二叉树常考面试题
当前地址:http://cdiso.cn/article/psosig.html

其他资讯