红黑树是一种自平衡二叉查找树,在插入和删除操作时通过特定操作(左旋、右旋)保持二叉查找树的平衡,从而获得较高的查找性能。
红黑树是一种高效查找的树,通过左旋右旋调整树的平衡,它的本质也是对概念模型:2-3-4树的一种实现。
2-3-4树在计算机科学中是阶为 4 的B树(Balance Tree),B树也叫平衡多路查找树。它最重要的特性在于平衡,这使得我们能够在最坏情况下也保持O(LogN)的时间复杂度实现查找。
先看B树中的2-3树或2-3-4树的2节点结构:
再看B树中2-3树或2-3-4树的3节点结构:
再看B树中2-3-4树的4节点结构:
这里就会有个问题了,红黑树是从平衡树转换得到的,那么红黑树的染色和旋转的意义是什么?
红黑树插入删除操作如何调整可以在可视化网页上建一个红黑树:Red/Black Tree Visualization
下面通过一个例子去使用具有红黑树结构的TreeMap,一般不建议自定义的红黑树结构,避免留坑,下面的case记录经过坐标轴点的次数,
package com.hust.zhang.leetcode.tree;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.platform.commons.util.StringUtils;
import java.util.Map;
import java.util.TreeMap;
public class DemoTest {
private static Coordinate LOCATION = new Coordinate(0, 0);
/**
* 记录坐标点经过的次数,按距离坐标原点最近的坐标点进行排序
*/
private static Map treeMap = new TreeMap<>(
(o1, o2) -> (int) (Math.pow(o1.axis_x, 2) + Math.pow(o1.axis_y, 2) -
Math.pow(o2.axis_x, 2) - Math.pow(o2.axis_y, 2)));
/**
* 起始坐标点
*/
static {
treeMap.put(new Coordinate(LOCATION.axis_x, LOCATION.axis_y), 1);
}
@Data
@AllArgsConstructor
private static class Coordinate {
private int axis_x;
private int axis_y;
}
public static void main(String[] args) {
String inputStr = "NESW";
updateCoordinate(inputStr);
treeMap.entrySet().forEach(System.out::println);
}
/**
* 更新坐标轴记录
*
* @param inputStr 移动动作指令
* 'N' 代表NORTH—北
* 'S' 代表SOUTH-南
* 'W' 代表WEST——西
* 'E' 代表EAST——东
*/
private static void updateCoordinate(String inputStr) {
if (StringUtils.isBlank(inputStr)) {
return;
}
for (int i = 0; i < inputStr.length(); i++) {
if (inputStr.charAt(i) == 'N') {
addLocationToMap(new Coordinate(LOCATION.axis_x, LOCATION.axis_y + 1));
LOCATION.axis_y += 1;
} else if (inputStr.charAt(i) == 'S') {
addLocationToMap(new Coordinate(LOCATION.axis_x, LOCATION.axis_y - 1));
LOCATION.axis_y -= 1;
} else if (inputStr.charAt(i) == 'W') {
addLocationToMap(new Coordinate(LOCATION.axis_x - 1, LOCATION.axis_y));
LOCATION.axis_x -= 1;
} else if (inputStr.charAt(i) == 'E') {
addLocationToMap(new Coordinate(LOCATION.axis_x + 1, LOCATION.axis_y));
LOCATION.axis_x += 1;
}
}
}
private static void addLocationToMap(Coordinate coordinate) {
boolean flag = true;
for (Map.Entry entry : treeMap.entrySet()) {
Coordinate location = entry.getKey();
if (location.axis_x == coordinate.axis_x && location.axis_y == coordinate.axis_y) {
flag = false;
treeMap.put(location, entry.getValue() + 1);
}
}
if (flag) {
treeMap.put(coordinate, 1);
}
}
}
在计算机科学中,AVL树是最先发明的自平衡二叉查找树。
windows对进程地址空间进行管理,参看文末链接3。
树的左右高度之差保证在「-1,0,1」,基于平衡性,查找的时间复杂度是O(logn)。树的平衡也是通过左旋和右旋进行维持。
插入删除时可能经过一次或多次左旋右旋,慢动作看数据结构可视化可以看到插入某一元素后,先调整高度,然后可能经过两次左旋。
下面直接看左旋右旋如何实现,
package com.hust.zhang.leetcode.tree;
/**
* Self-Balancing Binary Search Tree,平衡二叉搜索树
*
* 左右两个子树的高度差的绝对值不超过1
*/
public class AVLTree {
private AVLTree(int val) {
root = new TreeNode(val);
}
private TreeNode root;
/**
* 二叉树节点定义
*/
private static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
this.val = x;
}
}
/**
* 左旋操作
* @param root 根节点
* @return 一次左旋后的根节点
*
* A C
* / \ / \
* B C ——————> A E
* / \ / \
* D E B D
*/
private TreeNode leftRotation(TreeNode root) {
TreeNode C = root.right;
TreeNode D = C.left;
// 左旋
C.left = root;
root.right = D;
return C;
}
/**
* 右旋操作
* @param root 根节点
* @return 一次右旋后的根节点
*
* A B
* / \ / \
* B C ——————> D A
* / \ / \
* D E E C
*
*/
private TreeNode rightRotation(TreeNode root) {
TreeNode B = root.left;
TreeNode E = B.right;
// 右旋
B.right = root;
root.left = E;
return B;
}
/**
* 获取树的高度
*
* @param node 当前根节点
* @return 树高
*/
private int height(TreeNode node) {
if (node == null) {
return 0;
}
return Math.max(height(node.left), height(node.right)) + 1;
}
}
从上面可以了解到红黑树和AVL树都是平衡二叉树,具体的区别如下,
平衡二叉树类型 | 平衡度 | 调整频率 | 适用场景 |
AVL树 | 高 | 高 | 查询多,增/删少 |
红黑树 | 低 | 低 | 增/删频繁 |
参考链接:
1、CFS调度算法:http://c.biancheng.net/view/1255.html
2、数据结构可视化:Data Structure Visualization
3、Windows运用AVL树对进程地址空间的管理 - 开发者知识库
4、红黑树和AVL树的区别(转) - 奋斗终生 - 博客园