和二叉树相关的面试题
2016-07-18 14:33
381 查看
所谓的二叉树就是树中的每个节点最多有两个孩子节点。满二叉树:除最后一层没有子节点外,其它层的节点都具有两个子节点。完全二叉树:若二叉树的高度为h,除最后一层外,其它层的节点数目都达到最大,并且最后一层的节点都集中在树的左侧。
二叉树的节点结构:
1、二叉树的创建
先序:先创建根节点,再创建左子树,最后创建右子树
2、二叉树的遍历
先序:先遍历根节点,再遍历左子树,最后遍历右子树
中序:先遍历左子树,再遍历根节点,最后遍历右子树
后序:先遍历左子树,再遍历右子树,最后遍历根节点
层次遍历:一层一层的遍历
3、二叉树的节点的个数
4、二叉树叶子节点的个数
叶子节点的特征是既没有左孩子也没有右孩子,我们根据这个特征在层次遍历的过程中统计出叶子节点的个数
6、二叉树是否为平衡二叉树
对于树中的任意节点,其左右子树的高度差不大于1。
7、把二叉树转换成双向链表
8、二叉树是否对称
左右子树同时遍历,如果出现不一致,则不是对称的
9、两个节点的最近公共祖父节点
最近公共祖先(Lowest Common Ancestor)指的是:给定一棵有根树T和两个节点x、y,找到一个距离T最远的节点z,使得z即是x的祖父节点同时也是y的祖父节点。
BTree get_lowest_common_ancestor(BTree root, BTree a, BTree b)
{
if (root == NULL || root == a || root == b)
return root;
BTree left = get_lowest_common_ancestor(root->lchild, a, b);
BTree right = get_lowest_common_ancestor(root->rchild, a, b);
if (left && right)
return root;
return left ? left : right;
}
10、二叉树中和为某一值的路径
11、求二叉树中第K层的节点个数
12、求二叉树中两个节点的最大距离
二叉树中两个节点的最大距离分为两种情况:①路径经过左子树的最深节点,通过根节点,再经过右子树的最深节点;②路径不经过根节点,而是左子树或右子树的最大距离,取二者的最大值。
这个问题的核心是,情况①、②需要不同的信息:情况①需要子树的最大深度,情况②需要子树的最大距离。如果程序能在同一个节点返回这两个信息,那么算法将会变得很简单:
13、判断二叉树是否为满二叉树
满二叉树的总节点个数和层数之间满足如下关系:节点个数 = 2^K - 1(其中K为二叉树的层数)。我们可以利用此关系来判断是否为满二叉树。
14、判断二叉树是否为完全二叉树
我们采用树的按层次遍历,那么在遍历的过程中,如果遇到没有左孩子或右孩子的节点,我们设置标志位,如果在后序的遍历中遇到了有左孩子或右孩子的节点,那么该树不是完全二叉树。
15、根据前序和中序遍历结果,重建二叉树
二叉树的节点结构:
typedef struct node { char data; struct node *lchild, *rchild; }Btree_node,*BTree;
1、二叉树的创建
先序:先创建根节点,再创建左子树,最后创建右子树
void first_create(BTree *root) { char temp; cin >> temp; if (temp != '#') { *root = (BTree)malloc(sizeof(Btree_node)); (*root)->data = temp; (*root)->lchild = NULL; (*root)->rchild = NULL; first_create( &(*root)->lchild); first_create( &(*root)->rchild); } }
2、二叉树的遍历
先序:先遍历根节点,再遍历左子树,最后遍历右子树
/*先序遍历---递归*/ void first_traverse(BTree root) { if (root != NULL) { cout << root->data; first_traverse(root->lchild); first_traverse(root->rchild); } } /*先序遍历--非递归*/ void first_traverse_no(BTree root) { stack<BTree> sk; while ( !sk.empty() || root != NULL)//栈非空 { if (root != NULL) { cout << root->data; sk.push(root); root = root->lchild; } else { root = sk.top(); sk.pop(); root = root->rchild; } } }
中序:先遍历左子树,再遍历根节点,最后遍历右子树
/*中序遍历---递归*/ void mid_traverse(BTree root) { if (root != NULL) { mid_traverse(root->lchild); cout << root->data; mid_traverse(root->rchild); } } /*中序遍历---非递归*/ void mid_traverse_no(BTree root) { stack<BTree> sk; while (!sk.empty() || root != NULL) { if (root != NULL) { sk.push(root); root = root->lchild; } else { root = sk.top(); sk.pop(); cout << root->data; root = root->rchild; } } }
后序:先遍历左子树,再遍历右子树,最后遍历根节点
void last_traverse(BTree root) { if (root != NULL) { last_traverse(root->lchild); last_traverse(root->rchild); cout << root->data; } }
层次遍历:一层一层的遍历
/*按层次遍历*/ void level_traverse(BTree root) { queue<BTree> qe; if (root != NULL) qe.push(root); while ( !qe.empty()) { root = qe.front(); qe.pop(); cout << root->data; if (root->lchild != NULL) qe.push(root->lchild); if (root->rchild != NULL) qe.push(root->rchild); } }
3、二叉树的节点的个数
int count_node_num(BTree root) { if (root == NULL) return 0; int numleft, numright, num; numleft = count_node_num(root->lchild); numright = count_node_num(root->rchild); num = numleft + numright + 1; return num; }
4、二叉树叶子节点的个数
叶子节点的特征是既没有左孩子也没有右孩子,我们根据这个特征在层次遍历的过程中统计出叶子节点的个数
int get_leaf(BTree root) { int leaf = 0;//用来记录树中叶子节点的个数 queue<BTree> qu; if (root == NULL) return true; qu.push(root); BTree tmp; while ( !qu.empty() ) { tmp = qu.front(); qu.pop(); if (tmp->lchild != NULL || tmp->rchild != NULL) { if (tmp->lchild != NULL) qu.push(tmp->lchild); if (tmp->rchild != NULL) qu.push(tmp->rchild); } else leaf++; } return leaf; }5、二叉树的高度
int cout_high(BTree root) { if (root == NULL) return 0; int left_high, right_high, high; left_high = cout_high(root->lchild); right_high = cout_high(root->rchild); high = 1 + (left_high > right_high ? left_high : right_high); return high; }
6、二叉树是否为平衡二叉树
对于树中的任意节点,其左右子树的高度差不大于1。
int count_high(BTree root) { if (root == NULL) return 0; int left_high, right_high, high; left_high = count_high(root->lchild); right_high = count_high(root->rchild); high = 1 + (left_high > right_high ? left_high : right_high); return high; } bool banlance_tree(BTree root) { if (root == NULL) return true; int left_high, right_high,diff; left_high = count_high(root->lchild); right_high = count_high(root->rchild); diff = left_high - right_high; if (diff > 1 || diff < -1) return false; return (banlance_tree(root->lchild) && banlance_tree(root->rchild)); }
7、把二叉树转换成双向链表
void Btree_to_list(BTree root,BTree *head) { if (root == NULL)//如果是棵空树 { *head = NULL; return; } /*如果不是空树则寻找此树的最左边的节点,作为链表的头节点*/ BTree tmp = root; while (tmp->lchild != NULL) tmp = tmp->lchild; *head = tmp; BTree last_node = NULL;//用来记录链表的最后一个节点 convert_node(root, &last_node); } void convert_node(BTree root, BTree *last_node) { if (root == NULL) return; /*先转换树的左子树*/ if (root->lchild != NULL) convert_node(root->lchild, last_node); root->lchild = *last_node; if (*last_node != NULL) (*last_node)->rchild = root; /*调整last_node指向链表的最后一个元素*/ *last_node = root; /*对树的右子树进行调整*/ if (root->rchild != NULL) convert_node(root->lchild, last_node); }
8、二叉树是否对称
左右子树同时遍历,如果出现不一致,则不是对称的
bool jude_tree(BTree root) { if (root == NULL) return true; return jude(root->lchild, root->rchild); } bool jude(BTree left, BTree right) { if (left != NULL && right != NULL) { if (jude(left->lchild, right->rchild) && jude(left->rchild, right->lchild)) return true; return false; } else if (left == NULL && right == NULL) return true; else return false; }
9、两个节点的最近公共祖父节点
最近公共祖先(Lowest Common Ancestor)指的是:给定一棵有根树T和两个节点x、y,找到一个距离T最远的节点z,使得z即是x的祖父节点同时也是y的祖父节点。
BTree get_lowest_common_ancestor(BTree root, BTree a, BTree b)
{
if (root == NULL || root == a || root == b)
return root;
BTree left = get_lowest_common_ancestor(root->lchild, a, b);
BTree right = get_lowest_common_ancestor(root->rchild, a, b);
if (left && right)
return root;
return left ? left : right;
}
10、二叉树中和为某一值的路径
void get_path(BTree root, int target) { if (root == NULL) return; static int sum = 0; static deque<BTree> qu; /*如果当前路径为目标值*/ if (sum + root->data == target) { /*打印出路径*/ for (int i = 0; i < qu.size(); i++) cout << qu[i] << "->"; cout << root->data << endl; return; } else if (sum + root->data > target) return; else//当前路径值小于目标值 { qu.push_back(root); sum += root->data; get_path(root->lchild, target); get_path(root->rchild, target); qu.pop_back(); sum -= root->data; } }
11、求二叉树中第K层的节点个数
int get_Klevel_nodenum(BTree root, int k) { if (root == NULL || k <= 0) return 0; if (k == 1) return 1; return(get_Klevel_nodenum(root->lchild, k - 1) + get_Klevel_nodenum(root->rchild, k - 1)); }
12、求二叉树中两个节点的最大距离
二叉树中两个节点的最大距离分为两种情况:①路径经过左子树的最深节点,通过根节点,再经过右子树的最深节点;②路径不经过根节点,而是左子树或右子树的最大距离,取二者的最大值。
这个问题的核心是,情况①、②需要不同的信息:情况①需要子树的最大深度,情况②需要子树的最大距离。如果程序能在同一个节点返回这两个信息,那么算法将会变得很简单:
RESULT get_max_distance(BTree root) { if (root == NULL) { RESULT empty = { 0, -1 }; return empty; } RESULT rsl = get_max_distance(root->lchild); RESULT rsr = get_max_distance(root->rchild); RESULT resut; resut.maxDepth = max(rsl.maxDepth, rsr.maxDepth) + 1; resut.maxDistace = max(max(rsl.maxDistace, rsr.maxDistace), rsl.maxDepth + rsr.maxDepth + 2); return resut; }
13、判断二叉树是否为满二叉树
满二叉树的总节点个数和层数之间满足如下关系:节点个数 = 2^K - 1(其中K为二叉树的层数)。我们可以利用此关系来判断是否为满二叉树。
bool jude_full(BTree root) { int level;;//用来记录树的层数 int node_num = 0;//用来记录树中节点的个数 queue<BTree> qu; if (root == NULL) return true; qu.push(root); BTree tmp; while ( !qu.empty() ) { tmp = qu.front(); qu.pop(); node_num++; if (tmp->lchild != NULL) qu.push(tmp->lchild); if (tmp->rchild != NULL) qu.push(tmp->rchild); } level = get_depth(root); if (node_num == (pow(2, level) - 1)) return true; return false; }
14、判断二叉树是否为完全二叉树
我们采用树的按层次遍历,那么在遍历的过程中,如果遇到没有左孩子或右孩子的节点,我们设置标志位,如果在后序的遍历中遇到了有左孩子或右孩子的节点,那么该树不是完全二叉树。
bool jude_full(BTree root) { if (root == NULL) return true; queue<BTree> qu; qu.push(root); BTree tmp; int flag = 1; while ( !qu.empty()) { tmp = qu.front(); qu.pop(); if (tmp->lchild != NULL) { if (flag == 0) return false; qu.push(tmp->lchild); } else flag = 0; if (tmp->rchild != NULL) { if (flag == 0) return false; qu.push(tmp->rchild); } else flag = 0; } return true; }
15、根据前序和中序遍历结果,重建二叉树
void rebuild(const char *preOrder, const char *midOrder, int nodeNum, BTree *root) { if (preOrder == NULL || midOrder == NULL || nodeNum <= 0) return; /*用前序遍历的第一个节点构造根节点*/ (*root) = new Btree_node; (*root)->data = *preOrder; (*root)->lchild = NULL; (*root)->rchild = NULL; /*如果树中还剩一个节点则结束*/ if (nodeNum == 1) return; /*寻找子树的长度*/ int tmpLen = 0; const char *leftEnd = midOrder; /*找到左子树的结尾*/ while (*preOrder != *leftEnd) { tmpLen++; /*检查是否溢出*/ if (tmpLen > nodeNum) return; leftEnd++; } /*计算左子树的节点个数*/ int leftNode = leftEnd - midOrder; /*计算右子树的节点个数*/ int rightNode = nodeNum - leftNode - 1; /*重建左子树*/ if (leftNode > 0) rebuild( preOrder + 1, midOrder, leftNode, &((*root)->lchild) ); /*重建右子树*/ if (rightNode > 0) rebuild(preOrder + leftNode + 1, midOrder + leftNode + 1, rightNode, &((*root)->rchild)); }
相关文章推荐
- Java面试题的收集
- 关于Js那些经常出的面试题
- 面试题26:复杂链表的复制
- 程序员的基础生存技能:使用Google
- Android面试之组件
- iOS面试必看,最全梳理
- 扬长避短
- 图书推荐:《Java程序员修炼之道》 Skip to end of metadata
- .NET面试题系列[5] - 垃圾回收:概念与策略
- 年轻程序员与老程序员的对话
- 信雅达电话面试题2016-6-30
- 新人程序员入门问题详解
- “灰头土脸”的机会往往更能带给人更多惊喜!
- 面试 腾讯 阿里 百度
- 成为优秀程序员的十个Tips
- (转)30道面试题
- 已拿到offer 2015年腾讯暑期实习面试总结(技术岗)
- C# 程序员最常犯的 10 个错误
- 信息安全职业全图
- Java线程经典面试题