In looking for a solution to this question, I ran across some old binary tree code by Daniel Lichtblau, reproduced below:
Clear[leftsubtree, rightsubtree, nodevalue, emptyTree, treeInsert]
leftsubtree[{left_, _, _}] := left
rightsubtree[{_, _, right_}] := right
nodevalue[{_, val_, _}] := val
emptyTree = {};
treeInsert[emptyTree, elem_] := {emptyTree, elem, emptyTree}
treeInsert[tree_, elem_] /; SameQ[nodevalue[tree], elem] := tree
treeInsert[tree_, elem_] /; OrderedQ[{nodevalue[tree], elem}] :=
{leftsubtree[tree],
nodevalue[tree], treeInsert[rightsubtree[tree], elem]}
treeInsert[tree_, elem_] := {treeInsert[leftsubtree[tree], elem],
nodevalue[tree], rightsubtree[tree]}
When mapped onto a list, treeInsert
gives you a sorted duplicate free list. For example,
tr = {};
Scan[(tr = treeInsert[tr, #]) &, RandomInteger[100, 5]];
Flatten@tr
(* {13, 28, 53, 59, 88} *)
On my machine, this takes ~2 s to process RandomInteger[10, 10^5]
, but this increases to nearly 20 s with RandomInteger[10, 10^6]
. There are likely other techniques to speed this up, but I am curious as to how memoization could be adapted to this problem. At issue, though, is that the tree
changes with each insertion, and so it cannot be used directly for memoization because the definition depends directly on that form. How would one do this?
Edit: as I discovered in my own testing, Fold
works much better than Scan
for creating a tree, as follows
tr = Fold[treeInsert, {}, RandomInteger[100, 5]];
Flatten@tr
(* {13, 28, 53, 59, 88} *)
Update: while my question, per se, was not answered directly, the answers themselves indicated that there were better ways to accomplish what I wanted. In the end, I chose the one I did for two reasons: speed and simplicity.
Min[list]-1
andMax[list]+1
, then useCompile
, then replace back. This way, you can keep it almost as fast as without infinities. $\endgroup$