gb_sets.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 709 行 · 第 1/2 页
ERL
709 行
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Richard Carlsson.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id $%%%% =====================================================================%% Ordered Sets implemented as General Balanced Trees%%%% Copyright (C) 1999-2001 Richard Carlsson%%%% An implementation of ordered sets using Prof. Arne Andersson's%% General Balanced Trees. This can be much more efficient than using%% ordered lists, for larger sets, but depends on the application. See%% notes below for details.%% ---------------------------------------------------------------------%% Notes:%%%% The complexity on set operations is bounded by either O(|S|) or O(|T|%% * log(|S|)), where S is the largest given set, depending on which is%% fastest for any particular function call. For operating on sets of%% almost equal size, this implementation is about 3 times slower than%% using ordered-list sets directly. For sets of very different sizes,%% however, this solution can be arbitrarily much faster; in practical%% cases, often between 10 and 100 times. This implementation is%% particularly suited for ackumulating elements a few at a time,%% building up a large set (more than 100-200 elements), and repeatedly%% testing for membership in the current set.%%%% As with normal tree structures, lookup (membership testing),%% insertion and deletion have logarithmic complexity.%%%% Operations:%%%% - empty(): returns empty set.%%%% Alias: new(), for compatibility with `sets'.%%%% - is_empty(S): returns 'true' if S is an empty set, and 'false'%% otherwise.%%%% - size(S): returns the number of nodes in the set as an integer.%% Returns 0 (zero) if the set is empty.%%%% - singleton(X): returns a set containing only the element X.%%%% - is_member(X, S): returns `true' if element X is a member of set S,%% and `false' otherwise.%%%% Alias: is_element(), for compatibility with `sets'.%%%% - insert(X, S): inserts element X into set S; returns the new set.%% *Assumes that the element is not present in S.*%%%% - add(X, S): adds element X to set S; returns the new set. If X is%% already an element in S, nothing is changed.%%%% Alias: add_element(), for compatibility with `sets'.%%%% - delete(X, S): removes element X from set S; returns new set.%% Assumes that the element exists in the set.%%%% - delete_any(X, S): removes key X from set S if the key is present%% in the set, otherwise does nothing; returns new set.%%%% Alias: del_element(), for compatibility with `sets'.%%%% - balance(S): rebalances the tree representation of S. Note that this%% is rarely necessary, but may be motivated when a large number of%% elements have been deleted from the tree without further%% insertions. Rebalancing could then be forced in order to minimise%% lookup times, since deletion only does not rebalance the tree.%%%% - union(S1, S2): returns a new set that contains each element that is%% in either S1 or S2 or both, and no other elements.%%%% - union(Ss): returns a new set that contains each element that is in%% at least one of the sets in the list Ss, and no other elements.%%%% - intersection(S1, S2): returns a new set that contains each element%% that is in both S1 and S2, and no other elements.%%%% - intersection(Ss): returns a new set that contains each element that%% is in all of the sets in the list Ss, and no other elements.%%%% - difference(S1, S2): returns a new set that contains each element in%% S1 that is not also in S2, and no other elements.%%%% Alias: subtract(), for compatibility with `sets'.%%%% - is_subset(S1, S2): returns `true' if each element in S1 is also a%% member of S2, and `false' otherwise.%%%% - to_list(S): returns an ordered list of all elements in set S. The%% list never contains duplicates.%%%% - from_list(List): creates a set containing all elements in List,%% where List may be unordered and contain duplicates.%%%% - from_ordset(L): turns an ordered-set list L into a set. The list%% must not contain duplicates.%%%% - smallest(S): returns the smallest element in set S. Assumes that%% the set S is nonempty.%%%% - largest(S): returns the largest element in set S. Assumes that the%% set S is nonempty.%%%% - take_smallest(S): returns {X, S1}, where X is the smallest element%% in set S, and S1 is the set S with element X deleted. Assumes that%% the set S is nonempty.%%%% - take_largest(S): returns {X, S1}, where X is the largest element in%% set S, and S1 is the set S with element X deleted. Assumes that the%% set S is nonempty.%%%% - iterator(S): returns an iterator that can be used for traversing%% the entries of set S; see `next'. The implementation of this is%% very efficient; traversing the whole set using `next' is only%% slightly slower than getting the list of all elements using%% `to_list' and traversing that. The main advantage of the iterator%% approach is that it does not require the complete list of all%% elements to be built in memory at one time.%%%% - next(T): returns {X, T1} where X is the smallest element referred%% to by the iterator T, and T1 is the new iterator to be used for%% traversing the remaining elements, or the atom `none' if no%% elements remain.%%%% - filter(P, S): Filters set S using predicate function P. Included%% for compatibility with `sets'.%%%% - fold(F, A, S): Folds function F over set S with A as the initial%% ackumulator. Included for compatibility with `sets'.%%%% - is_set(S): returns 'true' if S appears to be a set, and 'false'%% otherwise. Not recommended; included for compatibility with `sets'.-module(gb_sets).-export([empty/0, is_empty/1, size/1, singleton/1, is_member/2, insert/2, add/2, delete/2, delete_any/2, balance/1, union/2, union/1, intersection/2, intersection/1, difference/2, is_subset/2, to_list/1, from_list/1, from_ordset/1, smallest/1, largest/1, take_smallest/1, take_largest/1, iterator/1, next/1, filter/2, fold/3, is_set/1]).%% `sets' compatibility aliases:-export([new/0, is_element/2, add_element/2, del_element/2, subtract/2]).%% GB-trees adapted from Sven-Olof Nystr鰉s implementation for%% representation of sets.%%%% Data structures:%% - {Size, Tree}, where `Tree' is composed of nodes of the form:%% - {Key, Smaller, Bigger}, and the "empty tree" node:%% - nil.%%%% No attempt is made to balance trees after deletions. Since deletions%% don't increase the height of a tree, this should be OK.%%%% Original balance condition h(T) <= ceil(c * log(|T|)) has been%% changed to the similar (but not quite equivalent) condition 2 ^ h(T)%% <= |T| ^ c. This should also be OK.%%%% Behaviour is logarithmic (as it should be).%% Some macros. -define(p, 2). % It seems that p = 2 is optimal for sorted keys-define(pow(A, _), A * A). % correct with exponent as defined above.-define(div2(X), X bsr 1). -define(mul2(X), X bsl 1).empty() -> {0, nil}.new() -> empty().is_empty({0, nil}) -> true;is_empty(_) -> false.size({Size, _}) -> Size.singleton(Key) -> {1, {Key, nil, nil}}.is_element(Key, S) -> is_member(Key, S).is_member(Key, {_, T}) -> is_member_1(Key, T).is_member_1(Key, {Key1, Smaller, _}) when Key < Key1 -> is_member_1(Key, Smaller);is_member_1(Key, {Key1, _, Bigger}) when Key > Key1 -> is_member_1(Key, Bigger);is_member_1(_, {_, _, _}) -> true;is_member_1(_, nil) -> false.insert(Key, {S, T}) -> S1 = S + 1, {S1, insert_1(Key, T, ?pow(S1, ?p))}.insert_1(Key, {Key1, Smaller, Bigger}, S) when Key < Key1 -> case insert_1(Key, Smaller, ?div2(S)) of {T1, H1, S1} when is_integer(H1) -> T = {Key1, T1, Bigger}, {H2, S2} = count(Bigger), H = ?mul2(max(H1, H2)), SS = S1 + S2 + 1, P = ?pow(SS, ?p), if H > P -> balance(T, SS); true -> {T, H, SS} end; T1 -> {Key1, T1, Bigger} end;insert_1(Key, {Key1, Smaller, Bigger}, S) when Key > Key1 -> case insert_1(Key, Bigger, ?div2(S)) of {T1, H1, S1} when is_integer(H1) -> T = {Key1, Smaller, T1}, {H2, S2} = count(Smaller), H = ?mul2(max(H1, H2)), SS = S1 + S2 + 1, P = ?pow(SS, ?p), if H > P -> balance(T, SS); true -> {T, H, SS} end; T1 -> {Key1, Smaller, T1} end;insert_1(Key, nil, 0) -> {{Key, nil, nil}, 1, 1};insert_1(Key, nil, _) -> {Key, nil, nil};insert_1(Key, _, _) -> erlang:fault({key_exists, Key}).count({_, nil, nil}) -> {1, 1};count({_, Sm, Bi}) -> {H1, S1} = count(Sm), {H2, S2} = count(Bi), {?mul2(max(H1, H2)), S1 + S2 + 1};count(nil) -> {1, 0}.max(X, Y) when X < Y -> Y;max(X, _Y) -> X.balance({S, T}) -> {S, balance(T, S)}.balance(T, S) -> balance_list(to_list_1(T), S).balance_list(L, S) -> {T, _} = balance_list_1(L, S), T.balance_list_1(L, S) when S > 1 -> Sm = S - 1, S2 = Sm div 2, S1 = Sm - S2, {T1, [K | L1]} = balance_list_1(L, S1), {T2, L2} = balance_list_1(L1, S2), T = {K, T1, T2}, {T, L2};balance_list_1([Key | L], 1) -> {{Key, nil, nil}, L};balance_list_1(L, 0) -> {nil, L}.add_element(X, S) -> add(X, S).add(X, S) -> case is_member(X, S) of true -> S; % we don't have to do anything here false -> insert(X, S) end.from_list(L) -> from_ordset(ordsets:from_list(L)).from_ordset(L) -> S = length(L), {S, balance_list(L, S)}.del_element(Key, S) -> delete_any(Key, S).delete_any(Key, S) -> case is_member(Key, S) of true -> delete(Key, S); false -> S end.delete(Key, {S, T}) -> {S - 1, delete_1(Key, T)}.delete_1(Key, {Key1, Smaller, Larger}) when Key < Key1 -> Smaller1 = delete_1(Key, Smaller), {Key1, Smaller1, Larger};delete_1(Key, {Key1, Smaller, Bigger}) when Key > Key1 -> Bigger1 = delete_1(Key, Bigger), {Key1, Smaller, Bigger1};delete_1(_, {_, Smaller, Larger}) -> merge(Smaller, Larger).merge(Smaller, nil) -> Smaller;merge(nil, Larger) -> Larger;merge(Smaller, Larger) -> {Key, Larger1} = take_smallest1(Larger), {Key, Smaller, Larger1}.take_smallest({S, T}) -> {Key, Larger} = take_smallest1(T),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?