二叉树常见面试题(进阶) – 滴巴戈

一、公共用地题型

1. 两个压紧的比来公共先人;

2. 求二叉树中最远的两个压紧的间隔;

3. 由前序遍历和中序遍历使更新二叉树(如:前序序列:1 2 3 4 5 6 – 中序序列 :3 2 4 1 6 5);

4. 判别一棵树倘若是原封不动的二叉树 ;

5. 将两叉搜索树替换为排序的双链表。。省掉建立新压紧,最适当的调理树中压紧拨弄的暴露;

6.求二叉树的宽度;

7. 判别一棵二叉树倘若是均衡二叉树;

8.判别一颗二叉树倘若是另一颗树的子树。

二、解题思惟探析

1。两个压紧的比来协同先人

两个压紧的比来公共先人可分为三种使适应,别离为:

(1)搜索二叉树,范围搜索二叉树的优点,左子树中间的极度的压紧都决不根压紧。,右子树的极度的压紧都大于脚后跟的压紧。。


条件两个压紧决不根压紧,隐现左子树 ;
条件两个压紧大于脚后跟的压紧,隐现右子树 ;
要不然,左子树中间的两个压紧,右子树中间的一体,目前的压紧是比来的协同先人压紧。。

 1 Node* GetAncestor(Node* root, Node* x1, Node* X2)//1.该二叉树为搜索二叉树 2        {
 3             断言(X1) && X2);
 4if (x1->_data <= root->_data && x2->_data <= root->datum的复数)
 5            {
 6return GetAncestor(根)->_left, x1, X2);//这两个党派决不根压紧。,左子树中间的比来协同先人 7            }
 8elseif (x1->_data > root->_data && x2->_data > root->datum的复数)
 9            {
10return GetAncestor(根)->_right, x1, X2);//这两个党派大于根压紧。,左子树中间的比来协同先人11            }
12else13return root;  //左子树中间的一体,右子树中间的一体,寻觅协同先人1415         }

(2)三叉神经链,二叉树压紧有得分父压紧的拨弄。率先,装备NODE1的父压紧NODE1->父父压紧。,这么将NoDE1的极度的父压紧与NoDE2->父压紧停止相比。,条件发现物两个压紧相当,这么地压紧是比来的协同先人。,直截了当地撤退。条件缺席找到相当的压紧,则将node2的极度的父压紧以次和node1->_parent->_parent作相比……直到node1->_parent==NULL。行为准则如次:

 1     struct BinaryNode   //压紧的构造
 2     {  
 3         BinaryNode* _left;  
 4         BinaryNode* _right;  
 5         BinaryNode* _parent;  
 6         int _data;  
 7       
 8         BinaryNode(const int& datum的复数)  
 9             :_data(datum的复数)  
10             , 左(空)  
11             , 右(空)  
12             , 父(空)  
13         {}  
14     };  
 1   Node * GetLastCommonAncestor(Node * root, Node * node1, Node * node2)  
 2    {  
 3         Node * temp;  
 4while (node1 != 空)  
 5        {  
 6             node1 = node1->_parent;  
 7             temp = node2;  
 8while (高烧) != 空)  
 9            {  
10if (node1 == temp->父亲)  
11return node1;  
12                 temp = temp->_parent;  
13            }  
14        }  
15     }  

算法的时期错综复杂的状态为O(n ^ 2)。,O(n)的替代的算法:

赠送的的两个压紧具有父压紧,如此,这两个压紧可以考虑是两个链表的头压紧。,将两个压紧的比来公共先人压紧转变为求两链表的交点,两个链表的尾压紧都是根压紧。。

 1int Hight(BinaryNode* root, BinaryNode* 压紧)  
 2    {  
 3int len = 0;  
 4for (; node != NULL; node = node->父亲)  
 5             len++;  
 6 7return len;  
 8    }  
 9     BinaryNode* GetLastCommonAncestor(BinaryNode* root, BinaryNode* node1, BinaryNode* node2)  
10    {  
1112if (根) == NULL || node1 == NULL || node2==空)  
13return NULL;  
1415int len1 = Hight(根),node1);  
16int len2 = Hight(根),node2);  
1719for (; len1 > len2; len1--)  
20             node1 = node1->_parent;  
21for (; len2 > len1; len2--)  
22             node2 = node2->_parent;  
2324while (node1 && node2 && node1 != node2)  
25        {  
26             node1 = node1->_parent;  
27             node2 = node2->_parent;  
28        }  
2930if (node1 == node2)  
31return node1;  
32else33return NULL;  
34     }  

(3)普通二叉树,这种使适应可采用与搜索二叉树近亲相干的溶液

