二叉树(c++实现)
来源:互联网 发布:怪盗joker js 编辑:程序博客网 时间:2024/05/06 00:49
转载:http://www.cnblogs.com/BeyondAnyTime/archive/2012/08/27/2659163.html
关于二叉树
二叉树作为树的一种,是一种重要的数据结构,也是面试官经常考的东西。昨天看了一下关于树中的面试题,发现二叉树中的面试题比较常见的题型大概有下面几个:创建一颗二叉树(先序,中序,后序)、遍历一颗二叉树(先序,中序,后序和层次遍历)、求二叉树中叶子节点的个数、求二叉树的高度、求二叉树中两个节点的最近公共祖先、打印和为某一值的全部路径、求某一节点是否在一个树中等等。
再详细的说这些面试题之前,不妨先看一下几种常见的二叉树:
完全二叉树:若二叉树的高度是h,除第h层之外,其他(1~h-1)层的节点数都达到了最大个数,并且第h层的节点都连续的集中在最左边。想到点什么没?实际上,完全二叉树和堆联系比较紧密哈~~~
满二叉树:除最后一层外,每一层上的所有节点都有两个子节点,最后一层都是叶子节点。
哈夫曼树:又称为最有数,这是一种带权路径长度最短的树。哈夫曼编码就是哈夫曼树的应用。
平衡二叉树:所谓平衡二叉树指的是,左右两个子树的高度差的绝对值不超过 1。
红黑树:红黑树是每个节点都带颜色的树,节点颜色或是红色或是黑色,红黑树是一种查找树。红黑树有一个重要的性质,从根节点到叶子节点的最长的路径不多于最短的路径的长度的两倍。对于红黑树,插入,删除,查找的复杂度都是O(log N)。
二叉树中的那些面试题
再具体说二叉树中的那些面试题之前,我们先看一下二叉树中的每个节点是什么样的,以及为了完成这些面试题,二叉树中声明的函数原型是什么样的:
二叉树的节点:BinTreeNode
1 //二叉树的节点类 2 class BinTreeNode 3 { 4 private: 5 int data; 6 BinTreeNode *left,*right; 7 public: 8 //利用初始化列表完成data,left,rightn的初始化 9 BinTreeNode(const int &item,BinTreeNode *lPtr = NULL,BinTreeNode *rPtr = NULL):data(item) ,left(lPtr),right(rPtr){};10 void set_data(int item)11 {12 data = item;13 }14 int get_data()const15 {16 return data;17 }18 void set_left(BinTreeNode *l)19 {20 left = l;21 }22 BinTreeNode* get_left()const23 {24 return left;25 }26 void set_right(BinTreeNode *r)27 {28 right = r;29 }30 BinTreeNode* get_right()const31 {32 return right;33 }34 };
二叉树原型:BinTree,其中包含了这篇文章中要完成的函数原型的完整声明。
1 //二叉树 2 class BinTree 3 { 4 private: 5 BinTreeNode *root; 6 public: 7 BinTree(BinTreeNode *t = NULL):root(t){}; 8 ~BinTree(){delete root;}; 9 void set_root(BinTreeNode *t)10 {11 root = t;12 }13 BinTreeNode* get_root()const14 {15 return root;16 }17 //1.创建二叉树18 BinTreeNode* create_tree();19 //2.前序遍历20 void pre_order(BinTreeNode *r)const;21 //3.中序遍历22 void in_order(BinTreeNode *r)const;23 //4.后序遍历24 void post_order(BinTreeNode *r)const;25 //5.层次遍历26 void level_order(BinTreeNode *r)const;27 //6.获得叶子节点的个数28 int get_leaf_num(BinTreeNode *r)const;29 //7.获得二叉树的高度30 int get_tree_height(BinTreeNode *r)const;31 //8.交换二叉树的左右儿子32 void swap_left_right(BinTreeNode *r);33 //9.求两个节点pNode1和pNode2在以r为树根的树中的最近公共祖先34 BinTreeNode* get_nearest_common_father(BinTreeNode *r,BinTreeNode *pNode1,BinTreeNode *pNode2)const;35 //10.打印和为某一值的所有路径36 void print_rout(BinTreeNode *r,int sum)const;37 //11.判断一个节点t是否在以r为根的子树中38 bool is_in_tree(BinTreeNode *r,BinTreeNode *t)const;39 };
2.1 创建一颗二叉树
创建一颗二叉树,可以创建先序二叉树,中序二叉树,后序二叉树。我们在创建的时候为了方便,不妨用‘#’表示空节点,这时如果先序序列是:6 4 2 3 # # # # 5 1 # # 7 # #,那么创建的二叉树如下:
下面是创建二叉树的完整代码:穿件一颗二叉树,返回二叉树的根。
1 //创建二叉树,这里不妨使用前序创建二叉树,遇到‘#’表示节点为空 2 BinTreeNode* BinTree::create_tree() 3 { 4 char item; 5 BinTreeNode *t,*t_l,*t_r; 6 cin>>item; 7 if(item != '#') 8 { 9 BinTreeNode *pTmpNode = new BinTreeNode(item-48);10 t = pTmpNode;11 t_l = create_tree();12 t->set_left(t_l);13 t_r = create_tree();14 t->set_right(t_r);15 return t;16 }17 else18 {19 t = NULL;20 return t;21 }22 }
2.2 二叉树的遍历
二叉树的遍历分为:先序遍历,中序遍历和后序遍历,这三种遍历的写法是很相似的,利用递归程序完成也是灰常简单的:
1 //前序遍历 2 void BinTree::pre_order(BinTreeNode *r)const 3 { 4 BinTreeNode *pTmpNode = r; 5 if(pTmpNode != NULL) 6 { 7 cout<<pTmpNode->get_data()<<" "; 8 pre_order(pTmpNode->get_left()); 9 pre_order(pTmpNode->get_right());10 }11 }12 //中序遍历13 void BinTree::in_order(BinTreeNode *r)const14 {15 BinTreeNode *pTmpNode = r;16 if(pTmpNode != NULL)17 {18 in_order(pTmpNode->get_left());19 cout<<pTmpNode->get_data()<<" ";20 in_order(pTmpNode->get_right());21 }22 }23 //后序遍历24 void BinTree::post_order(BinTreeNode *r)const25 {26 BinTreeNode *pTmpNode = r;27 if(pTmpNode != NULL)28 {29 post_order(pTmpNode->get_left());30 post_order(pTmpNode->get_right());31 cout<<pTmpNode->get_data()<<" ";32 }33 }
2.3 层次遍历
层次遍历也是二叉树遍历的一种方式,二叉树的层次遍历更像是一种广度优先搜索(BFS)。因此二叉树的层次遍历利用队列来完成是最好不过啦,当然不是说利用别的数据结构不能完成。
1 //层次遍历 2 void BinTree::level_order(BinTreeNode *r)const 3 { 4 if(r == NULL) 5 return; 6 deque<BinTreeNode*> q; 7 q.push_back(r); 8 while(!q.empty()) 9 {10 BinTreeNode *pTmpNode = q.front();11 cout<<pTmpNode->get_data()<<" ";12 q.pop_front();13 if(pTmpNode->get_left() != NULL)14 {15 q.push_back(pTmpNode->get_left());16 }17 if(pTmpNode->get_right() != NULL)18 {19 q.push_back(pTmpNode->get_right());20 }21 }22 }
2.4 求二叉树中叶子节点的个数
树中的叶子节点的个数 = 左子树中叶子节点的个数 + 右子树中叶子节点的个数。利用递归代码也是相当的简单,易懂。
1 //获取叶子节点的个数 2 int BinTree::get_leaf_num(BinTreeNode *r)const 3 { 4 if(r == NULL)//该节点是空节点,比如建树时候用'#'表示 5 { 6 return 0; 7 } 8 if(r->get_left()==NULL && r->get_right()==NULL)//该节点并不是空的,但是没有孩子节点 9 {10 return 1;11 }12 //递归整个树的叶子节点个数 = 左子树叶子节点的个数 + 右子树叶子节点的个数13 return get_leaf_num(r->get_left()) + get_leaf_num(r->get_right());14 }
2.5 求二叉树的高度
求二叉树的高度也是非常简单,不用多说:树的高度 = max(左子树的高度,右子树的高度) + 1 。
1 //获得二叉树的高度 2 int BinTree::get_tree_height(BinTreeNode *r)const 3 { 4 if(r == NULL)//节点本身为空 5 { 6 return 0; 7 } 8 if(r->get_left()==NULL && r->get_right()==NULL)//叶子节点 9 {10 return 1;11 }12 int l_height = get_tree_height(r->get_left());13 int r_height = get_tree_height(r->get_right());14 return l_height >= r_height ? l_height + 1 : r_height + 1; 15 }
2.6 交换二叉树的左右儿子
交换二叉树的左右儿子,可以先交换根节点的左右儿子节点,然后递归以左右儿子节点为根节点继续进行交换。树中的操作有先天的递归性。。
1 //交换二叉树的左右儿子 2 void BinTree::swap_left_right(BinTreeNode *r) 3 { 4 if(r == NULL) 5 { 6 return; 7 } 8 BinTreeNode *pTmpNode = r->get_left(); 9 r->set_left(r->get_right());10 r->set_right(pTmpNode);11 swap_left_right(r->get_left());12 swap_left_right(r->get_right());13 }
2.7 判断一个节点是否在一颗子树中
可以和当前根节点相等,也可以在左子树或者右子树中。
1 //判断一个节点t是否在以r为根的子树中 2 bool BinTree::is_in_tree(BinTreeNode *r,BinTreeNode *t)const 3 { 4 if(r == NULL) 5 { 6 return false; 7 } 8 else if(r == t) 9 {10 return true;11 }12 else13 {14 bool has = false;15 if(r->get_left() != NULL)16 {17 has = is_in_tree(r->get_left(),t);18 }19 if(!has && r->get_right()!= NULL)20 {21 has = is_in_tree(r->get_right(),t);22 }23 return has;24 }25 }
2.8 求两个节点的最近公共祖先
求两个节点的公共祖先可以用到上面的:判断一个节点是否在一颗子树中。(1)如果两个节点同时在根节点的右子树中,则最近公共祖先一定在根节点的右子树中。(2)如果两个节点同时在根节点的左子树中,则最近公共祖先一定在根节点的左子树中。(3)如果两个节点一个在根节点的右子树中,一个在根节点的左子树中,则最近公共祖先一定是根节点。当然,要注意的是:可能一个节点pNode1在以另一个节点pNode2为根的子树中,这时pNode2就是这两个节点的最近公共祖先了。显然这也是一个递归的过程啦:
1 //求两个节点的最近公共祖先 2 BinTreeNode* BinTree::get_nearest_common_father(BinTreeNode *r,BinTreeNode *pNode1,BinTreeNode *pNode2)const 3 { 4 //pNode2在以pNode1为根的子树中(每次递归都要判断,放在这里不是很好。) 5 if(is_in_tree(pNode1,pNode2)) 6 { 7 return pNode1; 8 } 9 //pNode1在以pNode2为根的子树中10 if(is_in_tree(pNode2,pNode1))11 {12 return pNode2;13 }14 bool one_in_left,one_in_right,another_in_left,another_in_right;15 one_in_left = is_in_tree(r->get_left(),pNode1);16 another_in_right = is_in_tree(r->get_right(),pNode2);17 another_in_left = is_in_tree(r->get_left(),pNode2);18 one_in_right = is_in_tree(r->get_right(),pNode1);19 if((one_in_left && another_in_right) || (one_in_right && another_in_left))20 {21 return r;22 }23 else if(one_in_left && another_in_left)24 {25 return get_nearest_common_father(r->get_left(),pNode1,pNode2);26 }27 else if(one_in_right && another_in_right)28 {29 return get_nearest_common_father(r->get_right(),pNode1,pNode2);30 }31 else32 {33 return NULL;34 }35 }
可以看到这种做法,进行了大量的重复搜素,其实有另外一种做法,那就是存储找到这两个节点的过程中经过的所有节点到两个容器中,然后遍历这两个容器,第一个不同的节点的父节点就是我们要找的节点啦。 实际上这还是采用了空间换时间的方法。
2.9 从根节点开始找到所有路径,使得路径上的节点值和为某一数值(路径不一定以叶子节点结束)
这道题要找到所有的路径,显然是用深度优先搜索(DFS)啦。但是我们发现DFS所用的栈和输出路径所用的栈应该不是一个栈,栈中的数据是相反的。看看代码:注意使用的两个栈。
1 //注意这两个栈的使用 2 stack<BinTreeNode *>dfs_s; 3 stack<BinTreeNode *>print_s; 4 //打印出从r开始的和为sum的所有路径 5 void BinTree::print_rout(BinTreeNode *r,int sum)const 6 { 7 if(r == NULL) 8 { 9 return;10 }11 //入栈12 sum -= r->get_data();13 dfs_s.push(r);14 if(sum <= 0)15 {16 if(sum == 0)17 {18 while(!dfs_s.empty())19 {20 print_s.push(dfs_s.top());21 dfs_s.pop();22 }23 while(!print_s.empty())24 {25 cout<<print_s.top()->get_data()<<" ";26 dfs_s.push(print_s.top());27 print_s.pop();28 }29 cout<<endl;30 }31 sum += r->get_data();32 dfs_s.pop();33 return;34 }35 //递归进入左子树36 print_rout(r->get_left(),sum);37 //递归进入右子树38 print_rout(r->get_right(),sum);39 //出栈40 sum += r->get_data();41 dfs_s.pop();42 }
最后,给出一点测试代码:
1 int main() 2 { 3 BinTree tree; 4 /*--------------------------------------------------------------------------*/ 5 cout<<"请输入二叉树前序序列进行建树,'#'代表空节点:"<<endl; 6 tree.set_root(tree.create_tree()); 7 cout<<endl; 8 /*--------------------------------------------------------------------------*/ 9 cout<<"前序遍历的结果:";10 tree.pre_order(tree.get_root());11 cout<<endl<<endl;12 /*--------------------------------------------------------------------------*/13 cout<<"中序遍历的结果:";14 tree.in_order(tree.get_root());15 cout<<endl<<endl;16 /*--------------------------------------------------------------------------*/17 cout<<"后序遍历的结果:";18 tree.post_order(tree.get_root());19 cout<<endl<<endl;20 /*--------------------------------------------------------------------------*/21 cout<<"层次遍历的结果:";22 tree.level_order(tree.get_root());23 cout<<endl<<endl;24 /*--------------------------------------------------------------------------*/25 cout<<"该二叉树叶子节点的个数:";26 cout<<tree.get_leaf_num(tree.get_root())<<endl<<endl;27 /*--------------------------------------------------------------------------*/28 cout<<"该二叉树的高度是:";29 cout<<tree.get_tree_height(tree.get_root())<<endl<<endl;30 /*--------------------------------------------------------------------------*/31 tree.swap_left_right(tree.get_root());32 cout<<"交换左右子树之后的先序遍历结果为:";33 tree.pre_order(tree.get_root());34 cout<<endl<<endl;35 /*--------------------------------------------------------------------------*/36 BinTreeNode *p1 = tree.get_root()->get_left()->get_right();37 BinTreeNode *p2 = tree.get_root()->get_left()->get_left();38 BinTreeNode *p3 = tree.get_root()->get_right()->get_right()->get_right();39 cout<<p1->get_data()<<" 和 "<<p2->get_data()<<"的最近公共祖先是:";40 BinTreeNode *p = tree.get_nearest_common_father(tree.get_root(),p1,p2);41 cout<<p->get_data()<<endl;42 cout<<p1->get_data()<<" 和 "<<p3->get_data()<<"的最近公共祖先是:";43 p = tree.get_nearest_common_father(tree.get_root(),p1,p3);44 cout<<p->get_data()<<endl<<endl;45 /*--------------------------------------------------------------------------*/46 cout<<"路径如下:"<<endl;47 tree.print_rout(tree.get_root(),12);48 return 0;49 }
运行结果:
以上内容,只是本人看过的面试题目的一点总结,可能不够全,可能其中的代码也有很多的错误,可能其中的内容有点水,但是我想对那部分即将参加面试的应届毕业生还是有帮助的。。大神们勿喷~~~
- 二叉树 c实现
- 二叉树 C实现
- 二叉树 c 实现
- 二叉查找树(c语言实现)
- 二叉树,红黑树 (C 实现)
- 二叉树的基本功能实现(c++)
- 二叉树遍历(c/c++实现)
- 二叉树实现排序(C语言)
- 数据结构(C实现)------- 遍历二叉树
- 二叉搜索树(c实现)
- 数据结构:二叉树的实现(C++)
- 二叉查找树的实现(c++)
- C++(二叉树的实现)
- 【二叉查找树】c实现
- 用c实现二叉树
- C语言实现二叉树
- 用c实现二叉树
- 二叉树递归C实现
- wordpress子目录,博客根目录
- 无法SSH到Ubuntu
- ubuntu qt4 终端更改
- servlet缩放图片
- Miller–Rabin 素性测试
- 二叉树(c++实现)
- 数据仓库构建步骤
- jqGrid
- android之发送短信的方法研究
- 归并排序源代码
- 传指针和传指针引用的区别/指针和引用的区别(本质)
- BT和WIFI共存
- CCSpriteFrameCache的用法
- HDU 4705