AVL Map & Set
由 AVL 实现的 Map 映射(或称字典)和 Set 集合
Map的实现
Map 的实现有多种方式,最简单的就是BST, 还有 AVL, 红黑树,后面章节将会详解。
为了增强代码的复用性和解耦,我们不从头实现一个 Map。而是定义一个 Map 的接口, BST, AVL , 红黑树去实现这个接口即可。而事实上,通过我们上一节所学的 AVL 来实现一个 Map 是非常简单的。
public interface IMap<K,V> {
void add(K key, V value);
V remove(K key);
boolean contains(K key);
V get(K key);
void set(K key, V newValue);
int getSize();
boolean isEmpty();
}
随后定义我们的 AVLMap:
public class AVLMap<K extends Comparable<K>, V> implements IMap<K,V> {
private AVLTree<K,V> avl;
public AVLMap(){
avl=new AVLTree<>();
}
@Override
public void add(K key, V value) {
avl.add(key, value);
}
@Override
public V remove(K key) {
return avl.remove(key);
}
@Override
public boolean contains(K key) {
return avl.contains(key);
}
@Override
public V get(K key) {
return avl.get(key);
}
@Override
public void set(K key, V newValue) {
avl.set(key,newValue);
}
@Override
public int getSize() {
return avl.getSize();
}
@Override
public boolean isEmpty() {
return avl.isEmpty();
}
}
可以看出,所有AVLMap的操作, 都可以直接调用 BST 的方法进行实现。
测试一下我们的AVLMap
定义一个AVLMapTest,我们利用一个文件处理类FileOperation读取一本英文书,然后将里面的英文单词放入BSTMap里面,若单词存在,则value+1,否则新建一个节点 key 为单词,value为1。最后统计全过程用时。我们特意增加了一个对比,拿BST和AVL做对比。
public class AVLMapTest {
public static void main(String[] args) {
ArrayList<String> words =new ArrayList<>();
FileOperation.readFile("./pride-and-prejudice.txt",words );
System.out.println("Total words : "+ words .size());
//测试极端情况,BSTMap退化成链表,AVLMap依然稳定
//Collections.sort(words);
AVLMap<String,Integer> avlMap = new AVLMap<>();
BSTMap<String,Integer> bstMap= new BSTMap<>();
//测试AVL性能
long startTime=System.nanoTime();
for (String word :words){
if (avlMap.contains(word))
avlMap.set(word, avlMap.get(word)+1);
else
avlMap.add(word,1);
}
for (String word :words){
avlMap.remove(word);
}
long endTime=System.nanoTime();
double time=(endTime-startTime)/1000000000.0;
System.out.println("AVL Map time : "+ time+" s.");
//测试BST 性能
startTime=System.nanoTime();
for (String word :words){
if (bstMap.contains(word))
bstMap.set(word, bstMap.get(word)+1);
else
bstMap.add(word,1);
}
endTime=System.nanoTime();
time=(endTime-startTime)/1000000000.0;
System.out.println("BST Map time : "+ time+" s.");
}
}
输出结果:
Total words : 122758
AVL Map time : 0.1474229 s.
BST Map time : 0.1185719 s.
是不是很意外,我们费了这么大的力气进行维护平衡,到头来竟然性能不如BST。这是为什么呢?
这是因为AVL维护平衡的过程需要一定的性能开销。对于完全随机的数据,BST性能已经足够好了,AVL额外的平衡维护是一种负担。
那我们还要AVL做什么呢?
AVL的优势在于时时刻刻维护树的平衡,尤其是对于完全有序或是近似有序的数据,AVL的时间复杂度都趋于稳定,而不像BST。对于完全有序的数据, 可以想象BST 的树型结构会退化成一个链表结构,导致时间复杂度越级成O(N)。另外AVL在查询的时候性能非常稳定,因为整一棵树足够的平衡。
我们可以将上面的这一句代码注释掉
Collections.sort(words);
这句代码会对数据进行排序,随后加入BST, AVL的数据都是完全有序的了,我们再来观察运行结果。
Total words : 122758
AVL Map time : 0.1021732 s.
BST Map time : 12.6293168 s.
我们可以看到BST的时间增加了100多倍,而AVL时间依然稳定,几乎不变。这就是AVL的优势!
AVLSet
大家可以参考BSTSet的写法和AVLMap的对比测试,自己来写这一部分代码。代码非常的简单,我这里就不演示了。也可以访问我的 github代码仓库。大家加油!
Last updated
Was this helpful?