点击上方“Java面试题精选”,关注公众号
面试刷图,查缺补漏
号外:****往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅。
0 概述
在说二叉树前,先来看看什么是树。树中基本单位是结点,结点之间的链接,称为分支。一棵树最上面的结点称之为根节点,而下面的结点为子结点。一个结点可以有0个或多个子结点,没有子结点的结点我们称之为叶结点。
二叉树是指子结点数目不超过2个的树,它是一种很经典的数据结构。而二叉搜索树(BST)是有序的二叉树,BST需要满足如下条件:
本文接下来会从定义,二叉搜索树的增删查以及二叉树的递归和非递归遍历进行整理。
1 定义
我们先定义一个二叉树的结点,如下:
typedef struct BTNode {
int value;
struct BTNode *left;
struct BTNode *right;
} BTNode;
其中 value 存储值,left 和 right 指针分别指向左右子结点。二叉搜索树跟二叉树可以使用同一个结构,只是在插入或者查找时会有不同。
2 基本操作
接下来看看二叉树和二叉查找树的一些基本操作,包括BST插入结点,BST查找结点,BST最大值和最小值,二叉树结点数目和高度等。二叉查找树(BST)特有的操作都在函数前加了 bst 前缀区分,其他函数则是二叉树通用的。
1) 创建结点
分配内存,初始化值即可。
/**
* 创建BTNode
*/
BTNode *newNode(int value)
{
BTNode *node = (BTNode *)malloc(sizeof(BTNode));
node-value = value;
node-left = node-right = NULL;
return node;
}
2) BST 插入结点
插入结点可以用递归或者非递归实现,如果待插入值比根节点值大,则插入到右子树中,否则插入到左子树中。如下图所示(图来自参考资料1,2,3):
/**
* BST中插入值,递归方法
*/
/**
* BST中插入结点,递归方法
*/
BTNode *bstInsert(BTNode *root, int value)
{
if (!root)
return newNode(value);
if (root-value value) {
root-left = bstInsert(root-left, value);
} else {
root-right = bstInsert(root-right, value);
}
return root;
}
/**
* BST中插入结点,非递归方法
*/
BTNode *bstInsertIter(BTNode *root, int value)
{
BTNode *node = newNode(value);
if (!root)
return node;
BTNode *current = root, *parent = NULL;
while (current) {
parent = current;
if (current-value value)
current = current-left;
else
current = current-right;
}
if (parent-value = value)
parent-left = node;
else
parent-right = node;
return root;
}
3) BST 删除结点
删除结点稍微复杂一点,要考虑3种情况:
删除的是叶子结点,好办,移除该结点并将该叶子结点的父结点的 left 或者 right 指针置空即可。
删除的结点有两个子结点,则需要找到该结点左子树的最大结点(使用后面的bstSearchIter 函数),并将其值替换到待删除结点中,然后递归调用删除函数删除该结点左子树最大结点即可。
删除的结点只有一个子结点,则移除该结点并将其子结点的值填充到该删除结点即可(需要判断是左孩子还是右孩子结点)。
/**
* BST中删除结点
*/
BTNode *bstDelete(BTNode *root, int value)
{
BTNode *parent = NULL, *current = root;
BTNode *node = bstSearchIter(root, &parent, value);
if (!node) {
printf("Value not foundn");
return root;
}
if (!node-left && !node-right) {
// 情况1:待删除结点是叶子结点
if (node != root) {
if (parent-left == node) {
parent-left = NULL;
} else {
parent-right = NULL;
}
} else {
root = NULL;
}
free(node);
} else if (node-left && node-right) {
// 情况2:待删除结点有两个子结点
BTNode *predecessor = bstMax(node-left);
bstDelete(root, predecessor-value);
node-value = predecessor-value;
} else {
// 情况3:待删除结点只有一个子结点
BTNode *child = (node-left) ? node-left : node-right;
if (node != root) {
if (node == parent-left)
parent-left = child;
else
parent-right = child;
} else {
root = child;
}
free(node);
}
return root;
}
4) BST 查找结点
注意在非递归查找中会将父结点也记录下来。
/**
* BST查找结点-递归
*/
BTNode *bstSearch(BTNode *root, int value)
{
if (!root) return NULL;
if (root-value == value) {
return root;
} else if (root-value value) {
return bstSearch(root-left, value);
} else {
return bstSearch(root-left, value);
}
}
/**
* BST查找结点-非递归
*/
BTNode *bstSearchIter(BTNode *root, BTNode **parent, int value)
{
if (!root) return NULL;
BTNode *current = root;
while (current && current-value != value) {
*parent = current;
if (current-value value)
current = current-left;
else
current = current-right;
}
return current;
}
5)BST 最小值结点和最大值结点
最小值结点从左子树递归查找,最大值结点从右子树递归找。
/**
* BST最小值结点
*/
BTNode *bstMin(BTNode *root)
{
if (!root-left)
return root;
return bstMin(root-left);
}
/**
* BST最大值结点
*/
BTNode *bstMax(BTNode *root)
{
if (!root-right)
return root;
return bstMax(root-right);
}
6)二叉树结点数目和高度
/**
* 二叉树结点数目
*/
int btSize(BTNode *root)
{
if (!root) return 0;
return btSize(root-left) + btSize(root-right) + 1;
}
/**
* 二叉树高度
*/
int btHeight(BTNode *root)
{
if (!root) return 0;
int leftHeight = btHeight(root-left);
int rightHeight = btHeight(root-right);
int maxHeight = leftHeight rightHeight ? leftHeight+1 : rightHeight+1;
return maxHeight;
}
3 二叉树遍历
递归遍历-先序、中序、后序、层序
二叉树遍历的递归实现比较简单,直接给出代码。这里值得一提的是层序遍历,先是计算了二叉树的高度,然后调用的辅助函数依次遍历每一层的结点,这种方式比较容易理解,虽然在时间复杂度上会高一些。
/**
* 二叉树先序遍历
*/
void preOrder(BTNode *root)
{
if (!root) return;
printf("%d ", root-value);
preOrder(root-left);
preOrder(root-right);
}
/**
* 二叉树中序遍历
*/
void inOrder(BTNode *root)
{
if (!root) return;
inOrder(root-left);
printf("%d ", root-value);
inOrder(root-right);
}
/**
* 二叉树后序遍历
*/
void postOrder(BTNode *root)
{
if (!root) return;
postOrder(root-left);
postOrder(root-right);
printf("%d ", root-value);
}
/**
* 二叉树层序遍历
*/
void levelOrder(BTNode *root)
{
int btHeight = height(root);
int level;
for (level = 1; level = btHeight; level++) {
levelOrderInLevel(root, level);
}
}
/**
* 二叉树层序遍历辅助函数-打印第level层的结点
*/
void levelOrderInLevel(BTNode *root, int level)
{
if (!root) return;
if (level == 1) {
printf("%d ", root-value);
return;
}
levelOrderInLevel(root-left, level-1);
levelOrderInLevel(root-right, level-1);
}
非递归遍历-先序、中序、后序、层序
这里我另外实现了一个队列 BTNodeQueue 和栈 BTNodeStack,用于二叉树非递归遍历。
/*********************/
/** 二叉树遍历-非递归 **/
/*********************/
/**
* 先序遍历-非递归
*/
void preOrderIter(BTNode *root)
{
if (!root) return;
int size = btSize(root);
BTNodeStack *stack = stackNew(size);
push(stack, root);
while (!IS_EMPTY(stack)) {
BTNode *node = pop(stack);
printf("%d ", node-value);
if (node-right)
push(stack, node-right);
if (node-left)
push(stack, node-left);
}
free(stack);
}
/**
* 中序遍历-非递归
*/
void inOrderIter(BTNode *root)
{
if (!root) return;
BTNodeStack *stack = stackNew(btSize(root));
BTNode *current = root;
while (current || !IS_EMPTY(stack)) {
if (current) {
push(stack, current);
current = current-left;
} else {
BTNode *node = pop(stack);
printf("%d ", node-value);
current = node-right;
}
}
free(stack);
}
/**
* 后续遍历-使用一个栈非递归
*/
void postOrderIter(BTNode *root)
{
BTNodeStack *stack = stackNew(btSize(root));
BTNode *current = root;
do {
// 移动至最左边结点
while (current) {
// 将该结点右孩子和自己入栈
if (current-right)
push(stack, current-right);
push(stack, current);
// 往左子树遍历
current = current-left;
}
current = pop(stack);
if (current-right && peek(stack) == current-right) {
pop(stack);
push(stack, current);
current = current-right;
} else {
printf("%d ", current-value);
current = NULL;
}
} while (!IS_EMPTY(stack));
}
/**
* 后续遍历-使用两个栈,更好理解一点。
*/
void postOrderIterWith2Stack(BTNode *root)
{
if (!root) return;
BTNodeStack *stack = stackNew(btSize(root));
BTNodeStack *output = stackNew(btSize(root));
push(stack, root);
BTNode *node;
while (!IS_EMPTY(stack)) {
node = pop(stack);
push(output, node);
if (node-left)
push(stack, node-left);
if (node-right)
push(stack, node-right);
}
while (!IS_EMPTY(output)) {
node = pop(output);
printf("%d ", node-value);
}
}
/**
* 层序遍历-非递归
*/
void levelOrderIter(BTNode *root)
{
if (!root) return;
BTNodeQueue *queue = queueNew(btSize(root));
enqueue(queue, root);
while (1) {
int nodeCount = QUEUE_SIZE(queue);
if (nodeCount == 0)
break;
btHeight
while (nodeCount 0) {
BTNode *node = dequeue(queue);
printf("%d ", node-value);
if (node-left)
enqueue(queue, node-left);
if (node-right)
enqueue(queue, node-right);
nodeCount--;
}
printf("n");
}
}
参考资料
http://www.techiedelight.com/insertion-in-bst/ http://www.techiedelight.com/search-given-key-in-bst/ http://www.techiedelight.com/deletion-from-bst/ https://www.geeksforgeeks.org/print-level-order-traversal-line-line/ https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/ https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/
最近三期
与其在网上拼命找题?** 不如马上关注我们~**
原文始发于微信公众号(Java面试题精选):