从根压紧开端遍历,条件NoDE1和NoDE2中间的究竟哪个一体与根婚配,与根婚配的压紧是最小的的协同先人。。 条件不婚配,这么隐现左、右子树,条件有一体 压紧出如今左子树中,另一体压紧出如今右子树中,根是最小的的协同先人。 条件在左子树中呈现两个压紧,产生弄清,最小协同先人谎言左子树中。,要不然在右子树中。

 1 Node* GetAncestor(Node* root, Node* x1, Node* X2)
 2        {
 3             断言(X1) && X2);
 4if (根) == 空) {
 5return NULL;
 6            }
 7if (根) == x1 || root == X2) //条件两个压紧是爷儿俩相干,内容一体压紧是协同先人。 8            {
 9return root;
10            }
11bool x1inleft, x2inleft, x1inright, x2inright;
12             x1inleft = JudgeNode(根)->_left, X1)  //决定x1倘若在左子树中13             x1inright = JudgeNode(根)->_right X1)  //决定x1倘若在右子树中14             断言(X1)inleft || x1in右)  //至多有一体是真的15             x2inleft = JudgeNode(根)->_left, X2);  //决定x2倘若在左子树中16             x2inright = JudgeNode(根)->_right, X2);  //决定x2倘若在右子树中17             assert(x2inleft || x2in右)  //至多有一体是真的18if ((x1inleft && x2inright) || (x1inright && x2inright))
19            {
20return root;  //左子树中间的一体,右子树中间的一体,寻觅协同先人21            }
22elseif (x1inleft && x2in左)  //这两个党派在左子树中。,左子树中间的比来协同先人23            {
24return GetAncestor(根)->_left, x1, X2);
25            }
26else {  //这两个党派在右子树中。,右子树中间的比来协同先人27return GetAncestor(根)->_right, x1, X2);
28            }
29         }

前述的办法的时期错综复杂的状态为O(n ^ 2)。,时期错综复杂的状态的以下办法是O(n),只因必要额定的住宿来往事路程。。

1) 找到从根到压紧1的路程,并往事在矢径或大楼中。
2)找到从根到压紧2的路程,并往事在矢径或大楼中。
3) 遍历这两条路程,直到尤指不期而遇明显的的压紧,前面的一体是最小的的协同先人。

 

 1bool GetNodePaths(Node* root, Node* node, stack& s)
 2        {
 3if (根) == 空)
 4            {
 5returnfalse;
 6            }
 7            s.push(根));
 8if (根) == 压紧)
 9            {
10return true;
11            }
12bool inleft = GetNodePaths(根)->_left, node, s);
13if (in左)
14            {
15returntrue;
16            }
17bool inright = GetNodePaths(根)->_right, node, s);
18if (inright)
19            {
20returntrue;
21            }
22            s.pop();
23returnfalse;
24        }
25         Node* GetAncestor(Node* root, Node* x1, Node* X2);
26        {
27             断言(X1) && X2);
28             stack paths1, paths2;
29if (!GetNodePaths(根)->_left, x1, paths1) || !GetNodePaths(根)->_right, x2, paths2))
30            {
31return NULL;
32            }
else{
while(()>()){
();
}
while(()<()){
();
}

while(!paths1.empty() && !() && ()!=()){
if(()==())
return ();
();
();
}
}
return NULL;
33 }

两个最远的压紧当中的间隔为2。

在第一种使适应下,两个最远的压紧当中的间隔是SU。,也有能够从最远的DI当中的两个压紧当中的路程。,如图所示:

因而不要去想它。,直截了当地表现两个最远的压紧与间隔的间隔。有两种处理办法:

它依然必要经过两个子树的高位来处理。,只因总数树的隐现,条件在子树中在其次种使适应,则最大间隔应该是UPD。,时期错综复杂的状态为O(n ^ 2)。

 1//求二叉树中最远的两个压紧的间隔 2    size_t MaxLen()
 3    {
 4         size_t maxlen = 0;
 5        _MaxLen(_root, maxlen);
 6return maxlen;
 7    }
 8void _MaxLen(Node* root, size_t maxlen)  //O(N^2) 9    {
10if (根) == 空)
11        {
12return0;
13        }
14int leftdepth = Depth(根)->左)  
15int rightdepth = Depth(根)->右)
16if (leftdepth + rightdepth > maxlen)
17        {
18             maxlen = leftdepth + rightdepth;
19        }
20         _MaxLen(根)->_left, maxlen);
21         _MaxLen(根)->_right, maxlen);
22     }

时期错综复杂的状态的替代的处理方案是O(n)。:

 1     size_t _MaxLen(Node* root, size_t maxlen)  //O(N) 2    {
 3if (根) == 空)
 4        {
 5return;
 6        }
 7         size_t left = _MaxLen(根)->_left, maxlen);
 8         size_t right = _MaxLen(根)->_right, maxlen);
 9if (right+left>maxlen)
