二叉树其他面试题链接:二叉树面试题——下
一、创建一棵二叉树
思路:利用数组把二叉树用代码表示出来,例如创建下面这颗树:
int a[] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6, '#', '#', '#' };
传参时把数组名、数组下标以及非法值传过去。invalid是‘#’的ascll码值,代表NULL,如果某节点的左右孩子都是‘#’,那他就是叶子。
创建使用的是递归法,具体代码实现如下:
(PS:此处传数组下标采用的是传址方式,因为递归法会有多层函数栈帧,用传值的方法的话,在这层函数栈帧里修改了下标并不会影响其它栈帧里面下标的值,那样就会导致创建树失败!)
typedef int BTSTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
BTSTDataType _data;
}BTNode;
BTNode* BuyBTNode(BTSTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
assert(node != NULL);
node->_left = NULL;
node->_right = NULL;
node->_data = x;
return node;
}
BTNode* CreateBTree(BTSTDataType* a, size_t* pIndex, BTSTDataType invalid)// 创建二叉树
{
BTNode* root = NULL;
if (a[*pIndex] != invalid)
{
root = BuyBTNode(a[*pIndex]);
(*pIndex)++;
root->_left = CreateBTree(a, pIndex, invalid);
(*pIndex)++;
root->_right = CreateBTree(a, pIndex, invalid);
}
return root;
}
二、前序遍历(非递归法)
思路:用栈结构来实现非递归前序遍历:
- 创建一个栈s并初始化;
- 从树根开始,把1,2,3依次入栈并输出;
- 访问3的左孩子时,它为NULL,内层循环结束;
- 3出栈,访问3的右孩子;
………….
void BTreePrevOrderNonR(BTNode* root)
{
Stack s;
StackInit(&s);
BTNode* top = NULL;
BTNode* cur = root;
while (cur != NULL || StackEmpty(&s) != 0)
{
while (cur != NULL)
{
printf("%d ", cur->_data);
StackPush(&s, cur);
cur = cur->_left;
}
top = StackTop(&s);
StackPop(&s);
cur = top->_right; //子问题
}
}
三、中序遍历(非递归法)
思路:和上一题大致相同,只是改变了输出函数的位置。
void BTreeInOrderNonR(BTNode* root)
{
Stack s;
StackInit(&s);
BTNode* top = NULL;
BTNode* cur = root;
while (cur != NULL || StackEmpty(&s) != 0)
{
while (cur != NULL)
{
StackPush(&s, cur);
cur = cur->_left;
}
top = StackTop(&s);
printf("%d ", top->_data);
StackPop(&s);
cur = top->_right;
}
}
四、后序遍历(非递归法)
思路:加一个prev指针,它指向上一个访问的节点。
- 首先 1、2、3依次入栈;
- 访问3的左孩子,为NULL,跳出内层循环,top = 3;
- 输出3,prev = 3,节点3出栈;
- top = 2,跳过if进入else,cur = 4;
- 4入栈,cur = NULL,top = 4,prev = 4,4出栈;
- top = 2,进入if,输出2,prev = 2,2出栈;
……………….
void BTreePostOrderNonR(BTNode* w)
{
Stack s;
StackInit(&s);
BTNode* cur = root;
BTNode* top = NULL;
BTNode* prev = NULL;//z
while (cur != NULL || StackEmpty(&s) != 0)
{
while (cur != NULL)
{
StackPush(&s, cur);
cur = cur->_left;
}
top = StackTop(&s);
//判断top的右树有没有访问过
if (NULL == top->_right || top->_right == prev)
{
printf("%d ", top->_data);
prev = top;
StackPop(&s);
}
else
{
//右子树还没有访问过,子问题方式访问
cur = top->_right;
}
}
}
五、求树的节点个数
思路:一棵树的节点个数 = 它左子树节点个数 + 它右子树节点个数 + 加它本身
size_t BTreeSize(BTNode* root)//节点个数
{
if (NULL == root)
{
return 0;
}
//转换成子问题:树节点个数等于左子树节点个数 + 右子树节点个数 + 加自己
return BTreeSize(root->_left) + BTreeSize(root->_right) + 1;
}
六、求树的叶子个数
size_t BTreeLeafSize(BTNode* root) //计算树叶子的数量
{
if (NULL == root)
{
return 0;
}
if (root->_left == NULL && root->_right == NULL)
{
return 1;
}
return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);
}
七、求第K层节点个数
思路:树tree第K层节点个数 = 它左子树第K-1层节点个数 + 它右子树第K-1层节点个数
size_t BTreeKLevelSize(BTNode* root, size_t k)
{
if (NULL == root)
{
return 0;
}
if (1 == k)
{
return 1;
}
return BTreeKLevelSize(root->_left, k - 1) + BTreeKLevelSize(root->_right, k - 1);
}
八、求树的深度
思路:一棵树的深度 = 它左、右子树深度较大的那个子树的深度 + 1
size_t BTreeDepth(BTNode* root)
{
if (NULL == root)
{
return 0;
}
size_t leftDepth = BTreeDepth(root->_left);
size_t rightDepth = BTreeDepth(root->_right);
//树的深度 = 它左右两个子树深度较大的那个 + 1;
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
九、在树中查找指定节点
BTNode* BTreeFind(BTNode* root, BTSTDataType x)
{
if (NULL == root)
{
return NULL;
}
if (x == root->_data)
{
return root;
}
BTNode* leftRet = BTreeFind(root->_left, x);
if (leftRet != NULL)
{
return leftRet;
}
BTNode* rightRet = BTreeFind(root->_right, x);
if (rightRet != NULL)
{
return rightRet;
}
return NULL;
}
十、层序遍历
思路:创建一个队列q并初始化;
1. 根节点1入队,*front = 1,输出1,1出队;
2. 把1的左、右孩子2和5入队;
3. *front = 2,输出2,2出队;
4. 把2的左、右孩子3和5入队;
5. *front = 5,输出5,5出队;
6. 把5的左孩子6入队(5的右孩子为NULL);
………………….
void BTreeLevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root != NULL)
{
QueuePush(&q, root);
}
while (QueueEmpty(&q) != 0)
{
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);
}
}
}
十一、判断一棵树是否为完全二叉树(方法1)
1. 方法一
思路:采用和上一题相似的方法,把树的节点按层入队,当遇到NULL时,入队部分结束,进入下一个循环,把队列里的元素出队,如果此时队列的元素都是NULL,那这棵树就是完全二叉树;如果还有非空元素,那就是非完全二叉树。
int IsCompleteBTree(BTNode* root)// 判断完全二叉树
{
Queue q;
QueueInit(&q);
if (root != NULL)
{
QueuePush(&q, root);
}
while (QueueEmpty(&q) != 0)
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (NULL == front)
{
break;
}
else
{
QueuePush(&q, front->_left);
QueuePush(&q, front->_right);
}
}
while (QueueEmpty(&q) != 0)
{
BTNode* front = QueueFront(&q);
if (front != NULL)
{
return 0; //不是完全二叉树
}
QueuePop(&q);
}
return 1; //是完全二叉树
}
2. 方法二
思路:利用一个flag变量,先还是层序的方法进出队,遇到NULL时,flag = 0;如果下次再出队的遇到非空元素且此时flag = 0,那这就是非完全二叉树。
int IsCompleteBTree1(BTNode* root)// flag的方式判断 非递归遍历
{
Queue q;
QueueInit(&q);
int flag = 1;
if (root != NULL)
{
QueuePush(&q, root);
}
while (QueueEmpty(&q) != 0)
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front->_left != NULL)
{
if (0 == flag)
{
return 0;
}
QueuePush(&q, front->_left);
}
else
{
flag = 0;
}
if (front->_right != NULL)
{
if (0 == flag)
{
return 0;
}
QueuePush(&q, front->_right);
}
else
{
flag = 0;
}
}
return 1;
}