二叉排序树
介绍
BST(Binary Sort Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大
[注] 如果有相同的值,可以将该节点放在左子节点或右子节点(根据创建算法不同自行决定)
创建思路
class Node
{
void Add(待添加节点 node)
{
如果node为空,直接返回
if(node的Value 小于当前节点的Value)
{
如果当前节点的Left为空,则将当前节点的Left指向node
否则就向左递归 this.Left.Add(node);
}
}
}
删除节点思路
情况一:删除的节点是叶子节点
- 先找到待删除的节点 targetNode
- 找到targetNode的父亲节点 parent
- 确定targetNode是parent的左子节点还是右子节点
- 根据 3 中的情况进行删除
左: parent.Left = null
右: parent.Right = null
情况二:删除的节点有一个子节点
- 找到targetNode及parent
- 确定targetNode的子节点是左子节点还是右子节点
左:Y = Left
右:Y = Right - 确定targetNode是parent的左子节点还是右子节点
左:X = Left
右:Y = Right - parent.X = targetNode.Y;
即用targetNode的子节点替换targetNode
[注] 此情况有特殊情况,整个二叉树只有两个节点,删除的节点是根节点时,此时parent指向空,需要单独处理,否则会有空引用异常
情况三:删除的节点有两个子节点
- 找到targetNode以及parent
- 从targetNode的右子树中找到值最小的节点(或从左子树中找到值最大的节点),将此节点的值存储在一个变量temp中
- 删除此节点
- targetNode.Value = temp
完整代码
using System;
namespace BinarySortTreeDemo
{
class Program
{
static void Main(string[] args)
{
int[] arr = { 7, 3, 10, 12, 5, 1, 9, 0 };
//int[] arr = { 0, 1 };
BinarySortTree bst = new BinarySortTree();
for (int i = 0; i < arr.Length; i++)
{
bst.Add(new Node(arr[i]));
}
bst.InfixOrder();
Console.WriteLine();
bst.DeleteNode(0);
bst.DeleteNode(1);
bst.DeleteNode(3);
bst.DeleteNode(12);
bst.DeleteNode(10);
bst.InfixOrder();
}
}
class BinarySortTree
{
private Node Root;
public void Add(Node node)
{
if (Root == null)
Root = node;
else
Root.AddNode(node);
}
public void InfixOrder()
{
if (Root != null)
Root.InfixOrder();
else
Console.WriteLine("Empty!");
}
//查找要删除的节点
public Node Search(int value)
{
if (Root == null)
return null;
else
return Root.Search(value);
}
//查找待删除结点的父节点
public Node SearchParent(int value)
{
if (this.Root == null)
return null;
else
return Root.SearchParent(value);
}
/// <summary>
/// 1. 返回以node为根节点的二叉排序树的最小节点的值
/// 2. 删除node为根节点的二叉排序树的最小节点
/// </summary>
/// <param name="node">传入节点,当作二叉排序树的根节点</param>
/// <returns>返回以node为根节点的二叉排序树的最小节点的值</returns>
public int DeleteRightTreeMin(Node node)
{
Node t = node;
//循环查找左节点
while (t.Left != null)
{
t = t.Left;
}
//此时 t指向最小节点
DeleteNode(t.Value);
return t.Value;
}
/// <summary>
/// 1. 返回以node为根节点的二叉排序树的最大节点的值
/// 2. 删除node为根节点的二叉排序树的最大节点
/// </summary>
/// <param name="node">传入节点,当作二叉排序树的根节点</param>
/// <returns>返回以node为根节点的二叉排序树的最大节点的值</returns>
public int DeleteLeftTreeMax(Node node)
{
Node t = node;
while (t.Right != null)
{
t = t.Right;
}
DeleteNode(t.Value);
return t.Value;
}
//删除节点
public void DeleteNode(int value)
{
if (Root == null)
return;
//1.查找targetNode
Node targetNode = Search(value);
//如果没有找到要删除的节点
if (targetNode == null)
return;
//如果当前二叉排序树只有一个节点
if (Root.Left == null && Root.Right == null)
{
Root = null;
return;
}
//查找targetNode的父节点
Node parent = SearchParent(value);
//情况一
if (targetNode.Left == null && targetNode.Right == null)
{
//判断targetNode 是parent的Left还是Right
if (parent.Left != null && parent.Left.Value == value)
{
parent.Left = null;
}
else if (parent.Right != null && parent.Right.Value == value)
{
parent.Right = null;
}
}
else if (targetNode.Left != null && targetNode.Right != null)//情况三
{
// 从右子树找到值最小的节点并处理
//int minVal = DeleteRightTreeMin(targetNode.Right);
//targetNode.Value = minVal;
//从左子树找到值最大的节点并处理
int maxVal = DeleteLeftTreeMax(targetNode.Left);
targetNode.Value = maxVal;
}
else //余下的就是只有一颗子树的节点
{
//如果要删除的节点有左子节点
if (targetNode.Left != null)
{
if (parent != null)
{
//如果targetNode是parent的左子节点
if (parent.Left.Value == value)
{
parent.Left = targetNode.Left;
}
else
{
parent.Right = targetNode.Left;
}
}
else
{
Root = targetNode.Left;
}
}
else
{
if (parent != null)
{
if (parent.Left.Value == value)
{
parent.Left = targetNode.Right;
}
else
{
parent.Right = targetNode.Right;
}
}
else
{
Root = targetNode.Right;
}
}
}
}
}
class Node
{
public int Value { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
public Node(int value)
{
this.Value = value;
}
/// <summary>
/// 通过value查找对应节点
/// </summary>
/// <param name="value">希望查找的节点的value</param>
/// <returns>如果查找到对应节点则返回,否则返回null</returns>
public Node Search(int value)
{
if (value == this.Value)
return this;
else if (value < this.Value) //查找的值小于当前节点
{
if (this.Left == null)
return null;
else
return this.Left.Search(value);
}
else
{
if (this.Right == null)
return null;
else
return this.Right.Search(value);
}
}
/// <summary>
/// 通过value查找对应节点的父节点
/// </summary>
/// <param name="value">带查找节点的value</param>
/// <returns>如果查找到对应节点的父节点则返回,否则返回null</returns>
public Node SearchParent(int value)
{
if ((this.Left != null && this.Left.Value == value) || (this.Right != null && this.Right.Value == value))
return this;
else
{
//如果查找的值,小于当前节点的值,并且当前节点的左子节点不为空
if (value < this.Value && this.Left != null)
{
return this.Left.SearchParent(value);
}
else if (value >= this.Value && this.Right != null)
{
return this.Right.SearchParent(value);
}
else
return null;
}
}
//添加节点
public void AddNode(Node node)
{
if (node == null)
return;
//判断节点的值,与当前子树根节点的值的关系
if (node.Value < this.Value)
{
//如果当前节点左子节点为null
if (this.Left == null)
this.Left = node;
else //递归向左子树添加
this.Left.AddNode(node);
}
else //node 的值大于等于当前节点的值
{
if (this.Right == null)
this.Right = node;
else //递归向右处理
this.Right.AddNode(node);
}
}
public void InfixOrder()
{
if (this.Left != null)
this.Left.InfixOrder();
Console.WriteLine(this);
if (this.Right != null)
this.Right.InfixOrder();
}
public override string ToString()
{
return $"Node: [value = {this.Value}]";
}
}
}