10        {
11             maxlen = right + left;
12        }
13return left > right ? left + 1 : right + 1;
14     }

3. 前序遍历和中序遍历使更新二叉树

这么地题是要用一颗二叉树的前序遍历序列和中序遍历序列,如:前序序列:1 2 3 4 5 6 – 中序序列 :3 2 4 1 6 5,来重行建筑物二叉树。we的所有格形式可以运用根压紧的投资特点在前序SE中。。图解解析跑过如次:

建立右子树的办法与。当 prev 遍历前序序列,即二叉树建立取得。行为准则如次:

 1//由前序遍历和中序遍历使更新二叉树(如:前序序列:1 2 3 4 5 6 - 中序序列 :3 2 4 1 6 5) 2         Node* RebulidTree(char* prev, char* inbgein, char* inend)
 3        {
 4             断言 && inbgein && inend);
 5if (inbgein > inend || prev == ''\0'')
 6            {
 7return NULL;
 8            }
 9             Node* root = new 压紧(*PRV)  //率先建立根压紧10char* div = inbgein;  //让div查找根压紧11while (DIV) <= inend) {
12if (*div == *前级)
13                {
14if (inbgein <= div -1)
15                    {
16                         root->_left = RebulidTree(++prev, inbgein, div - 1);//隐现建立左子树17                    }
18else {
19                         root->_left = NULL;
20                    }
21if (DIV) + 1 <= inend)
22                    {
23                         root->_right = RebulidTree(++prev, div + 1, inend);//隐现建立右子树24                    }
25else {
26                         root->_right = NULL;
27                    }
28break;
29                }
30                 ++div;
31            }
32return root;
33         }

4. 判别一棵树倘若是原封不动的二叉树

原封不动的二叉树: 前n-1层满,条件在启动,则层N,它在向右掉了,n层最向右的压紧,右边是满的。,向右是空的。。

这是一体层序遍历非隐现法的变型题,还必要运用额定的住宿来往事压紧。。鉴于层序遍历二叉树,查找第一体非原封不动的压紧(此压紧仅仅两个容器),这孩子是空的,或许只剩缺席合适的的。,条件前面的压紧有非满压紧,则失去嗅迹。

 1bool IsComplateTree(Node* 根)
 2    {
 3         queue q;
 4if (根))
 5        {
 6             q.push(根));  //率先将压紧压入队列 7        }
 8//在嗨,条件帐单出现辩论满压紧,则装备帐单。 9bool tag = true;
10while (!())
11        {
12             Node* front = ();  
13            q.pop();
14//条件非满压紧曾经呈现,则前面再呈目前的孩子的结节则必然失去嗅迹原封不动的二叉树。15if (交谈)左)
16            {
17if (帐单) == false)
18                {
19returnfalse;
20                }
21                 q.push(交谈)左)
22            }
23else {
24                 tag = false;
25            }
26if (交谈)右)
27            {
28if (帐单) == false)
29                {
30returnfalse;
31                }
32                 q.push(交谈)右)
33            }
34else {
35                 tag = false;
36            }
37        }
38returntrue;
39     }

其次种模糊想法方法:将极度的压紧停车场队列中,无论什么时候we的所有格形式判别队列头时,条件队列头是空的,这么JUM,条件尔后队列中不断地元素则失去嗅迹原封不动的二叉树。

 1bool IsCompleteTree(BinaryTreeNode *普罗特)
 2{
 3if(普罗特 == 空)
 4returnfalse;
 5 6           queue q;
 7          q.push(普罗特);
 8           BinaryTreeNode* pCur = ();
 9while(pCUR) != 空)
10          {
11               q.pop();
12                q.push(pCUR) -> 左)
13                q.push(pCUR) -> 右)
14                pCur = ();
15          }
1617           q.pop();//滚出空无所有的防喷器
18//因有一体空的。,因而如果头不为空就失去嗅迹原封不动的二叉树19while(! ())
20          {
21if(() != 空)
22returnfalse;
23               q.pop();
24          }
25returntrue;
26 }

5. 将两叉搜索树替换为排序的双链表。

与二叉树的键花化同样

 1void _ToList(Node* cur, Node*& 前级)
 2    {
 3if (库尔 == 空)
 4return;
 5 6         _ToList(库尔->_left, 前级);
 7// 8         cur->_left = prev;
 9if(前级)
10             prev->_right = cur;
1112         prev = cur;
1314         _ToList(库尔->_right, 前级);
15    }
1617     Node* ToList(Node* 根)
18    {
19         Node* prev = NULL;
20        _ToList(根), 前级);
2122         Node* head = root;
23while (头) && head->左)
24        {
25             head = head->_left;
26        }
2728return head;
29     }

