二叉树常见面试题汇总
来源:互联网 发布:金融职位人数 知乎 编辑:程序博客网 时间:2024/05/22 05:11
1.先序/中序/后序遍历二叉树(递归非递归实现),层序遍历二叉树遍历
2.求二叉树的高度
size_t Height()//获取该树的高度 { return _Height(_pRoot); } size_t _Height(Node* pRoot)//获取数树的深度,根节点为1 { if(pRoot==NULL) return 0; if(pRoot->_pLeft==NULL && pRoot->_pRight==NULL) return 1; size_t LeftHeight = _Height(pRoot->_pLeft); size_t RightHeight = _Height(pRoot->_pRight); if(LeftHeight > RightHeight) return LeftHeight+1; else return RightHeight+1; }
3.叶子节点的个数
size_t GetLeefNode()//获取叶子节点的个数(左子树叶子节点加上右子树的叶子节点) { return _GetLeefNode(_pRoot); } size_t _GetLeefNode(Node* pRoot)//获取叶子节点的个数---递归左子树叶子节点加上右子树的叶子节点数 { if(pRoot==NULL) return 0; if(pRoot->_pLeft==NULL || pRoot->_pRight==NULL) return 1; size_t pLeftLeefNode = _GetLeefNode(pRoot->_pLeft); size_t pRightLeefNode = _GetLeefNode(pRoot->_pRight); return pLeftLeefNode+pRightLeefNode; }
4.求第k层节点的个数
size_t GetKLevelNode(const size_t& k)//获取第K层节点总数 { return _GetKLevelNode(_pRoot,k); }size_t _GetKLevelNode(Node* pRoot,size_t k)//获取第k层节点个数--递归K-1层节点数 { if(pRoot==NULL || k<=0 || k>_Height(pRoot)) return 0; if(k == 1) return 1; size_t LeftKLevelNode = _GetKLevelNode(pRoot->_pLeft,k-1); size_t RightKLevelNode = _GetKLevelNode(pRoot->_pRight,k-1); return LeftKLevelNode+RightKLevelNode; }
2、3、4这三道常见面试题都是采用根据先序遍历的方法,来对节点进行递归操作
5.树中两个节点的最低公共祖先
思考分析:
该题根据题目给出的条件不同,将会有不同的简便算法;
1>.该树是二叉收索树。
二叉收索树的左孩子小于根节点,根节点小于右孩子,所 以我们可以直接根据节点的值,对左右子树进行判断,从而得到结果
2>.该树是普通树,但有pParent指针。此时我们可以根据pPaent指针将该问题转化为求相交两个链表第一个相交的节点
3>.该树是普通的树,不含pParent指针
我们今天就介绍普通的树,有三种得到最低公共祖先的方法
- 从根节点开始遍历节点,判断所求节点是否在其左右子树,若在,继续向下遍历,否则,根节点就是它的双亲节点
缺点:重复遍历节点,时间开销大
//树中两个节点的最低公共祖先(实现2---利用先序遍历每一个节点,并判断两个节点是否在该节点的左右分支上,在,则该节点即是最低公共祖先,否则,继续遍历查找) Node* GetLastCommonParent2(Node* pNode1,Node* pNode2) { return _GetLastCommonParent2(_pRoot,pNode1,pNode2); } //树中两个节点的最低公共祖先---实现2(重复遍历节点并进行判断,该方法的时间消耗大) Node* _GetLastCommonParent2(Node* pRoot,Node* pNode1,Node* pNode2) { if(pRoot==NULL || pNode1==NULL ||pNode2==NULL) return NULL; Node* pCur = NULL; if(IsFindNode(pRoot,pNode1) && IsFindNode(pRoot,pNode2))//若在根为pRoot的子树中查找到pNode和pNode2,则继续向下寻找,若找不到,二叉树的根为最低祖先 { if(pCur=_GetLastCommonParent2(pRoot->_pLeft,pNode1,pNode2)) return pCur; if(pCur=_GetLastCommonParent2(pRoot->_pRight,pNode1,pNode2)) return pCur; return pRoot; } return NULL; }
- 普通树,用辅助内存栈实现,查找两个栈中最后一个相同的元素
Node* _GetLastCommonParent1(Node* pRoot,Node* pNode1,Node* pNode2) { if(pRoot == NULL || pNode1==NULL || pNode2==NULL) return NULL; stack<Node*> s1; stack<Node*> s2; //调用查找该节点并保存路径的函数 bool flags = false; FindNodeAndPush(pRoot,pNode1,s1,flags); flags = false; FindNodeAndPush(pRoot,pNode2,s2,flags); //进行出栈操作,找到最低公共祖先 while(!s1.empty() && !s2.empty()) { if(s1.size()>s2.size()) s1.pop(); else if(s1.size() < s2.size()) s2.pop(); else { while(s1.top() != s2.top()) { s1.pop(); s2.pop(); } return s1.top(); } } return NULL; } void FindNodeAndPush(Node* pRoot,Node* pNode,stack<Node*> &s,bool& flags)//从根节点开始查找该节点并保存路径到栈 { if(pRoot==NULL || pNode == NULL) return; s.push(pRoot); if(pRoot == pNode) { flags = true; return; } if(!flags) FindNodeAndPush(pRoot->_pLeft,pNode,s,flags); if(!flags) FindNodeAndPush(pRoot->_pRight,pNode,s,flags); if(!flags) { s.pop(); return; } return; }
- 利用辅助空间链表,将问题转化为求两个链表的最后一个相交的节点
Node* _GetLastCommonParent3(Node* pRoot,Node* pNode1,Node* pNode2) { if(pRoot==NULL || pNode1==NULL ||pNode2==NULL) return NULL; list<Node*> l1; list<Node*> l2; //从根节点开始查找节点并保存路径 bool flags = false; FindNodeAndRemPath(pRoot,pNode1,l1,flags); flags = false; FindNodeAndRemPath(pRoot,pNode2,l2,flags); //求两个相交链表中最后一个相交的节点 return FindCrossPointOfTwoList(l1,l2); } void FindNodeAndRemPath(Node* pRoot,Node* pNode,list<Node*>& l,bool& flags)//从根节点开始查找节点并保存路径 { if(pRoot==NULL) return; l.push_back(pRoot); if(pRoot == pNode) { flags = true; return; } if(!flags) FindNodeAndRemPath(pRoot->_pLeft,pNode,l,flags); if(!flags) FindNodeAndRemPath(pRoot->_pRight,pNode,l,flags); if(!flags) { l.pop_back(); return; } } Node* FindCrossPointOfTwoList(list<Node*> l1,list<Node*> l2)//求两个相交链表中最后一个相交的节点 { //Node* pCur1 = l1.front(); //Node* pCur2 = l2.front(); list<Node*>::iterator it1 = l1.begin(); list<Node*>::iterator it2 = l2.begin(); Node* pCur = NULL; while(it1 != l1.end() && it2 != l2.end()) { if(*it1 == *it2) { pCur = *it1; it1++; it2++; } else { return pCur; } } return NULL; }
6.求二叉树的镜像
- 先序遍历得到结果
//获取一棵树的镜像---实现1(利用先序遍历) void GetMirrorTree1() { _pRoot = _GetMirrorTree1(_pRoot); } //获取一棵树的镜像树---先序遍历实现 Node* _GetMirrorTree1(Node* pRoot) { if(pRoot == NULL || (pRoot->_pRight==NULL && pRoot->_pLeft==NULL)) return NULL; std::swap(pRoot->_pLeft,pRoot->_pRight); Node* pCur = NULL; pCur = _GetMirrorTree(pRoot->_pLeft); pCur = _GetMirrorTree(pRoot->_pRight); return pRoot; }
- 层序遍历的到结果
//获取一棵树的镜像---实现2(利用层序遍历) void GetMirrorTree2() { _pRoot = _GetMirrorTree2(_pRoot); }//获取一棵树的镜像树---层序遍历实现 Node* _GetMirrorTree2(Node* pRoot) { if(pRoot==NULL) return NULL; queue<Node*> q; q.push(pRoot); Node* pCur = NULL; while(!q.empty()) { pCur=q.front(); if(pCur->_pLeft!=NULL || pCur->_pRight!=NULL) std::swap(pCur->_pLeft,pCur->_pRight); q.pop(); if(pCur->_pLeft!=NULL) q.push(pCur->_pLeft); if(pCur->_pRight!=NULL) q.push(pCur->_pRight); } return pRoot; }
7.判断一棵树是否为完全二叉树
//判断一棵树是否为完全二叉树 bool IsCompleteTree() { return _IsCompleteTree(_pRoot); } bool _IsCompleteTree(Node* pRoot)//判断一颗树是不是完全二叉树 { //用一个队列来保存数据,从根节开始,访问他的左右子树,若左右子树都存在,则说明该树有可能成为完全二叉树,左右节点入栈 //取队列的头节点,访问其左右子树,若只有左子树存在,说明有可能是完全二叉树,入栈继续访问,此时该节点前面的节点都满足 //完全二叉树的要求,若后面的节点有一个孩子节点,则说明该树不是完全二叉树; //若只有右子树存在,则节点没有左子树该树一定不是完全二叉树 //若该节点没有左右孩子,同只有左孩子一样,继续访问后面的节点进行判断 if(pRoot==NULL) return true; queue<Node*> q; q.push(pRoot); Node* pCur = NULL; while(!q.empty()) { pCur = q.front(); if(pCur->_pLeft!=NULL && pCur->_pRight!=NULL) { q.push(pCur->_pLeft); q.push(pCur->_pRight); } else if(pCur->_pLeft!=NULL) { q.push(pCur->_pLeft); //判断处理 q.pop(); while(!q.empty()) { pCur=q.front(); if(pCur->_pLeft!=NULL || pCur->_pRight!=NULL) return false; q.pop(); } } else if(pCur->_pRight!=NULL) { return false; } else { //判断处理 q.pop(); while(!q.empty()) { pCur=q.front(); if(pCur->_pLeft!=NULL || pCur->_pRight!=NULL) return false; q.pop(); } } if(!q.empty()) q.pop(); } return true; }
8.用先序和中序列表重建二叉树
void RebuildTree(char* preorder,char* inorder)//根据先序数列和中序数列重建二叉树 { if(preorder==NULL || inorder==NULL) return; size_t height1 = strlen(preorder); size_t height2 = strlen(inorder); size_t index = 0; _pRoot = _RebuildTree(_pRoot,preorder,preorder+height1-1,inorder,inorder+height2-1,index); }Node* _RebuildTree(Node* pRoot,char* pstart,char* pend,char* instart,char* inend,size_t& index) { if(pstart==NULL || pend==NULL || instart==NULL || inend==NULL) return NULL; if(instart>inend) { --index; return NULL; } pRoot = new Node; pRoot->_data = pstart[index]; size_t idx=0; for(idx=0; idx<(inend-instart); ++idx) { if(pstart[index]==instart[idx]) break; } pRoot->_pLeft = _RebuildTree(pRoot->_pLeft,pstart,pend,instart,instart+idx-1,++index); pRoot->_pRight = _RebuildTree(pRoot->_pRight,pstart,pend,instart+idx+1,inend,++index); return pRoot; }
9.根据中序和后序数列重建二叉树
void RebuildTree2(char* inorder,char* postorder)//根据后序数列和中序数列重建二叉树 { if(inorder==NULL || postorder==NULL) return; size_t height1 = strlen(inorder); size_t height2 = strlen(postorder); if(height1 != height2) return; size_t index=height2-1; _pRoot = _RebuildTree2(_pRoot,inorder,inorder+height1-1,postorder,postorder+height2-1,index); } //根据中序数列和后序数列重建二叉树 Node* _RebuildTree2(Node* pRoot,char* instart,char* inend,char* poststart,char* postend,size_t& index) { if(instart==NULL || inend==NULL || poststart==NULL || postend==NULL) return NULL; if(instart>inend) { ++index; return NULL; } pRoot = new Node; pRoot->_data = poststart[index]; size_t idx=0; for(; idx<(inend-instart); ++idx) { if(poststart[index]==instart[idx]) break; } pRoot->_pRight = _RebuildTree2(pRoot->_pRight,instart+idx+1,inend,poststart,postend,--index); pRoot->_pLeft = _RebuildTree2(pRoot->_pLeft,instart,instart+idx-1,poststart,postend,--index); return pRoot; }
10.获取一棵完全二叉树所有节点的个数
- 普通遍历计数
缺点:需要遍历每个节点,时间开销大
size_t GetAllNode(size_t& count)//利用先序遍历,查找每一个节点并计数,时间复杂度O(N),N是总的节点个数 { return _GetAllNode(_pRoot,count); } size_t _GetAllNode(Node* pRoot,size_t& count) { if(pRoot==NULL) return count; if(pRoot!=NULL) ++count; count = _GetAllNode(pRoot->_pLeft,count); count = _GetAllNode(pRoot->_pRight,count); }
- 代码优化:注意条件—完全二叉树
//计算一棵完全二叉树的所有节点的个数---优化,时间复杂度O(logN)2(平方) size_t GetAllNode1(size_t& count) { return _GetAllNode1(_pRoot,count); } size_t _GetAllNode1(Node* pRoot,size_t& count) { if(pRoot==NULL) return 0; if(pRoot->_pLeft==NULL && pRoot->_pRight==NULL) return 1; int h = GetHight(pRoot);//求深度O(logN) size_t ret = 0; if((ret=GetHight(pRoot->_pRight)+1)==h)//左子树为满二叉树,用公式计算,右子树遍历计算 { count = ((1<<(h-1))-1+1+_GetAllNode(pRoot->_pRight,count));//O(logN) } else { count = ((1<<(h-2))-1+1+_GetAllNode(pRoot->_pLeft,count));//右子树为满二叉树,用公式计算,左子树遍历计算 } return count; } size_t GetHight(Node* pRoot)//计算树的最大深度 { Node* pCur = pRoot; size_t ret=0; while(pCur) { pCur = pCur->_pLeft; ret++; } return ret; }
11.判断一棵树是不是平衡二叉树
- 普通解法—从根节点开始访问,判断其左右子树是否为平衡二叉树,若不是,这直接退出,若是,继续访问左右子树
缺点:计算节点左右子树高度时重复计算,多次访问,时间开销大
bool IsBalanceTree1()//实现1---利用先序遍历的方法,但是节点重复遍历了,从根节点开始计算并比较每个子树的深度 { return _IsBalanceTree1(_pRoot); } bool _IsBalanceTree1(Node* pRoot)//判断一棵树是不是平衡二叉树---实现1 { //从根节点开始,得到左右子树的高度,比较,若不满足条件,则直接退出,后面再不用比较 if(pRoot==NULL) return true; int left= _Height(pRoot->_pLeft); int right = _Height(pRoot->_pRight); int diff = left-right; if(diff>1 || diff<-1) return false; return (_IsBalanceTree1(pRoot->_pLeft) && _IsBalanceTree1(pRoot->_pRight)); }
- 利用后序遍历的方法实现对节点的访问
bool IsBalanceTree2()//实现二---利用后序遍历的方法,保证每个节点只遍历一次,根据根节点的深度为左右子树深度的最大值加一 { int depth = 0; return _IsBalanceTree2(_pRoot,depth); }bool _IsBalanceTree2(Node* pRoot,int& depth)//判断一棵树是不是平衡二叉树---实现2 { //从树的最左端的节点开始访问其左右子树,这里没有调用_Height函数,最左端的节点的左子树高度一定为1,最右端的右节点的深度也是1 //再递归回退的过程中,得到节点左子树的高度和右子树的高度(下层高度+1),比较,若满足条件,则继续回退,得到当前层的高度,继续比较上一层 //若不满足,则直接返回错误 if(pRoot==NULL) { depth = 0; return true; } int left; int right; if(_IsBalanceTree2(pRoot->_pLeft,left)&&_IsBalanceTree2(pRoot->_pRight,right)) { int diff = left-right; if(diff<=1 && diff>=-1) { depth = 1+((left>right)? left:right); return true; } return false; } return false; }
0 0
- 二叉树常见面试题汇总
- 二叉树常见面试题汇总(改进版)
- 二叉树面试题汇总
- 二叉树常见面试题
- 二叉树常见面试题
- 常见的二叉树面试题大汇总(涵盖二叉搜索树)
- 常见面试题汇总
- 二叉树经典面试题汇总
- 二叉树相关面试题汇总
- 二叉树面试题汇总(一)
- 二叉树面试题汇总(二)
- 常见的二叉树面试题
- 常见面试题之二叉树
- 数据结构之二叉树常见面试题
- 二叉树的常见面试题总结
- Hibernate常见面试题汇总
- Hibernate常见面试题汇总
- Hibernate常见面试题汇总
- java struts2
- 对AngularJs中依赖注入的理解和使用方法
- Geode Region 管理
- R语言plyr包学**
- Sqlserver2000服务器安装配置
- 二叉树常见面试题汇总
- 【网站管理1】_dede织梦后台如何发布文章
- 分块练习- BZOJ2002-Bounce 弹飞绵羊
- java统一异常处理
- PHP知识(一)
- Leetcode学习(42)—— Assign Cookies
- 如何把本地的jar包添加到Maven仓库
- Android Studio 与SVN的关联及使用方法
- 如何看待放弃生活努力赚钱的"阿里人"