🧧🧧🧧🧧🧧个人主页🎈🎈🎈🎈🎈
🧧🧧🧧🧧🧧数据结构专栏🎈🎈🎈🎈🎈
🧧🧧🧧🧧🧧【数据结构】常见的排序算法🎈🎈🎈🎈🎈
文章目录
- 1.前言
- 2.搜索树
- 2.1概念
- 2.2查询操作
- 2.3插入操作
- 2.4删除操作
- 2.5性能分析
1.前言
Map与Set的底层有两种结构实现,一种是搜索树(TreeMap与TreeSet),还有一个就是哈希表(HashMaP与HashSet),接下来我们先了解搜索树这个底层结构。
2.搜索树
2.1概念
二叉搜索树又称二叉排序树,它或者是一棵空树,搜索二叉树具备这样性质:
1)根结点的左边的结点都比根结点小
2)根结点的右边的结点都比根结点大
3)通过中序遍历来遍历这个二叉搜索树是一个升序的数据集合
搜索二叉树:
2.2查询操作
2.2.1 基本思路:
1.将要查询的值先于根结点比较,与根结点不相等,则判断该元素与根结点的大小。
2.根结点 > 该元素 则该元素在根结点的左边
3.根结点 < 该元素 则该元素在根结点的右边
4.遇到null则遍历结束
2.2.2绘图分析
2.2.3代码实现
/**
*寻找key元素
* @param key
* @return
*/
public boolean search(int key) {
TreeNode cur = root;
while(cur != null) {
//1.判断根结点
if(cur.val == key) {
return true;
} else if(cur.val > key) {
//2.根结点大于key
cur = cur.left;
} else {
//3.根结点小于key
cur = cur.right;
}
}
return false;
}
2.3插入操作
2.3.1基本思路:
1)插入的元素要满足中序遍历之后是一个升序的集合
2)插入的元素只能在搜索二叉树的叶子结点 进行插入
3) 通过一个指针来遍历要插入的位置的同时再让一个指针标记插入位置的父节点
遍历的过程:
1)root == null 直接插入
2)root != null 判断与根结点的大小关系
root.val > val 插入root左边
root.val < val 插入root右边
2.2.2绘图分析
2.2.3代码实现
/**
* 插入元素
*/
public void insert(int val) {
//1.root == null
if(root == null) {
root = new TreeNode(val);
return;
}
//2.root != null
TreeNode cur = root;
TreeNode parent = null;
while(cur != null) {
if(cur.val < val) {
//1.根结点小于key
parent = cur;
cur = cur.right;
} else {
//2.根结点大于key
parent = cur;
cur = cur.left;
}
}
//说明cur为空,元素插入parent结点的其中两侧
TreeNode node = new TreeNode(val);
if(parent.val < val) {
parent.right = node;
} else {
parent.left = node;
}
}
2.4删除操作
删除操作是最复杂也是最难的一个操作,我们以cur指针来标记删除结点,parent指针标记删除结点的父结点,我们对删除操作分为三种情况:
1.cur.left == null
2.cur.right == null
3.cur.left != null && cur.right != null
我们先画一棵搜索二叉树,这里我们删20这个结点为例子:
1.cur.left == null
2.cur.right == null
3.cur.left != null && cur.right != null
代码实现:
public boolean remove(int val) {
TreeNode cur = root;
TreeNode parent = null;
while(cur != null) {
if(cur.val < val) {
parent = cur;
cur = cur.right;
} else if(cur.val >val){
parent = cur;
cur = cur.left;
} else {
return removeNode(parent,cur);
}
}
return false;
}
private boolean removeNode(TreeNode parent,TreeNode cur) {
//1.cur.left == null
if(cur.left == null) {
if(cur == root) {
cur.right = root;
} else {
if(cur == parent.right) {
parent.right = cur.right;
}
if(cur == parent.left) {
parent.left = cur.right;
}
}
}
//2.cur.right == null
if(cur.right == null) {
if(cur == root) {
cur.left = root;
} else {
if(cur == parent.left) {
parent.left = cur.left;
}
if(cur == parent.right) {
parent.right = cur.left;
}
}
}
//3.cur.left !=null && cur.right != null
TreeNode child = cur.right;
TreeNode p = null;//p为child的父亲结点
while(child.left != null) {
p = child;
child = child.left;
}
//找到最小值,将最小值赋值给cur
cur.val = child.val;
//最小值的父亲结点的左节点是否等于最小值结点
if(p.left == child) {
p.left = child.right;
} else {
p.right = child.right;
}
return true;
}
2.5性能分析
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:以2为底的log(N)
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
关于Map与Set先分享到这,后面的内容更加精彩,请各位看官慢慢期待。