6.求二叉树的宽度

同一事物二叉树的宽度是指:二叉树各层压紧等于的巅值。

we的所有格形式确信层序遍历二叉树是运用 queue 来获得的:每回邮票一体压紧后,条件有左子树和右子树,摆布子树被压入 queue,这么此刻的队列中能够既包括目前的层的压紧,它还包括下地层压紧。。

we的所有格形式必要的是一体考虑到层的压紧等于。,因而we的所有格形式必要从结的开端,记载每层的等于,在四周目前的层的每个压紧,在树敲击后,按下树的左、右子树。 queue,当目前的层整个从队列中敲击时,队列中剩的是下地层压紧。。这么相比从前腰槽的队列尺寸和最大宽度。,巅值是队列的宽度。。至死队列是空的,腰槽的maxWidth执意二叉树的宽度!

 1int 宽度(压紧) 根)
 2    {
 3         queue q;
 4if (根))
 5            q.push(根));
 6int maxwidth = 1;
 7while (!())    
 8        {
 9int length = ();
10while (巨大 > 0)    
11            {
12                 Node* front = ();
13                q.pop();
14if (交谈)左)
15                {
16                     q.push(交谈)左)
17                }
18if (交谈)右)
19                {
20                     q.push(交谈)右)
21                }
22            }
23             maxwidth = maxwidth > () ? maxwidth : ();
24        }
25return maxwidth;
26     }

7. 二叉树倘若是均衡二叉树

二叉树中每一体压紧的摆布子树高位之差均决不2即为均衡二叉树。这么当一颗二叉树的极度的子树都是均衡二叉树时,它本身必定为均衡二叉树,用这么地模糊想法可以隐现判别。二叉树倘若是均衡二叉树。行为准则如次:

 1//--判别一棵二叉树倘若是均衡二叉树 2bool IsBalance(Node* 根)  //O(N^2) 3    {
 4if (根) == 空)
 5        {
 6returnfalse;
 7        }
 8int left = Depth(根)->左)
 9int right = Depth(根)->右)  
10return ABS(右) - 左) < 2 && IsBalance(根)->左) && IsBalance(根)->右)
11     }

这种办法借助摆布的高位相比来决定倘若为二叉树,需屡次遍历二叉树,时期错综复杂的状态为O(n ^ 2)。上面是O(n)的一体算法:

 1bool IsBalance(Node* root, int& 吃水)  //O(N) 2    {
 3if (根) == 空)
 4        {
 5             depth = 0;
 6returntrue; 7        }
 8int leftdepth = 0;
 9if (IsBalance(根)->_left, left吃水) == false)
10        {
11returnfalse;
12        }
13int rightdepth = 0;
14if (IsBalance(根)->_right, right吃水) == false)
15        {
16returnfalse;
17        }
18         depth = rightdepth > leftdepth ? rightdepth + 1 : leftdepth + 1;
19return abs(leftdepth - right吃水) < 2;
20     }

 8.二叉树倘若为另一颗树的子树

判别一颗二叉树倘若是另一颗树的子树。

 先在找二叉树里找根压紧,找到后,决定后续压紧倘若相当。,条件相当,它是一棵子树。。

 1bool JudgeNextTree(Node* next, Node* 幼雏) //两棵树的接收压紧的值相当。,那个压紧倘若相当 2    {
 3if (幼雏) == 空)
 4        {
 5returntrue;
 6        }
 7if 下一步 == 空)
 8        {
 9returnfalse;
10        }
11if 下一步->_data == child->datum的复数)    //
12        {
13return JudgeNextTree下一步->_left, child->左) && JudgeNextTree下一步->_right, child->右)
14        }
15else {
16returnfalse;  //条件摆布幼雏平均,它是一棵子树。,要不然就失去嗅迹17        }
18    }
19bool JudgeTree(Node* parent, Node* 幼雏) //决定孩子倘若是双亲的子树20    {
21if (幼雏) == 空) //空树是究竟哪个树的子树。22        {
23returntrue;
24        }
25if (父亲) == 空)  //空树缺席空树的子树。26        {
27returnfalse;
28        }
29if (父亲)->_data == child->datum的复数)  //目前的压紧与找到子树的根压紧的时期相同的。30        {
31return JudgeNextTree(父亲), 幼雏);  //从相当压紧判别它倘若是子树32        }
33elseif (JudgeTree(父亲)->_left, child->左) == true)  //决定目前的压紧的左子树倘若与TH相同的。34        {
35returntrue;
36        }
37else {
38return JudgeTree(父亲)->_right, child->右)  //决定目前的压紧的右子树倘若与39        }
40     }

发表评论

电子邮件地址不会被公开。 必填项已用*标注