概念
二叉搜索树
1、什么是二叉搜索树
二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树。
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
- 非空左子树的所有键值小于其根结点的键值。
- 非空右子树的所有键值大于其根结点的键值。
- 左、右子树都是二叉搜索树
因为 中序遍历,必定得到一个有序序列, 即为:二叉排序树
例子:
3、缺点:根结点如果没有选取好,那么这颗二叉搜索树会“退化”,变成一根链条
使用C#实现二叉搜索树
正常手搓一个二叉搜索树,结点内的数据类型只能定义为 int,只进行简单的比大小
因此作出尝试,将数据类型定义为 T (泛型),满足“字符串、数字、数组” 等数据类型
解决数据比较问题:
在插入值时,需要将插入值与当前结点的值进行比较,如果数据类型为 T ,值之间的比较方法就需要由创建二叉搜索树的类,自定义回调方法
使用 deledage (委托) 实现回调
采用非递归实现所有功能
二叉树的结构
public class TreeNode<T>
{
public int layout; // 层级
public T content; // 内容
public TreeNode<T> left; // 左子树
public TreeNode<T> right; // 右子树
public TreeNode<T> parent; // 父结点
}
其中 layout 的作用是标记当前结点在整个二叉树内的层级(即为 深度),方便打印二叉树而作的一个标记
结点中值的对比方法
public delegate bool CompareEqual(T a, T b); // 判断相等, false为 a!=b ,true为 a=b
public delegate bool Compare(T a, T b); // 判断大小, false为 a<b ,true为 a>b
该回调需要自定义后才能使用
例如 数据类型为 int 时:
CompareEqual compareEqual = delegate(int i, int i1) { return i == i1; }, CompareEqual compare = delegate(int i, int i1) { return i > i1; })
创建二叉树控制器
public class TreeControll<T>
{
public delegate bool CompareEqual(T a, T b); // 判断相等, false为 a!=b ,true为 a=b
public delegate bool Compare(T a, T b); // 判断大小, false为 a<b ,true为 a>b
private TreeNode<T> _treeNode;
private CompareEqual _compareEqual;
private Compare _compare;
public TreeControll(T[] contents, CompareEqual compareEqual, Compare compare)
{
_compareEqual = compareEqual;
_compare = compare;
for (int i = 0; i < contents.Length; i++)
{
// 插入方法
// Insert(contents[i]);
}
}
public TreeControll(CompareEqual compareEqual, Compare compare)
{
_compareEqual = compareEqual;
_compare = compare;
}
}
创建存储 int 类型的二叉搜索树 // 构造数据 int[] nums = new[] {15, 10, 19, 8, 13, 16, 28, 5, 9, 12, 14, 20, 30, 25}; TreeControll<int> treeControll = new TreeControll<int>(nums, delegate(int i, int i1) { return i == i1; }, delegate(int i, int i1) { return i > i1; });
打印下看看:(没有做处理,只是根据层级简单打印了下)
插入
二叉搜索树在插入前,要找到插入的位置,假设需要插入的值为 num
从根结点开始,比较num与当前结点的值
当 num < 当前结点.content 时,判断是否存在左子结点
存在:搜索 当前结点 的 左子结点
不存在:创建新的结点,新结点作为当前节点的左子结点
当 num > 当前结点.content 时,判断是否存在右子结点
存在:搜索 当前结点 的 右子结点
不存在:创建新的结点,新结点作为当前节点的右子结点
public void Insert(T content) // 用非递归实现
{
if (_treeNode == null)
{
_treeNode = new TreeNode<T>();
_treeNode.layout = 0;
_treeNode.content = content;
return;
}
TreeNode<T> curNode = _treeNode;
while (true)
{
if (_compareEqual(content, curNode.content))
{
// 特殊情况:插入的值在树中存在
Console.WriteLine("插入失败,该值已存在:" + content);
break;
}
if (_compare(content, curNode.content))
{
if (curNode.right != null) { curNode = curNode.right; }
else
{
TreeNode<T> node = new TreeNode<T>();
node.layout = curNode.layout + 1;
node.content = content;
node.parent = curNode;
curNode.right = node;
break;
}
}
else
{
if (curNode.left != null) { curNode = curNode.left; }
else
{
TreeNode<T> node = new TreeNode<T>();
node.layout = curNode.layout + 1;
node.content = content;
node.parent = curNode;
curNode.left = node;
break;
}
}
}
}
搜索
(1)查找从根结点开始,如果树为空,返回NULL
(2)若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
① 若X小于根结点键值,只需在左子树中继续搜索;
② 如果X大于根结点的键值,在右子树中进行继续搜索;
③若两者比较结果是相等,搜索完成,返回指向此结点的指针。
public TreeNode<T> Find(T content) // 用非递归实现
{
// 目前该功能只负责返回 是否找到
Console.WriteLine("---------------- 查找 ---------------");
bool isFind = false;
TreeNode<T> curNode = _treeNode;
while (curNode != null)
{
if (_compareEqual(content, curNode.content))
{
Console.WriteLine("已找到节点: " + content);
Console.WriteLine("层级为: " + curNode.layout + " 父节点为: " + (curNode.parent != null ? curNode.parent.content.ToString() : "空"));
isFind = true;
break;
}
if (_compare(content, curNode.content))
{
curNode = curNode.right;
}
else
{
curNode = curNode.left;
}
}
if (!isFind)
{
Console.WriteLine("没有找到节点: " + content);
}
Console.WriteLine("----------------查找结束---------------\n");
return isFind ? curNode : null;
}
查找 13 和 -1
删除结点
对于二叉搜索树的删除,相对来说就比较麻烦了。因为要考虑以下三种情况:
①要删除的是叶结点;左右子树都为空,删掉它并没什么后顾之忧,所以当我们要删除的是叶结点的时候,直接删除就好了。当然不要忘记一个重要的操作——删除之后要修改其父结点指针,即置为NULL
②要删除的结点只有一个孩子结点;被删除的结点的孩子无论是左孩子还是右孩子,都只会比被删除的结点的父结点小,所以我们只需要将被删除的结点的父结点的指针指向被删除的结点的孩子结点。
③要删除的结点有左、右两棵子树;用被删除结点的右子树的最小元素或者左子树的最大元素替代被删除结点。
因此设定 repleaseLeft 来自定义是找左子树还是右子树
public void Remove(T content, bool repleaseLeft = true) // 用非递归实现
{
// 找到该节点
TreeNode<T> curNode = Find(content);
bool isEnd = false;
while (!isEnd)
{
if (curNode.left == null && curNode.right == null) // 叶子结点
{
if (curNode.parent != null)
{
if (_compare(curNode.content, curNode.parent.content))
curNode.parent.right = null;
else
curNode.parent.left = null;
}
curNode = null;
isEnd = true;
}
else if(curNode.left != null && curNode.right != null) // 存在左右节点
{
TreeNode<T> replaceNode;
if (repleaseLeft) // 找到左节点最大的节点
{
replaceNode = curNode.left;
while (replaceNode.right != null)
{
replaceNode = replaceNode.right;
}
}
else // 找到右节点最小的节点
{
replaceNode = curNode.right;
while (replaceNode.left != null)
{
replaceNode = replaceNode.left;
}
}
curNode.content = replaceNode.content;
curNode = replaceNode;
}
else // 只存在一个子节点
{
TreeNode<T> tmpNode;
if (curNode.left != null)
tmpNode = curNode.left;
else
tmpNode = curNode.right;
tmpNode.parent = curNode.parent;
curNode = null;
if (_compare(tmpNode.content, tmpNode.parent.content))
tmpNode.parent.right = tmpNode;
else
tmpNode.parent.left = tmpNode;
refreshLayout(tmpNode);
isEnd = true;
}
}
}
// 刷新从当前结点开始的层级
private void refreshLayout(TreeNode<T> rootNode)
{
Queue<TreeNode<T>> queue = new Queue<TreeNode<T>>();
queue.Enqueue(rootNode);
while (queue.Count != 0)
{
TreeNode<T> node = queue.Dequeue();
if (node.parent != null) node.layout = node.parent.layout + 1;
else node.layout = 0;
if (node.left != null) queue.Enqueue(node.left);
if (node.right != null) queue.Enqueue(node.right);
}
}
删除结点 10 后,打印树:
总结
至此,瞎想的什么 泛型二叉搜索树 就写完了
写的比较水。。。。
全部代码:
TreeNode.cs
public class TreeNode<T>
{
public int layout; // 层级
public T content; // 内容
public TreeNode<T> left; // 左子树
public TreeNode<T> right; // 右子树
public TreeNode<T> parent; // 父节点
}
TreeControll.cs
public class TreeControll<T>
{
public delegate bool CompareEqual(T a, T b); // 判断相等, false为 a!=b ,true为 a=b
public delegate bool Compare(T a, T b); // 判断大小, false为 a<b ,true为 a>b
private TreeNode<T> _treeNode;
private CompareEqual _compareEqual;
private Compare _compare;
public TreeControll(T[] contents, CompareEqual compareEqual, Compare compare)
{
_compareEqual = compareEqual;
_compare = compare;
for (int i = 0; i < contents.Length; i++)
{
Insert(contents[i]);
}
}
public TreeControll(CompareEqual compareEqual, Compare compare)
{
_compareEqual = compareEqual;
_compare = compare;
}
/// <summary>
/// 插入值
/// </summary>
/// <param name="content"></param>
public void Insert(T content) // 用非递归实现
{
if (_treeNode == null)
{
_treeNode = new TreeNode<T>();
_treeNode.layout = 0;
_treeNode.content = content;
return;
}
TreeNode<T> curNode = _treeNode;
while (true)
{
if (_compareEqual(content, curNode.content))
{
// 特殊情况:插入的值在树中存在
Console.WriteLine("插入失败,该值已存在:" + content);
break;
}
if (_compare(content, curNode.content))
{
if (curNode.right != null) { curNode = curNode.right; }
else
{
TreeNode<T> node = new TreeNode<T>();
node.layout = curNode.layout + 1;
node.content = content;
node.parent = curNode;
curNode.right = node;
break;
}
}
else
{
if (curNode.left != null) { curNode = curNode.left; }
else
{
TreeNode<T> node = new TreeNode<T>();
node.layout = curNode.layout + 1;
node.content = content;
node.parent = curNode;
curNode.left = node;
break;
}
}
}
}
/// <summary>
/// 查找值
/// </summary>
/// <param name="content"></param>
/// <returns></returns>
public TreeNode<T> Find(T content) // 用非递归实现
{
// 目前该功能只负责返回 是否找到
Console.WriteLine("---------------- 查找 ---------------");
bool isFind = false;
TreeNode<T> curNode = _treeNode;
while (curNode != null)
{
if (_compareEqual(content, curNode.content))
{
Console.WriteLine("已找到节点: " + content);
Console.WriteLine("层级为: " + curNode.layout + " 父节点为: " + (curNode.parent != null ? curNode.parent.content.ToString() : "空"));
isFind = true;
break;
}
if (_compare(content, curNode.content))
{
curNode = curNode.right;
}
else
{
curNode = curNode.left;
}
}
if (!isFind)
{
Console.WriteLine("没有找到节点: " + content);
}
Console.WriteLine("----------------查找结束---------------\n");
return isFind ? curNode : null;
}
/// <summary>
/// 删除值
/// </summary>
/// <param name="content"></param>
/// <param name="repleaseLeft"> 删除值后的替换元素,是否采用左节点内的元素 </param>
public void Remove(T content, bool repleaseLeft = true) // 用非递归实现
{
/*
* 1、删除叶子节点:直接删
* 2、删除的节点只包含一个孩子节点:将该节点的父节点指向该节点的孩子节点
* 2、删除的节点包含左右节点:找到该节点左子树中的最大元素 或 右子树中的最小元素, 进行替换
*/
// 找到该节点
TreeNode<T> curNode = Find(content);
bool isEnd = false;
while (!isEnd)
{
if (curNode.left == null && curNode.right == null) // 叶子结点
{
if (curNode.parent != null)
{
if (_compare(curNode.content, curNode.parent.content))
curNode.parent.right = null;
else
curNode.parent.left = null;
}
curNode = null;
isEnd = true;
}
else if(curNode.left != null && curNode.right != null) // 存在左右节点
{
TreeNode<T> replaceNode;
if (repleaseLeft) // 找到左节点最大的节点
{
replaceNode = curNode.left;
while (replaceNode.right != null)
{
replaceNode = replaceNode.right;
}
}
else // 找到右节点最小的节点
{
replaceNode = curNode.right;
while (replaceNode.left != null)
{
replaceNode = replaceNode.left;
}
}
curNode.content = replaceNode.content;
curNode = replaceNode;
}
else // 只存在一个子节点
{
TreeNode<T> tmpNode;
if (curNode.left != null)
tmpNode = curNode.left;
else
tmpNode = curNode.right;
tmpNode.parent = curNode.parent;
curNode = null;
if (_compare(tmpNode.content, tmpNode.parent.content))
tmpNode.parent.right = tmpNode;
else
tmpNode.parent.left = tmpNode;
refreshLayout(tmpNode);
isEnd = true;
}
}
}
/// <summary>
/// 刷新从当前结点开始的层级
/// </summary>
/// <param name="rootNode"></param>
private void refreshLayout(TreeNode<T> rootNode)
{
Queue<TreeNode<T>> queue = new Queue<TreeNode<T>>();
queue.Enqueue(rootNode);
while (queue.Count != 0)
{
TreeNode<T> node = queue.Dequeue();
if (node.parent != null) node.layout = node.parent.layout + 1;
else node.layout = 0;
if (node.left != null) queue.Enqueue(node.left);
if (node.right != null) queue.Enqueue(node.right);
}
}
/// <summary>
/// 打印树
/// </summary>
public void PrintOut()
{
Console.WriteLine("---------------------- 打印树 ---------------------");
if (_treeNode == null)
{
Console.WriteLine("树为空");
return;
}
Queue<TreeNode<T>> queue = new Queue<TreeNode<T>>();
queue.Enqueue(_treeNode);
int curLayout = _treeNode.layout;
while (queue.Count != 0)
{
TreeNode<T> node = queue.Dequeue();
if (curLayout < node.layout)
{
curLayout = node.layout;
Console.Write("\n");
}
Console.Write(node.content + " ");
if (node.left != null)
{
queue.Enqueue(node.left);
}
if (node.right != null)
{
queue.Enqueue(node.right);
}
}
Console.WriteLine("\n----------------------打印结束---------------------\n");
}
}
main函数代码.cs
public class T_Main
{
//-----------------------------------------------------------------------------------------------//
// 二叉搜索树 BST (Binary Search Tree)
/*
* 性质:
* 可以为空
* 非空情况下:
* 非空左子树的所有键值小于其根节点的键值
* 非空右子树的所有键值大于其根节点的键值
* 左、右子树都是二叉搜索树
* 中序遍历,必定得到一个有序序列
*/
// 缺点:根节点如果没有选取好,那么这颗二叉搜索树会“退化”,变成一根链条
/// <summary>
/// 起始函数
/// </summary>
public static void Main(string[] args)
{
// 构造数据
int[] nums = new[] {15, 10, 19, 8, 13, 16, 28, 5, 9, 12, 14, 20, 30, 25};
// 创建存储 int 类型的二叉搜索树
TreeControll<int> treeControll = new TreeControll<int>(nums,
delegate(int i, int i1)
{
return i == i1;
},
delegate(int i, int i1)
{
return i > i1;
});
// 打印树结构
treeControll.PrintOut();
// 查找
treeControll.Find(15);
treeControll.Find(25);
treeControll.Find(13);
treeControll.Find(-1);
// 删除
treeControll.Remove(10);
treeControll.PrintOut();
}
}