二叉树的面试题
2016-07-25 21:17
375 查看
二叉树的面试题
1.递归创建二叉树;2.由前序遍历和中序遍历重建二叉树(前序序列:1 2 3 4 5 6 - 中序序列:3 2 4 1 6 5);
3.前、中、后三种遍历的递归;
4.层次遍历-队列应用;
5.前、中、后三种遍历的非递归-用桟实现非递归;
6.求结点的个数;
7.求二叉树的高度;
8.求叶子结点的个数;
9.查找一个数据;
10.求第k层结点的个数;
11.求二叉树的镜像;
12.判断二叉树是不是完全二叉树;
<span style="font-size:18px;">#pragma once #include<iostream> using namespace std; #include<assert.h> #include<queue> #include<stack> template <class T> struct BinaryTreeNode { BinaryTreeNode<T>* _left; BinaryTreeNode<T>* _right; T _data; BinaryTreeNode(T data) :_left(NULL) , _right(NULL) , _data(data) {} }; template <class T> class BinaryTree { typedef BinaryTreeNode<T> Node; public: BinaryTree() :_root(NULL) {} /////////////////////////////递归创建二叉树////////////////////////////// BinaryTree(const T* arr, int len, const T& invalid) { assert(arr); int index = 0; _CreateBinaryTree(_root, arr, len, index, invalid); } //由前序遍历和中序遍历重建二叉树(前序序列:1 2 3 4 5 6 - 中序序列:3 2 4 1 6 5) BinaryTree(T* Prev,T* InOrder,int len) { assert(Prev); assert(InOrder); _ReBuildCreateBinaryTree(_root,Prev,InOrder,len); } //前、中、后三种遍历的递归 void PreOrder() { _PreOrder(_root); cout << endl; } void InOrder() { _InOrder(_root); cout << endl; } void PostOrder() { _PostOrder(_root); cout << endl; } /////////////////////////层次遍历-队列应用///////////////////////////////// void LevelTraverse() { queue<Node*> q; if (_root) q.push(_root); while (!q.empty()) { Node* front = q.front(); cout << front->_data << " "; q.pop(); if (front->_left) q.push(front->_left); if (front->_right) q.push(front->_right); } cout << endl; } //前、中、后三种遍历的非递归-用桟实现非递归 //前序非递归:访问当前结点,利用栈的特性后进先出,将右结点先 //入桟,再将左结点入桟,这样先访问左,再访问右 void PreOrder_Non() { stack<Node*> s; if (_root) s.push(_root); while (!s.empty()) { Node* top = s.top(); cout << top->_data << " "; s.pop(); if (top->_right) s.push(top->_right); if (top->_left) s.push(top->_left); } cout << endl; } //////////////中序遍历:先将左子树入桟,再访问,再将右子树入桟///////////////////// void InOrder_Non() { stack<Node*> s; Node* cur = _root; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->_left; } cur = s.top(); s.pop(); cout << cur->_data << " "; cur = cur->_right; } cout << endl; } //////////////////////////////////后续遍历,prev法///////////////////////////// void PostOrder_Non() { stack<Node*> s; Node* cur = _root; Node* prev = NULL; while (cur || !s.empty()) { //左子树入桟 while (cur) { s.push(cur); cur = cur->_left; } cur = s.top(); //prev指向前一个被访问过的结点,右子树入桟 if (cur->_right && prev != cur->_right) { cur = cur->_right; } //当右子树为空且该结点没有被访问过,则去访问该结点 else { cout << cur->_data << " "; prev = cur; s.pop(); cur = NULL; } } cout << endl; } ///////////////////////////////后续遍历,双桟法//////////////////////////// void PostOrder_Non_Two_Stack() { if (_root == NULL) return; stack<Node*> s1, s2; s1.push(_root); while (!s1.empty()) { Node* top = s1.top(); s1.pop(); s2.push(top); if (top->_left) s1.push(top->_left); if (top->_right) s1.push(top->_right); } while (!s2.empty()) { Node* top = s2.top(); cout << top->_data << " "; s2.pop(); } cout << endl; } ////////////////////////////求结点的个数///////////////////////////////// //递归 size_t Size() { size_t _size = _Size(_root); return _size; } //////////////加在一个静态变量上引入多线程安全问题/////////////////////// size_t Size1() { size_t _size1 = _Size1(_root); return _size1; } ///////////////////////引用解决了多线程安全问题///////////////////////// size_t Size2() { int index = 0; size_t _size2 = _Size2(_root, index); return _size2; } ///////////////////////////求二叉树的高度///////////////////////////// size_t Height() { size_t height = _Height(_root); return height; } ///////////////////////////求叶子结点的个数////////////////////////// size_t LeafNum() { size_t leaf = _LeafNum(_root); return leaf; } ///////////////////////////查找一个数据/////////////////////////// bool Find(const T& x) { return _Find(_root, x); } ////////////////////////求第k层结点的个数//////////////////////// size_t KLayerOfNodeNum(int k) { size_t num = 0; num = _KLayerOfNodeNum(_root,k); return num; } ///////////////////////求二叉树的镜像//////////////////////////// void GetMirror() { _root = _GetMirror(_root); } #include<queue> //////////////////判断二叉树是不是完全二叉树//////////////////// ///////////方法一:广度优先遍历二叉树//////////// bool IsCompleteBinaryTree() { if (_root == NULL) return true; //bool isCompleteTree = false; bool HasLeftNoRight = false; queue<Node*> q; q.push(_root); while (!q.empty()) { Node* front = q.front(); q.pop(); //第二次遇到只有左子树的结点 if (front->_left && front->_right == NULL && HasLeftNoRight) { return false; } if (front->_left && front->_right) { //在遇到只有左子树的情况之后,必定不能往进去压结点 if (HasLeftNoRight) { return false; } q.push(front->_left); q.push(front->_right); } else if (front->_left && front->_right == NULL && !HasLeftNoRight) { q.push(front->_left); //HasLeftNoRight为true时表示已经遇到只有左子树的情况 HasLeftNoRight = true; } else if (front->_left == NULL && front->_right) { return false; } else { //isCompleteTree = true; } } //return isCompleteTree; return true; } ////////////方法二:按照完全二叉树的定义,左树的深度大于等于右树的深度///////////// ///////////一棵树一棵树的递归,前序遍历的过程////////////// bool IsCompleteBinaryTree2() { return _IsCompleteBinaryTree2(_root); } protected: void _CreateBinaryTree(Node*& root, const T* arr, int len, int& index, const T& invalid) { if (root == NULL && index < len && arr[index] != invalid) { root = new Node(arr[index]); _CreateBinaryTree(root->_left, arr, len, ++index, invalid); _CreateBinaryTree(root->_right, arr, len, ++index, invalid); } } void _ReBuildCreateBinaryTree(Node*& root, T* Prev, T* InOrder, \ int len) { if (Prev == NULL && InOrder == NULL && len <= 0) return; return _ReBuildTree(root, Prev, Prev + len - 1, InOrder, InOrder + len - 1); } void _ReBuildTree(Node*& root,T* StartPre,T* EndPre, \ T* StartIn, T* EndIn) { //1.前序确定根结点 int rootValue = StartPre[0]; root = new Node(rootValue); //2.(1).只有一个结点 if (StartPre == EndPre) { if (StartIn == EndIn && *StartPre == *StartIn) { return; } else { cout << "Prev and InOrder is not Catching!" << endl; return; } } //2.(2).有多个结点,通过在中序中找到根结点 T* rootIn = StartIn; while (rootIn < EndIn && *rootIn != root->_data) { ++rootIn; } if (rootIn == EndIn && *rootIn != root->_data) { cout << "Prev and InOrder is not Catching!" << endl; return; } //3.上面找到根结点,同时通过此中序中的根结点计算出左子树结点的个数 int NumOfLeft = rootIn - StartIn; //4.通过根的左子树的个数在前序中计算左子树最后一个数据的位置 T* LastPlaceOfLeft = StartPre + NumOfLeft; //5.递归构建左子树 if (NumOfLeft > 0) { _ReBuildTree(root->_left, StartPre + 1, LastPlaceOfLeft, StartIn, rootIn - 1); } //6.递归构建右子树 if (NumOfLeft < EndPre - StartPre) { _ReBuildTree(root->_right, LastPlaceOfLeft + 1, EndPre, rootIn + 1, EndIn); } } void _PreOrder(Node* root) { if (root == NULL) return; cout << root->_data << " "; _PreOrder(root->_left); _PreOrder(root->_right); } void _InOrder(Node* root) { if (root == NULL) return; _InOrder(root->_left); cout << root->_data << " "; _InOrder(root->_right); } void _PostOrder(Node* root) { if (root == NULL) return; _PostOrder(root->_left); _PostOrder(root->_right); cout << root->_data << " "; } size_t _Size(Node* root) { if (root == NULL) return 0; return _Size(root->_left) + _Size(root->_right) + 1; } size_t _Size1(Node* root) { if (root == NULL) return 0; static int count = 0; ++count; _Size1(root->_left); _Size1(root->_right); return count; } size_t _Size2(Node* root,int& count) { if (root == NULL) return 0; ++count; _Size2(root->_left, count); _Size2(root->_right, count); return count; } size_t _Height(Node* root) { if (root == NULL) return 0; size_t left = _Height(root->_left); size_t right = _Height(root->_right); return left + 1 > right + 1 ? left + 1 : right + 1; } size_t _LeafNum(Node* root) { if (root == NULL) return 0; if (root->_left == NULL && root->_right == NULL) return 1; size_t leftOfLeaf = _LeafNum(root->_left); size_t rightOfLeaf = _LeafNum(root->_right); return leftOfLeaf + rightOfLeaf; } bool _Find(Node*& root, const T& x) { if (root && root->_data == x) return true; if (root->_left) return _Find(root->_left, x); if (root->_right) return _Find(root->_right, x); return false; } size_t _KLayerOfNodeNum(Node* root,const int& k) { if (root == NULL || k < 1) return 0; if (k == 1) return 1; //k层结点=左子树的k - 1层的结点 + 右子树的k - 1层的结点 size_t KOfLeft = _KLayerOfNodeNum(root->_left, k - 1); size_t ROfRight = _KLayerOfNodeNum(root->_right, k - 1); return KOfLeft + ROfRight; } Node* _GetMirror(Node* root) { if (root == NULL) return NULL; //求左子树与右子树的镜像 Node* leftMirror = _GetMirror(root->_left); Node* rightMirror = _GetMirror(root->_right); //交换左右子树 root->_left = rightMirror; root->_right = leftMirror; return root; } bool _IsCompleteBinaryTree2(Node* root) { if (root == NULL) return true; int left = _Height(root->_left); int right = _Height(root->_right); if (left - right < 0 || left - right > 1) { return false; } return _IsCompleteBinaryTree2(root->_left) && _IsCompleteBinaryTree2(root->_right); } private: Node* _root; }; void TestBinaryTree() { //int a[10] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6 }; //int a[15] = { 1, 2, '#', 3, '#', '#', 4, 5, '#', 6, '#', 7, '#', '#', 8 }; //int a[] = { 1, 2, '#', 3, '#', '#', 4, 5, 6, '#', 7, 8 }; int a[] = { 0, 1, 3, 7,'#', '#', 8, '#', '#', 4, 9, '#', '#','#', 2, 5, '#', '#', 6 }; //int a[] = { 1, 2, '#', 4, '#', '#', 3}; BinaryTree<int> bt(a, sizeof(a) / sizeof(a[0]),'#'); //前、中、后三种遍历的递归测试及层次遍历测试 bt.PreOrder(); bt.InOrder(); bt.PostOrder(); bt.LevelTraverse(); //前、中、后三种遍历的非递归测试 bt.PreOrder_Non(); bt.InOrder_Non(); bt.PostOrder_Non(); bt.PostOrder_Non_Two_Stack(); //总结点个数、高度、叶子结点个数、k层结点个数、查找函数及二叉树镜像问题 cout <<"Size():"<< bt.Size() << endl; cout <<"Size1():"<< bt.Size1() << endl; cout <<"Size2():"<< bt.Size2() << endl; cout <<"Height:"<< bt.Height() << endl; cout <<"LeafNum():"<< bt.LeafNum() << endl; cout <<"KLayerOfNodeNum(3):"<< bt.KLayerOfNodeNum(3) << endl; //bt.GetMirror(); cout << "IsCompleteBinaryTree()?" << bt.IsCompleteBinaryTree() << endl; cout << "IsCompleteBinaryTree2()?" << bt.IsCompleteBinaryTree2() << endl; } void TestPrevAndInReBuildBinaryTree() { int PrevOrder[] = { 0, 1, 3, 7, 8, 4, 9, 2, 5, 6 }; int InOrder[] = { 7, 3, 8, 1, 9, 4, 0, 5, 2, 6 }; BinaryTree<int> bt(PrevOrder, InOrder, sizeof(PrevOrder) / sizeof(PrevOrder[0])); }</span>
相关文章推荐
- JS面试题-算法台阶问题
- 面试题52: 构建乘积数组
- 成为高效程序员应该掌握的搜索技巧
- 剑指offer面试题30:用自定义堆实现
- java面试经历
- Google研究主管Peter Norvig:想当程序员吗?这里份成功“食谱”
- 记一次面试经历(互联网金融行业)
- 从程序员到事业单位再到公务员的一点感想
- C#面试-总结
- PHP工程师面试常见问题
- JAVA多线程和并发基础面试
- Java线程面试题
- 论初级程序员的自我修养
- 剑指offer面试题28-29
- 给年轻程序员的话
- 从今天开始,做一个测试工程师
- 让两个数进值互换 面试题
- 前端面试题答案的解答:html+css部分
- [转]海量数据处理面试题总结
- 剑指offer面试题36:数组中的逆序对