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

二叉树(经典面试题)

2018-02-01 16:59 267 查看
先要了解二叉树创建过程才能更好的理解经典面试题

列表内容创建二叉树

下图为所需要创建二叉树



1.1:创建树新节点
BTnode *BuyBTNewnode(BTDataType x)
{
BTnode*  node = (BTnode *)malloc(sizeof(BTnode));
node->_data = x;
node->_left = NULL;
node->_right = NULL;
return node;
}

1.2:创建一个二叉树
BTnode *CreateBT(BTDataType* bt, size_t *Index, BTDataType invalid)
{//Index创建为指针,要用传址方式,不能用传值,因为递归时传参形成临时变量
//导致递归的下一层中的变量发生改变不会使上一层的值改变,使用传址,下标
//Index才能随着递归而改变
BTnode *root = NULL;//创建树的根
if (bt[*Index] != invalid)//遍历数组,遇到的不是非法值时,创建结点
{
root = BuyBTNewnode(bt[*Index]);//创建新结点
++(*Index);//下标增加
root->_left = CreateBT(bt, Index, invalid);
++(*Index);
root->_right = CreateBT(bt, Index, invalid);
}
return root;
}


2.二叉树的基本操作

2.1:遍历二叉树(递归算法)
void BTreePrevOrder(BTnode* root)//前序遍历
{
if (root == NULL)
{
return;
}
printf("%d ", root->_data);
BTreePrevOrder(root->_left);
BTreePrevOrder(root->_right);
}

void BTreeInOrder(BTnode* root)//中序遍历
{
if (root == NULL)
{
return;
}
BTreeInOrder(root->_left);
printf("%d ", root->_data);
BTreeInOrder(root->_right);
}

v
4000
oid BTreePostOrder(BTnode* root)//后序遍历
{
if (root == NULL)
{
return;
}
BTreePostOrder(root->_left);
BTreePostOrder(root->_right);
printf("%d ", root->_data);
}
////从上面可以看到,这三种遍历主要区别就是输出位置的不同


2.2:遍历二叉树(循环算法)

void BTreePrevOrderNonR(BTnode* root)//前序遍历(非递归)
{//把栈作为媒介来更好的遍历
Stack s;//创建一个栈,并初始化
StackInit(&s);
BTnode* top = NULL;
BTnode* cur = root;
while (StackEmpty(&s) || cur)
{
while (cur != NULL)//先把左子树全部入栈
{
printf("%d ", cur->_data);//打印当前的左子树,即根
StackPush(&s, cur);//入栈
cur = cur->_left;
}
top = StackTop(&s);//把左子树全部入栈后,取栈顶元素即
//将二叉树中的3,给top保存
StackPop(&s);//栈顶出栈:出3
cur = top->_right;//指向3的右子树,因为左子树已经访问过了
}//循环往复遍历
}

void BTreeInOrderNonR(BTnode* root)//中序遍历(非递归)
{//中序遍历和前序遍历的差别就是输出的时机不同
Stack s;
StackInit(&s);
BTnode* top = NULL;
BTnode* cur = root;
while (StackEmpty(&s) || cur)
{
while (cur != NULL)//先把左子树全部入栈,然后一次访问右子树
{
StackPush(&s, cur);
cur = cur->_left;
}
top = StackTop(&s);//打印当前栈顶
printf("%d ", top->_data);
StackPop(&s);

cur = top->_right;
}
}

void BTreePostOrderNonR(BTnode* root)//后序遍历(非递归)
{//后序与前中序不同的是,要有一个标记节点,用来记录右子树是否访问过
//只有右子树为空或者右子树访问过才能访问根,
Stack s;
StackInit(&s);
BTnode *top = NULL;
BTnode *prev = NULL;//标记节点
BTnode *cur = root;
while (StackEmpty(&s) || cur)
{
while (cur)
{
StackPush(&s, cur);
cur = cur->_left;
}
top = StackTop(&s);
if (top->_right == NULL || top->_right == prev)//判断右子树为空或者是否访问过
{
printf("%d ", top->_data);
prev = top;//记录右子树已经访问过
StackPop(&s);//出栈,为访问上一个节点做准备
}
else
{
//右子树没有访问过,开始访问右子树,循环继续(相当于子问题)
cur = top->_right;
}

}

}


3.二叉树的进阶操作

3.1:求树的节点
size_t BTreeSize(BTnode* root)
{
if (root == NULL)//递归结束条件
{
return 0;
}//本题思路是:左右子树节点相加,并且返回时加1 ,1为根节点,采取递归方式
return BTreeSize(root->_left) + BTreeSize(root->_right) + 1;//每递归一次就加1
}

3.2:求树的叶子节点的个数
size_t BTreeLeafSize(BTnode* root)
{

if (root == NULL)
{
return 0;
}
//在树的节点的基础上增加求叶子节点的限制条件,只有当左右子树为空时
//才能说明该节点为叶子结点,即返回1
if (root->_left == NULL && root->_right == NULL)
{
return 1;
}
return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);//依然是左子树叶子结点加右子树叶子结点

}

3.3:求第k层的节点数
size_t BTreeKLevelSize(BTnode* root, size_t k)
{
if (root == NULL)//每递归一层k-1,直到递归到1时,就是第k层
{
return 0;
}
if (1 == k)
{
return 1;
}
return BTreeKLevelSize(root->_left, k - 1) + BTreeKLevelSize(root->_right, k - 1);
}

