• 平衡二叉树

    平衡二叉树

    在前面几节里,我们学会了怎样构建一棵非平衡二叉树。但不幸的是非平衡二叉树可能会变成一个列表,这样对树的插入和删除操作就是非随机的了。

    一个更好的方法是保持树在任何情况下都是平衡的。

    Adelsom-Velskii和Landis [?](在[?]中描述)使用一个简单的标准来衡量平衡这个概念:如果一棵树的每个结点的两个子树高度之差不超过1,我们就说这棵树是平衡的。具有这种特性的树常常被称作AVL树。平衡二叉树能够在O(logN)的时间规模里完成查找、插入和删除操作,N是树中结点的个数。

    假设我们用元组{Key,Value,Height,Smaller,Bigger}表示一棵 AVL树,用{,,0,,}表示一棵空树。然后在树中的查找操作就很容易实现了:

    1. lookup(Key, {nil,nil,0,nil,nil}) ->
    2. not_found;
    3.  
    4. lookup(Key, {Key,Value,_,_,_}) ->
    5. {found,Value};
    6.  
    7. lookup(Key, {Key1,_,_,Smaller,Bigger}) when Key < Key1 ->
    8. lookup(Key,Smaller);
    9.  
    10. lookup(Key, {Key1,_,_,Smaller,Bigger}) when Key > Key1 ->
    11. lookup(Key,Bigger).

    lookup的代码和非平衡二叉树中的基本一样。插入操作这样实现:

    1. insert(Key, Value, {nil,nil,0,nil,nil}) ->
    2. E = empty_tree(),
    3. {Key,Value,1,E,E};
    4.  
    5. insert(Key, Value, {K2,V2,H2,S2,B2}) when Key == K2 ->
    6. {Key,Value,H2,S2,B2};
    7.  
    8. insert(Key, Value, {K2,V2,_,S2,B2}) when Key < K2 ->
    9. {K4,V4,_,S4,B4} = insert(Key, Value, S2),
    10. combine(S4, K4, V4, B4, K2, V2, B2);
    11.  
    12. insert(Key, Value, {K2,V2,_,S2,B2}) when Key > K2 ->
    13. {K4,V4,_,S4,B4} = insert(Key, Value, B2),
    14. combine(S2, K2, V2, S4, K4, V4, B4).
    15.  
    16. empty_tree() ->
    17. {nil,nil,0,nil,nil}.

    思路是找到要插入的项将被插入到什么地方,如果插入使得树变得不平衡了,那么就平衡它。平衡一棵树的操作通过combine函数实现[4]。

    1. combine({K1,V1,H1,S1,B1},AK,AV,
    2. {K2,V2,H2,S2,B2},BK,BV,
    3. {K3,V3,H3,S3,B3} ) when H2 > H1, H2 > H3 ->
    4. {K2,V2,H1 + 2,
    5. {AK,AV,H1 + 1,{K1,V1,H1,S1,B1},S2},
    6. {BK,BV,H3 + 1,B2,{K3,V3,H3,S3,B3}}
    7. };
    8. combine({K1,V1,H1,S1,B1},AK,AV,
    9. {K2,V2,H2,S2,B2},BK,BV,
    10. {K3,V3,H3,S3,B3} ) when H1 >= H2, H1 >= H3 ->
    11. HB = max_add_1(H2,H3),
    12. HA = max_add_1(H1,HB),
    13. {AK,AV,HA,
    14. {K1,V1,H1,S1,B1},
    15. {BK,BV,HB,{K2,V2,H2,S2,B2},{K3,V3,H3,S3,B3}}
    16. };
    17. combine({K1,V1,H1,S1,B1},AK,AV,
    18. {K2,V2,H2,S2,B2},BK,BV,
    19. {K3,V3,H3,S3,B3} ) when H3 >= H1, H3 >= H2 ->
    20. HA = max_add_1(H1,H2),
    21. HB = max_add_1(HA,H3),
    22. {BK,BV,HB ,
    23. {AK,AV,HA,{K1,V1,H1,S1,B1},{K2,V2,H2,S2,B2}},
    24. {K3,V3,H3,S3,B3}
    25. }.
    26.  
    27. max_add_1(X,Y) when X =< Y ->
    28. Y + 1;
    29. max_add_1(X,Y) when X > Y ->
    30. X + 1.

    打印一棵树也很简单:

    1. write_tree(T) ->
    2. write_tree(0, T).
    3.  
    4. write_tree(D, {nil,nil,0,nil,nil}) ->
    5. io:tab(D),
    6. io:format('nil', []);
    7.  
    8. write_tree(D, {Key,Value,_,Smaller,Bigger}) ->
    9. D1 = D + 4,
    10. write_tree(D1, Bigger),
    11. io:format('~n', []),
    12. io:tab(D),
    13. io:format('~w ===> ~w~n', [Key,Value]),
    14. write_tree(D1, Smaller).

    现在让我们来看看我们的劳动成果。假设我们在一棵AVL树中插入键为1,2,3,…,16的16个数据。结果如图4.3,它是一棵平衡的树了(跟上一节那棵变形的树比较一下)。

    最后是AVL树中的删除操作:

    1. delete(Key, {nil,nil,0,nil,nil}) ->
    2. {nil,nil,0,nil,nil};
    3.  
    4. delete(Key, {Key,_,1,{nil,nil,0,nil,nil},{nil,nil,0,nil,nil}}) ->
    5. {nil,nil,0,nil,nil};
    6.  
    7. delete(Key, {Key,_,_,Smaller,{nil,nil,0,nil,nil}}) ->
    8. Smaller;
    9.  
    10. delete(Key, {Key,_,_,{nil,nil,0,nil,nil},Bigger}) ->
    11. Bigger;
    12.  
    13. delete(Key, {Key1,_,_,Smaller,{K3,V3,_,S3,B3}}) when Key == Key1 ->
    14. {K2,V2,Smaller2} = deletesp(Smaller),
    15. combine(Smaller2, K2, V2, S3, K3, V3, B3);
    16.  
    17. delete(Key, {K1,V1,_,Smaller,{K3,V3,_,S3,B3}}) when Key < K1 ->
    18. Smaller2 = delete(Key, Smaller),
    19. combine(Smaller2, K1, V1, S3, K3, V3, B3);
    20.  
    21. delete(Key, {K1,V1,_,{K3,V3,_,S3,B3},Bigger}) when Key > K1 ->
    22. Bigger2 = delete(Key, Bigger),
    23. combine( S3, K3, V3, B3, K1, V1, Bigger2).

    图4.3 一棵平衡二叉树

    1. nil
    2. 16 ===> a
    3. nil
    4. 15 ===> a
    5. nil
    6. 14 ===> a
    7. nil
    8. 13 ===> a
    9. nil
    10. 12 ===> a
    11. nil
    12. 11 ===> a
    13. nil
    14. 10 ===> a
    15. nil
    16. 9 ===> a
    17. nil
    18. 8 ===> a
    19. nil
    20. 7 ===> a
    21. nil
    22. 6 ===> a
    23. nil
    24. 5 ===> a
    25. nil
    26. 4 ===> a
    27. nil
    28. 3 ===> a
    29. nil
    30. 2 ===> a
    31. nil
    32. 1 ===> a
    33. nil

    deletisp函数删除并返回树中最大的元素。

    1. deletesp({Key,Value,1,{nil,nil,0,nil,nil},{nil,nil,0,nil,nil}}) ->
    2. {Key,Value,{nil,nil,0,nil,nil}};
    3. deletesp({Key,Value,_,Smaller,{nil,nil,0,nil,nil}}) ->
    4. {Key,Value,Smaller};
    5. deletesp({K1,V1,2,{nil,nil,0,nil,nil},
    6. {K2,V2,1,{nil,nil,0,nil,nil},{nil,nil,0,nil,nil}}}) ->
    7. {K2,V2,
    8. {K1,V1,1,{nil,nil,0,nil,nil},{nil,nil,0,nil,nil}}
    9. };
    10.  
    11. deletesp({Key,Value,_,{K3,V3,_,S3,B3},Bigger}) ->
    12. {K2,V2,Bigger2} = deletesp(Bigger),
    13. {K2,V2,combine(S3, K3, V3, B3, Key, Value, Bigger2)}.

    脚注

    [1]encode/2和本章其它一些例子的代码调用了io模块中的函数。这个模块是一个提供给用户进行格式化输入输出的标准模块。它的详细特性将在第??章和附录??中描述。
    [2]只有一个作者是系领带的。
    [3]这在数据库管理系统的数据字典里面是不用怀疑的。
    [4]有关合并规则的详细描述可以在第[??]章找到。