==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍
二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
1.若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2.若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3.任意节点的左、右子树也分别为二叉查找树;
4.没有键值相等的节点。
目录
1、查找
2、添加
3、删除
4、查找最小值
5、查找最大值
6、从小到大遍历
7、从大到小遍历
用如图二叉树进行说明
一、查找
步骤如下:
1.和根节点比较,若值相等,则查找成功(每一刻子树都有根节点)
2.若值小于根节点,则递归查找左子树
3.若值大于根节点,则递归查找右子树
4.重复1~3步骤,直到查找成功
如图:
C代码:
/*
* function 查找节点
* param PBTreeSearch 二叉查找树指针
* param int value 要查找的值
* return PBTreeSearch 返回该结点的指针
*/
PBTreeSearch SearchNode(PBTreeSearch btree, int value)
{
// 1.如果该节点为空,则退出
if (!btree)
return NULL;
// 2.如果该节点的值等于value,则返回该节点
else if (btree->value == value)
return btree;
// 3.如果该节点的值大于value,则递归遍历左子树
else if (btree->value > value)
SearchNode(btree->left, value);
// 4.如果该节点的值小于value,则递归遍历右子树
else if (btree->value < value)
SearchNode(btree->right, value);
}
java代码:
/*
* function 查找节点
* param Node 节点
* param int value 要查找的值
* return Node 成功返回该结点,否则返回null
*/
private Node searchNode(Node btree,int value) {
// 1.如果该节点为空,则退出
if (btree == null)
return null;
// 2.如果该节点的值等于value,则返回该节点
else if (btree.compare(value) == 0)
return btree;
// 3.如果该节点的值大于value,则递归遍历左子树
else if (btree.compare(value) == 1)
return searchNode(btree.getLeft(), value); //这里记得要加个return,否则最后会执行最后的return null
// 4.如果该节点的值小于value,则递归遍历右子树
else if (btree.compare(value) == -1)
return searchNode(btree.getRight(), value);
return null;
}
二、添加
步骤如下:
1.若根节点为空,则直接创建根节点(第一次添加肯定走这一步,后面就不用走了)
2.先查找要添加的值存不存在,若存在,则该节点的count自加即可
3.若不存在,则创建一个新的节点node
a)如果该节点的值大于value,且其左节点为null,则将node作为该节点的左孩子
b)如果该节点的值小于value,且其右节点为null,则将node作为该节点的右孩子
c)如果该节点的值大于value,且其左节点不为null,则递归左子树
d)如果该节点的值小于value,且其右节点不为null,则递归右子树
如图:
C代码:
/*
* function 添加节点辅助函数
* param PBTreeSearch 二叉查找树指针
* param PBTreeSearch 要添加的节点
* return 无
*/
void Add(PBTreeSearch btree, PBTreeSearch node)
{
if (!btree)
btree = node;
// 1.如果该节点的值大于value,且其左节点为null,则将node作为该节点的左孩子
else if (btree->value > node->value && !btree->left)
btree->left = node;
// 2.如果该节点的值小于value,且其右节点为null,则将node作为该节点的右孩子
else if (btree->value < node->value && !btree->right)
btree->right = node;
// 3.如果该节点的值大于value,且其左节点不为null,则递归左子树
else if (btree->value > node->value && btree->left)
Add(btree->left, node);
// 4.如果该节点的值小于value,且其右节点不为null,则递归右子树
else if (btree->value < node->value && btree->right)
Add(btree->right, node);
}
/*
* function 添加节点
* param PBTreeSearch 二叉查找树指针
* param int value 要添加的值
* return 无
*/
void AddValue(PBTreeSearch* btree, int value)
{
// 1.如果btree为null,说明当前是第一次添加根节点
if (!(*btree))
*btree = CreateNode(value);
else
{
// 2.先查找value值,如果存在,则直接count++
PBTreeSearch node = SearchNode(*btree, value);
if (node)
node->count++;
else
{
// 3.如果该值不存在,则创建新结点
node = CreateNode(value);
// 4.添加节点
Add(*btree, node);
}
}
}
三、删除
删除要分为三种情况。
1.该节点无孩子,直接删除
2.该节点有且只有一个节点
3.该节点有两个孩子(这里有个技巧,那就是它的右子树中最小的值肯定比它的左孩子大,比它的右孩子小)
还需要注意的是删除前要找到其父节点,要将指向孩子的指针置空,否则遍历的时候会出错。下面的步骤中,已经将这三个步骤封装成一个函数了。所以直接说删除。
步骤如下:
1.如果二叉树为空,则删除失败
2.如果要删除的是根节点,则必须构造一个根节点的父节点,待删除完成后,再修改根节点的指向。(因为除了根节点之外,其它节点必定有父节点)
3.如果是其它结点
a)先查找该节点的父节点,若父节点不存在,则删除失败(说明删除的值不存在)
b)若父节点存在,则删除该节点,并修改父节点的指向。(这里包含了三个小步骤)
如图:
C代码:
/*
* function 删除节点辅助函数
* param cur 需要删除的节点
* param par 需要删除节点的父节点
* param bLeft cur是左节点还是右节点
* return 无
*/
void Delete(PBTreeSearch* cur, PBTreeSearch* par,bool bLeft)
{
// 1.若该节点无孩子,则直接删除
if (!(*cur)->left && !(*cur)->right)
{
free(*cur);
if(bLeft)
(*par)->left = NULL;
else
(*par)->right = NULL;
}
// 2.若该节点只有一个孩子,则将该孩子直接顶替到原来的位置上
else if ((!(*cur)->left && (*cur)->right) || ((*cur)->left && !(*cur)->right))
{
PBTreeSearch temp = *cur;
if (temp->left) //根据bLeft的值来修改孩子节点
if(bLeft)
(*par)->left = temp->left;
else
(*par)->right = temp->left;
else
if(bLeft)
(*par)->left = temp->right;
else
(*par)->right = temp->right;
free(temp);
}
// 3.若该节点有两个孩子,则将该节点的右子树中最小的那个节点替换上去
else
{
PBTreeSearch minNode = FindMinNode((*cur)->right); //找到需要删除的节点的右子树中最小的节点
PBTreeSearch parent = SearchParentNode((*cur), minNode->value); //同样,获取父节点是为了置孩子节点为null
(*cur)->value = minNode->value; //修改值就好
if (parent->left->value == minNode->value) //如果是在左孩子中找到最小值
parent->left = NULL;
else if (parent->right->value == minNode->value) //如果是在右孩子中找到,则左孩子肯定为null,这时只需要修改指向即可
(*cur)->right = minNode->right;
free(minNode);
}
}
/*
* function 删除节点
* param PBTreeSearch 二叉查找树指针
* param int value 要删除的值
* return 无
*/
void DeleteValue(PBTreeSearch* btree, int value)
{
// 1.如果btree为空则退出
if (!(*btree));
// 2.如果该节点是根节点的话
else if ((*btree)->value == value)
{
PBTreeSearch rootPar = (PBTreeSearch)malloc(sizeof(_btree_search));
rootPar->left = *btree;
Delete(btree, &rootPar, true); //这里模拟下面的,声明一个root的父节点,然后以同样的方式调用它
*btree = rootPar->left; //最后要将root节点的指向修改
free(rootPar);
}
else
{
// 3.查找需要删除节点的父节点 (需要找到父节点是因为最后要将它的孩子置为null)
PBTreeSearch par = SearchParentNode(*btree, value);
if (!par)
return;
// 4.删除该节点(因为不懂是左还是右,所以要进一步判断)
if (par->left && par->left->value == value)
Delete(&par->left, &par, true);
else
Delete(&par->right, &par, false);
}
}
java代码:
/*
* function 删除节点辅助函数
* param cur 需要删除的节点
* param par 需要删除节点的父节点
* param bLeft cur是左节点还是右节点
* return 无
*/
private void delete(Node cur, Node par, boolean bLeft) {
// 1.若该节点无孩子,则直接删除
if (cur.isLRNull()) {
if (bLeft)
par.setLeft(null);
else
par.setRight(null);
}
// 2.若该节点只有一个孩子,则将该孩子直接顶替到原来的位置上
else if (cur.isOnlyOne()) {
if (!cur.isLNull()) //根据bLeft的值来修改孩子节点
if (bLeft)
par.setLeft(cur.getLeft());
else
par.setRight(cur.getLeft());
else
if (bLeft)
par.setLeft(cur.getRight());
else
par.setRight(cur.getRight());
}
// 3.若该节点有两个孩子,则将该节点的右子树中最小的那个节点替换上去
else {
Node minNode = findMinNode(cur.getRight()); //找到需要删除的节点的右子树中最小的节点
Node parent = searchParentNode(cur, minNode.value); //同样,获取父节点是为了置孩子节点为null
cur.value = minNode.value; //修改值就好
if (parent.getLeft().compare(minNode.value) == 0) //如果是在左孩子中找到最小值
parent.setLeft(null);
else if (parent.getRight().compare(minNode.value) == 0) //如果是在右孩子中找到,则左孩子肯定为null,这时只需要修改指向即可
cur.setRight(minNode.getRight());
}
}
/*
* function 删除值
* param int value 要删除的值
* return 删除失败返回false,否则返回true
*/
public boolean delete(int value) {
// 1.如果btree为空则退出
if (root == null) return false;
// 2.如果该节点是根节点的话
else if (root.compare(value) == 0) {
Node rootPar = new Node(0);
rootPar.setLeft(root);
delete(root, rootPar, true); //这里模拟下面的,声明一个root的父节点,然后以同样的方式调用它
root = rootPar.getLeft(); //最后要将root节点的指向修改
--nodeCount;
}
else {
// 3.查找需要删除节点的父节点 (需要找到父节点是因为最后要将它的孩子置为null)
Node par = searchParentNode(root, value);
if (par == null)
return false;
// 4.删除该节点(因为不懂是左还是右,所以要进一步判断)
if (!par.isLNull() && par.getLeft().compare(value) == 0)
delete(par.getLeft(), par, true);
else
delete(par.getRight(), par, false);
--nodeCount;
}
return true;
}
四、查找最小值
有一个规律就是,任意一个根节点开始,其最左边的节点必定是以该根节点构建的二叉树中值最小的。所以我们只需要迭代,找到其最左边的节点即可。
C代码:
/*
* function 查找最小节点
* param PBTreeSearch 二叉查找树指针
* return 无
*/
PBTreeSearch FindMinNode(PBTreeSearch btree)
{
if (!btree)
return NULL;
PBTreeSearch cur = btree;
while (cur->left)
cur = cur->left;
return cur;
}
/*
* function 查找最小值
* param PBTreeSearch 二叉查找树指针
* return 无
*/
int FindMinValue(PBTreeSearch btree)
{
PBTreeSearch node = FindMinNode(btree);
return node ? node->value : NO_FIND;
}
五、查找最大值
和查找最小值一样,其最右边的必定是最大值
/*
* function 查找最大值
* param PBTreeSearch 二叉查找树指针
* return 无
*/
int FindMaxValue(PBTreeSearch btree)
{
if (!btree)
return NO_FIND;
PBTreeSearch cur = btree;
while (cur->right)
cur = cur->right;
return cur->value;
}
六、从小到大遍历
二叉查找树有这样一个规律,其 右孩子 > 根节点 > 左节点,所以遍历顺序为:左 -> 根 ->右
/*
* function 遍历二叉查找树(从小到大,用中序遍历即可)
* param PBTreeSearch 二叉查找树指针
* return 无
*/
void PrintMinToMax(PBTreeSearch btree)
{
if (btree)
{
PrintMinToMax(btree->left);
int i = btree->count;
while (i--) //循环输出相同的值
printf("%d ", btree->value);
PrintMinToMax(btree->right);
}
}
七、从大到小遍历
二叉查找树有这样一个规律,其 右孩子 > 根节点 > 左节点,所以遍历顺序为:右 -> 根 ->左
/*
* function 遍历二叉查找树(从大到小,遍历顺序:右 -> 根 -> 左)
* param PBTreeSearch 二叉查找树指针
* return 无
*/
void PrintMaxToMin(PBTreeSearch btree)
{
if (btree)
{
PrintMaxToMin(btree->right);
int i = btree->count;
while (i--) //循环输出相同的值
printf("%d ", btree->value);
PrintMaxToMin(btree->left);
}
}