3.4:求树的深度
size_t BTreeDepth(BTnode* root)
{
if (root == NULL)
{
return 0;
}//判断每一个根节点的左右子树的深度,比较其大小,返回大的
//即该根节点的的深度,记得加1
size_t leftDepth = BTreeDepth(root->_left) + 1;
size_t righDepth = BTreeDepth(root->_right) + 1;
return leftDepth > righDepth ? leftDepth : righDepth;
}

3.5:查找树中的数,找到了返回该节点
BTnode* BTreeFind(BTnode* root, BTDataType x)(遍历一遍查找)
{

if (root == NULL)
{
return 0;
}
if (x == root->_data)
{
return root;
}//左右子树分别查找
BTnode *retLeft = BTreeFind(root->_left, x);
if (retLeft)//如果找到了,就向上返回,防止在递归,浪费时间
{
return retLeft;
}
BTnode *retRight = BTreeFind(root->_right, x);
if (retRight)
{
return retRight;
}
return NULL;
}


4.二叉树的高阶操作

4.1:层序遍历二叉树(即一层一层遍历,遍历结果为:1 2 5 3 4 6 7 )
void BTreeLevelOrder(BTnode* root)
{//层序遍历利用队列为媒介,进行遍历(队列有先进先出性质)
Queue q;//创建队列并初始化
QueueInit(&q);
if (root);//给队列先插入根节点,保证队列不为空,进入循环
{
QueuePush(&q, root);
}
while (QueueEmpty(&q))
{
BTnode *front = QueueFront(&q);//返回对头
printf("%d ", front->_data);//打印当前值
QueuePop(&q);//出队列
if (front->_left != NULL)//当对头节点左子树不为空时,再入队列
{
QueuePush(&q, front->_left);
}
if (front->_right != NULL)//当对头节点右子树不为空时,再入队列
{
QueuePush(&q, front->_right);
}

}
printf("\n");
}


4.2:判断是否是完全二叉树

完全二叉树定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都必须为满的,第 h 层所有的结点都连续集中在最左边。如下图



int IsCompleteBTreeOne(BTnode* root)
{//利用层序遍历来判断是否为完全二叉树
Queue q;
QueueInit(&q);
if (root);//给队列先插入根节点,保证队列不为空,进入循环
{
QueuePush(&q, root);
}
while (QueueEmpty(&q))
{
BTnode *front = QueueFront(&q);//front保存对头
QueuePop(&q);//出头节点
if (front == NULL)//判断front是否为空,为空了终止循环
{
break;
}
QueuePush(&q, front->_left);//继续入左右子树
QueuePush(&q, front->_right);
}
while (QueueEmpty(&q))//当front为空时,判断队列中是否全部为空
//只有全部为空说明这是完全二叉树,否则不是
{
BTnode *front = QueueFront(&q);
if (front != NULL)//当队列中还有不为NULL的
//表示该二叉树不是完全二叉树
{
return 0;
}
QueuePop(&q);//对头为空,出队列,还需判断
}
return 1;
}




4.3:判断是否是完全二叉树 (flag方式,提高效率)
int IsCompleteBTreeThree(BTnode* root)// flag的方式判断 (用到队列)
{
Queue q;
size_t flag = 1;
QueueInit(&q);
if (root);//给队列先插入根节点,保证队列不为空,进入循环
{
QueuePush(&q, root);
}
while (QueueEmpty(&q))
{
BTnode* front = QueueFront(&q);
QueuePop(&q);
if (front->_left != NULL)//当左子树不为空时,入栈
{
if (0 == flag)//上次判断左子树或者右子树已经为空
//现在左子树不为空,则为不完全二叉树
{
return 0;
}
QueuePush(&q, root->_left);
}
else//当左子树为空时,只flag
{
flag = 0;
}
if (front->_right != NULL)//同左
{
if (0 == flag)
{
return 0;
}
QueuePush(&q, root->_right);
}
else
{
flag = 0;
}
}
return 1;
}


4.4:求二叉树中两个节点的最低公共祖先节点
BTnode* BTFindNode(BTnode* root, BTnode *node)//查找树中的节点(遍历一遍查找)
{

if (root == NULL || node == NULL)
{
return 0;
}
if (node == root)
{
return root;
}
BTnode *retLeft = BTFindNode(root->_left, node);
if (retLeft)//如果找到了,就向上返回,防止在递归,浪费时间
{
return retLeft;
}
BTnode *retRight = BTFindNode(root->_right, node);
if (retRight)
{
return retRight;
}
return NULL;
}
//求二叉树中两个节点的最低公共祖先节点
BTnode * GetLastCommonParent(BTnode * root, BTnode * node1, BTnode * node2)//递归解决
{

if (BTFindNode(root->_left, node1))//先在左子树查找node1,找到了进入if语句,再到右子树查找
{
if (BTFindNode(root->_right, node2))
{
return root;//找到返回节点
}
else
{
GetLastCommonParent(root->_left, node1, node2);//没找到递归继续在下层找
}
}
else
{
if (BTFindNode(root->_left, node2))//左子树没有node1,就在左子树查找node2
{
if (BTFindNode(root->_right, node1))
{
return root;
}
else
{
GetLastCommonParent(root->_right, node1, node2);//没找到递归继续在下层找
}
}

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