您的位置:首页 > 职场人生

和二叉树相关的面试题

2016-07-18 14:33 381 查看
所谓的二叉树就是树中的每个节点最多有两个孩子节点。满二叉树:除最后一层没有子节点外,其它层的节点都具有两个子节点。完全二叉树:若二叉树的高度为h,除最后一层外,其它层的节点数目都达到最大,并且最后一层的节点都集中在树的左侧。

二叉树的节点结构:

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));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: