AVL树(高度平衡的二叉搜索树)
AVL树全称是平衡二叉搜索树,相比于红黑树,它是一种高度平衡的二叉搜索树,所有节点的左右子树高度差不超过1。
完整代码在最下面
树结点
/**
* parent 父节点
* left 左子树
* right 右子树
* height 高度,叶子结点高度为1
*/
class TreeNode {
int key; // 结点值、键值
TreeNode parent;
TreeNode left;
TreeNode right;
int height;
// int size; 树的大小,若有此属性,可以在o(logN)时间内找到排第k的元素
}
初始化
1.可以初始化一棵空树;2.也可以用一个已经排好序的数组来初始化一棵树(leetCode108题)
/**
* nums 已排好序的数组
*/
public TreeNode bulidTree(int[] nums) {
return recursive(nums, 0, nums.length);
}
// 取[left, right)区间的中点mid作为根结点
// [left,mid)构建左子树,[mid+1, right)构建右子树
private TreeNode recursive(int[] nums, int left, int right) {
if (left == right) {
return null;
}
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = recursive(nums, left, mid);
root.right = recursive(nums, mid + 1, right);
return root;
}
查找
一个具有 N N N 个节点的AVL树,其高度是 log N + 1 \log N+1 logN+1,因此二分查找的复杂度是 o ( log N ) o(\log N) o(logN) 。这里举5种查找的应用场景:
- 查找确定值
- 查找确定值的下确界、上确界
- 查找给定结点的前驱(precursor)、后继(successor)结点
- 查找新结点的插入位置
- 在root中查找排第k的元素(需要用到size属性,若结点的定义中无size属性,则复杂度就是 o ( N ) o(N) o(N) )
查找确定值:给定一个待查找的值 key
,在根节点 root
中的二分查找的流程如下:
若根结点值偏大,则向左走,偏大则向右走,相等就返回;没有找到就返回null
public TreeNode getNode(int key) {
TreeNode node = root;
while (node != null) {
int cmp = node.key - key;
if (cmp > 0) {
// node偏大,要缩小
node = node.left;
} else if (cmp < 0) {
// node偏小,要变大
node = node.right;
} else {
// 相等就是找到了
return node;
}
}
// 没有找到
return null;
}
查找给定结点的前驱、后继,实际上就是找出中序遍历的前驱后继。以查找 node
结点的后继为例,若 node.right
不为空,则后继结点为右子树的最左侧结点;若 node.right
为空,则后继结点在上面,向上找,第一个具有左子树的祖先就是后继结点,可以用下图的“对称性”来辅助记忆,会发现后继结点的两种情况是轴对称的。
// 前驱
private TreeNode precursor(TreeNode node) {
if (node == null) {
return null;
} else if (node.left != null) {
// 左子节点不为空,前驱在左子树的最右侧节点上
TreeNode p = node.left;
while (p.right != null) {
p = p.right;
}
return p;
} else {
// 左子节点为空,前驱在上面
TreeNode p = node.parent;
TreeNode ch = node;
while (p != null && p.left == ch) {
ch = p;
p = p.parent;
}
// p可能为null
return p;
}
}
// 后继
private TreeNode successor(TreeNode node) {
if (node == null) {
return null;
} else if (node.right != null) {
TreeNode p = node.right;
while (p != null) {
p = p.left;
}
return p;
} else {
// 后继在上面
TreeNode p = node.parent;
TreeNode ch = p;
while (p != null && p.right == ch) {
ch = p;
p = p.parent;
}
return p;
}
}
查找下确界、上确界:给定一个key值,查找它的系确界
public TreeNode floorEntry(int key) {
TreeNode node = root;
while (node != null) {
int cmp = node.key - key;
if (cmp < 0) {
// node.key比key小,说明node就是一个下界
if (node.right != null)
node = node.right;
else
return node;
} else if (cmp > 0) {
if (node.left != null) {
node = node.left;
} else {
// 向上找到,第一个右转的结点就是下确界
TreeNode p = node.parent;
TreeNode ch = node;
while (p != null && p.left == ch) {
ch = p;
p = p.parent;
}
return p;
}
} else {
return node;
}
}
return null;
}
查找排第k的结点(k从0开始计数):查找排第k的结点,TreeNode的定义中需要多维护一个变量size,表示树的结点个数。
// 调用方法前,k保证满足:0 <= k < root.size
public TreeNode getKthEntry(int k) {
TreeNode node = root;
while (node != null) {
int rank = getRank(node);
if (rank > k) {
node = node.left;
} else if