October 22, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to KlausO Attachments: | KlausO wrote:
>
> Seems to be an interesting task to get used to the new foreach features. I'll give it a try.
>
> Klaus
Ok, I rewrote the intrusive version to a nonintrusive one. It has almost the interface you mentioned.
Please note: IMHO, there are some points where the template needs some refinement.
1. A third template parameter "bool duplicatesAllowed" should be
added and handled in insert/remove via "static if". Then you
have multisets at hand !
2. Sometimes it exposes an interface to the internally used nodes
to be able to access the key/value pairs. A special iterator
class which could encapsulate the "not found/not inserted"
state would be better.
3. The insert via opIndexAssign/find via opIndex is IMHO not
optimal because you could not return the "not found/not inserted"
state. There are some possibilities to resolve this:
a) Throw an exception (IMHO, ugly)
b) Return an empty node
c) ... any ideas ?
Using insert like
if (foo.insert("Jill", 13))
looks better to me.
Ok, thats it for now. Maybe I find some spare time next week
to adress some of the issues.
Greets
Klaus
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to KlausO | KlausO wrote:
> KlausO wrote:
>>
>> Seems to be an interesting task to get used to the new foreach
>> features. I'll give it a try.
>>
>> Klaus
>
> Ok, I rewrote the intrusive version to a nonintrusive one.
> It has almost the interface you mentioned.
>
> Please note: IMHO, there are some points where the template
> needs some refinement.
>
> 1. A third template parameter "bool duplicatesAllowed" should be
> added and handled in insert/remove via "static if". Then you
> have multisets at hand !
>
> 2. Sometimes it exposes an interface to the internally used nodes
> to be able to access the key/value pairs. A special iterator
> class which could encapsulate the "not found/not inserted"
> state would be better.
>
> 3. The insert via opIndexAssign/find via opIndex is IMHO not
> optimal because you could not return the "not found/not inserted"
> state. There are some possibilities to resolve this:
>
> a) Throw an exception (IMHO, ugly)
> b) Return an empty node
> c) ... any ideas ?
>
> Using insert like
> if (foo.insert("Jill", 13))
> looks better to me.
>
> Ok, thats it for now. Maybe I find some spare time next week
> to adress some of the issues.
> Greets
>
> Klaus
>
>
> ------------------------------------------------------------------------
>
> //////////////////////////////////////////////////////////////////////////
> /**
>
> Autors: Klaus Oberhofer
>
> License: use it for your own purpose
>
> */////////////////////////////////////////////////////////////////////////
> module redblacktree;
>
> enum NodeColor
> {
> RedNode = 0,
> BlackNode = 1
> };
>
> //////////////////////////////////////////////////////////////////////////
> /**
>
> Red/black tree implementation.
>
> */////////////////////////////////////////////////////////////////////////
> class RedBlackTree(KeyType, ValueType) {
> public:
> /// TreeNode type used to form red/black tree
> static class TreeNode
> {
> package:
> TreeNode left;
> TreeNode right;
> NodeColor color;
> KeyType key;
> ValueType value;
>
> package:
> this(KeyType key, ValueType value)
> {
> this.key = key;
> this.value = value;
>
> left = right = null;
> color = NodeColor.RedNode;
> }
> public:
> KeyType nkey() { return this.key; } ValueType nvalue() { return this.value; } }
> private:
> // The PtrPack struct is used to to save function-calling overhead
> struct PtrPack
> {
> TreeNode t; // The current node
> TreeNode p; // Parent TreeNode g; // Grandparent TreeNode gg; // Great Grandparent
> TreeNode s; // Sibling TreeNode rs; // Right children of s
> TreeNode ls; // Left children of s
> TreeNode m; // Matching node TreeNode pm; // Parent of matching node
> };
>
> // the tree root TreeNode root;
>
>
> // Helper functions
> static TreeNode InsBalance (TreeNode tree, PtrPack pp)
> {
> TreeNode cofgg; // New child of great-grandparent
> int side;
>
> side = pp.gg && pp.gg.right is pp.g;
>
> if (pp.g.left is pp.p) {
> if (pp.p.right is pp.t) { // Do double rotate
> pp.g.left = pp.t.right;
> pp.t.right = pp.g;
> pp.p.right = pp.t.left;
> pp.t.left = pp.p;
> pp.p = pp.gg;
> cofgg = pp.t;
> }
> else { // Do single rotate right
> pp.g.left = pp.p.right;
> pp.p.right = pp.g;
> cofgg = pp.p;
> }
> }
> else { // pp.g.right is pp.p
> if (pp.p.left is pp.t) { // Do double rotate
> pp.g.right = pp.t.left;
> pp.t.left = pp.g;
> pp.p.left = pp.t.right;
> pp.t.right = pp.p;
> pp.p = pp.gg;
> cofgg = pp.t;
> }
> else { // Do single rotate left
> pp.g.right = pp.p.left;
> pp.p.left = pp.g;
> cofgg = pp.p;
> }
> }
> cofgg.color = NodeColor.BlackNode;
> pp.g.color = NodeColor.RedNode;
>
> if (pp.gg) {
> if (side) pp.gg.right = cofgg; else pp.gg.left = cofgg;
> }
> else tree = cofgg;
>
> return tree;
> }
>
> static TreeNode DoReplacement(TreeNode tree, PtrPack pp)
> {
> // At this point, pp.m is the node to delete, pp.pm is it's parent,
> // pp.p is the replacement node, pp.g is it's parent. If pp.m has no
> // successor, pp.p will = pp.m, and replacement is the non-null
> // child of pp.m.
>
> if (pp.m) { // Matching node was found
> if (pp.p is pp.m || pp.m.left is null || pp.m.right is null) {
> // No successor, and/or at least one child null
> // Get non-null child, if any, else pp.p will be null
> pp.p = (pp.m.left) ? pp.m.left : pp.m.right;
> }
> else { // pp.m has a successor to use as a replacement
> if (pp.g !is pp.m) {
> // Successor is not immediate child of pp.m, so detach
> // from where it is, attach to where pp.m is
> pp.g.left = pp.p.right;
> pp.p.right = pp.m.right;
> }
> // Finish attaching where pp.m is.
> pp.p.left = pp.m.left;
> }
> // pp.p should have same color as pp.m since it's going where pp.m was
> if (pp.p) pp.p.color = pp.m.color;
> }
>
> // Fixup pp.pm child link to be pp.p
> if (pp.m) {
> if (pp.pm) {
> if (pp.pm.left is pp.m) pp.pm.left = pp.p; else pp.pm.right = pp.p;
> }
> else tree = pp.p; // New root, possibly null
> }
> return tree;
> }
>
> static TreeNode DelBalance (TreeNode tree, PtrPack pp)
> {
> if ((pp.t is null || pp.t.color == NodeColor.BlackNode) && pp.s !is null && pp.s.color == NodeColor.RedNode) {
> // Case: pp.t is black, pp.s red. pp.t might be null,
> // but pp.s and pp.p must not be.
>
> // Fix pp.g child to be pp.s. Also pp.s may become pp.p of pp.m
> if (pp.g) {
> if (pp.g.left is pp.p) pp.g.left = pp.s; else pp.g.right = pp.s;
> }
> else tree = pp.s;
>
> if (pp.p is pp.m) pp.pm = pp.s;
>
> // Finish rotating
> if (pp.p.left is pp.t) {
> // RotateLeft(pp.p);
> pp.p.right = pp.ls;
> pp.s.left = pp.p;
> pp.g = pp.s;
> pp.s = pp.ls;
> }
> else {
> // RotateRight(pp.p);
> pp.p.left = pp.rs;
> pp.s.right = pp.p;
> pp.g = pp.s;
> pp.s = pp.rs;
> }
>
> // Fixup children of pp.s
> if (pp.s) { pp.ls = pp.s.left; pp.rs = pp.s.right; }
>
> // Fixup colors
> pp.p.color = NodeColor.RedNode;
> pp.g.color = NodeColor.BlackNode;
> }
>
> if (pp.t && pp.t.color == NodeColor.BlackNode &&
> (pp.t.left is null || pp.t.left.color == NodeColor.BlackNode) &&
> (pp.t.right is null || pp.t.right.color == NodeColor.BlackNode)) {
> // Case: pp.t is a single binary node with two black children.
> if (pp.s && pp.s.color == NodeColor.BlackNode &&
> (pp.s.left is null || pp.s.left.color == NodeColor.BlackNode) &&
> (pp.s.right is null || pp.s.right.color == NodeColor.BlackNode)) {
>
> // Case: pp.t and pp.s are single binary nodes with two black children..
> pp.p.color = NodeColor.BlackNode; pp.t.color = NodeColor.RedNode; pp.s.color = NodeColor.RedNode;
>
> }
> else if (pp.ls && pp.ls.color == NodeColor.RedNode) {
>
> // Case: Tree is a single binary node with two black children.
> // pp.s is pair of binary nodes-one red, one black or pp.s is a set
> // of three binary nodes with a black parent and red child nodes.
> // pp.ls is red or pp.rs might be red.
>
> if (pp.p.left is pp.t) {
> // Fix colors
> pp.ls.color = pp.p.color; pp.p.color = NodeColor.BlackNode; pp.t.color = NodeColor.RedNode;
> // Fix pp.g child to be pp.ls. ALSo pp.ls may become pp.p of pp.m
> if (pp.g) {
> if (pp.g.left is pp.p) pp.g.left = pp.ls; else pp.g.right = pp.ls;
> }
> else tree = pp.ls;
> if (pp.p is pp.m) pp.pm = pp.ls;
>
> // Finish: DoubleRotateLeft(pp.s, pp.p);
> pp.p.right = pp.ls.left;
> pp.ls.left = pp.p;
> pp.s.left = pp.ls.right;
> pp.ls.right= pp.s;
> pp.g = pp.ls;
> // Do not fix pp.s or pp.ls since they get reassigned
> // at the top of next loop
> }
> else {
> // Fix colors
> pp.s.color = pp.p.color; pp.ls.color = NodeColor.BlackNode;
> pp.p.color = NodeColor.BlackNode; pp.t.color = NodeColor.RedNode;
> // Fix pp.g child to be pp.s. Also pp.s may become pp.p of pp.m
> if (pp.g) {
> if (pp.g.left is pp.p) pp.g.left = pp.s; else pp.g.right = pp.s;
> }
> else tree = pp.s;
> if (pp.p is pp.m) pp.pm = pp.s;
> // Finish: RotateRight(pp.p);
> pp.p.left = pp.rs;
> pp.s.right = pp.p;
> pp.g = pp.s;
> // Do not fix pp.s or pp.ls since they get reassigned
> // at the top of next loop
> }
> }
> else if (pp.rs && pp.rs.color == NodeColor.RedNode) {
> // Case: pp.t is a single binary node with two black children.
> // pp.s is a pair of binary nodes-one red, one black.
> // pp.ls is black, pp.rs is red.
>
> if (pp.p.left is pp.t) {
> // Fix colors
> pp.rs.color = NodeColor.BlackNode; pp.s.color = pp.p.color;
> pp.p.color = NodeColor.BlackNode; pp.t.color = NodeColor.RedNode;
> // Fix pp.g child to be pp.s. Also, pp.s may become pp.p of pp.m
> if (pp.g) {
> if (pp.g.left is pp.p) pp.g.left = pp.s; else pp.g.right = pp.s;
> }
> else tree = pp.s;
> if (pp.p is pp.m) pp.pm = pp.s;
> // Finish: RotateLeft(pp.p);
> pp.p.right = pp.ls;
> pp.s.left = pp.p;
> pp.g = pp.s;
> // Do not fix pp.s or pp.ls since they get reassigned
> // at the top of next loop
> }
> else {
> // Fix colors
> pp.rs.color = pp.p.color; pp.p.color = NodeColor.BlackNode; pp.t.color = NodeColor.RedNode;
> // Fix pp.g child to become pp.rs. ALSo, pp.rs may become pp.p of pp.m.
> if (pp.g) {
> if (pp.g.left is pp.p) pp.g.left = pp.rs; else pp.g.right = pp.rs;
> }
> else tree = pp.rs;
> if (pp.p is pp.m) pp.pm = pp.rs;
> // Finish: DoubleRotateRight(pp.s, pp.p);
> pp.p.left = pp.rs.right;
> pp.rs.right = pp.p;
> pp.s.right = pp.rs.left;
> pp.rs.left = pp.s;
> pp.g = pp.rs;
> // Do not fix pp.s or pp.ls since they get reassigned
> // at the top of next loop
> }
> }
> }
> return tree;
> }
>
> /// returns height of tree at node root
> int height(TreeNode tree) {
> int h = 0;
> if (tree !is null)
> {
> int lh = height(tree.left);
> int rh = height(tree.right);
> h = ((lh > rh) ? lh : rh) + 1;
> }
> return h;
> }
>
> /// returns number of nodes within tree
> int count (TreeNode tree) {
> int n = 0;
> if (tree !is null) {
> ++n;
> n += count(tree.left);
> n += count(tree.right); }
> return n; }
>
> /// get child node with "minimum" value
> TreeNode getMin(TreeNode tree)
> {
> if (tree !is null)
> {
> while(tree.right) tree = tree.right;
> }
> return tree;
> }
>
> /// get child node with "maximum" value
> TreeNode getMax (TreeNode tree)
> {
> if (tree !is null)
> {
> while(tree.left) tree = tree.left;
> }
> return tree;
> }
>
> /// insert child node into tree
> bool insert(TreeNode node)
> in
> {
> assert(node);
>
> // add only new nodes assert(node.left is null);
> assert(node.right is null);
> }
> body
> {
> bool bInserted = false;
>
> PtrPack pp;
> int side;
> pp.t = root; pp.p = null; pp.g = null; pp.gg = null;
>
> while ((pp.t !is null) && (node.key != pp.t.key)) {
> // If on a set of three binary nodes with a black
> // parent node and red child nodes, split it into
> // two single binary nodes with two black children.
> if (((pp.t.left !is null) && (pp.t.left.color == NodeColor.RedNode)) &&
> ((pp.t.right !is null) && (pp.t.right.color == NodeColor.RedNode))) {
> pp.t.color = NodeColor.RedNode;
> pp.t.left.color = NodeColor.BlackNode;
> pp.t.right.color = NodeColor.BlackNode;
>
> // Check for two reds in a row, and adjust if so
> if ((pp.p !is null) && (pp.p.color == NodeColor.RedNode))
> {
> root = InsBalance(root, pp);
> }
> }
> pp.gg = pp.g; pp.g = pp.p; pp.p = pp.t;
>
> // go to left if node < current node
> if (node.key < pp.t.key) {
> pp.t = pp.t.left; side = 0;
> }
> else {
> pp.t = pp.t.right; side = 1;
> }
> }
>
> // Reached the bottom, with no matching node
> if (pp.t is null) {
> bInserted = true;
>
> // now insert node
> //
> pp.t = node;
> pp.t.color = NodeColor.RedNode;
>
> if (pp.t is null) return 0; // Couldn't add
> if (pp.p) {
> if (side)
> {
> pp.p.right = pp.t; }
> else {
> pp.p.left = pp.t;
> }
> }
> else root = pp.t;
>
> // Check for two reds in a row, and adjust if so
> if (pp.p && pp.p.color == NodeColor.RedNode)
> {
> root = InsBalance(root, pp);
> }
> }
>
> // root always made black
> root.color = NodeColor.BlackNode; return bInserted;
> }
>
> // traverse tree in ascending order
> int applyForward(TreeNode tree, int delegate(inout TreeNode) dg)
> {
> int result = 0;
> while(tree) {
> result = applyForward(tree.left, dg);
> if (result) return result;
>
> result = dg(tree);
> if (result) return result;
> tree = tree.right;
> }
>
> return result;
> }
>
> // traverse tree in descending order
> int applyReverse(TreeNode tree, int delegate(inout TreeNode) dg)
> {
> int result = 0;
> while(tree) {
> result = applyReverse(tree.right, dg);
> if (result) return result;
>
> result = dg(tree);
> if (result) return result;
> tree = tree.left;
> }
>
> return result;
> }
>
> // public interface
> public:
> // insert value at position of key
> bool insert(KeyType key, ValueType value)
> { // create a new node
> TreeNode node = new TreeNode(key, value);
> return insert(node);
> }
>
> // support insertion via operator
> // e.g. persons["Mary"] = 30;
> ValueType opIndexAssign(ValueType value, KeyType key)
> { insert(key, value);
> return value;
> }
>
> // support find via operator
> // e.g. int age = persons["Mary"];
> ValueType opIndex(KeyType key)
> {
> TreeNode node = find(key);
> return node.value;
> }
>
> /// remove child node which matches key from the tree
> TreeNode remove(in KeyType compare)
> {
> PtrPack pp;
> if (root is null) return null;
> pp.t = root; pp.p = null; pp.g = null;
> pp.m = null; pp.pm = null;
> while (pp.t) {
> // Go down the tree one level, searching for node to delete
> if (pp.t.key == compare) {
> pp.m = pp.t; // Record matching node
> pp.pm = pp.p; // And it's parent
> }
> // Update ancestor pointers
> pp.g = pp.p; pp.p = pp.t;
> if (pp.t.key > compare) {
> pp.t = pp.p.left; pp.s = pp.p.right;
> }
> else { // Walk down even if it matches, to look for successor
> pp.t = pp.p.right; pp.s = pp.p.left;
> }
> if (pp.s) {
> pp.ls = pp.s.left; pp.rs = pp.s.right;
> }
> else {
> pp.ls = null; pp.rs = null;
> }
> root = DelBalance(root, pp);
> } root = DoReplacement(root, pp);
> if (root) root.color = NodeColor.BlackNode;
> // clear connection to tree
> if (pp.m)
> {
> pp.m.left = null;
> pp.m.right = null;
> }
>
> return pp.m; // TreeNode to delete
> }
>
> /// find child node which matches key
> TreeNode find(in KeyType compare)
> {
> TreeNode t = root;
> while (t) {
> if (t.key == compare) break;
> t = ((t.key > compare) ? t.left : t.right);
> }
> return t;
> }
>
> /// find child node which is "above" given key
> TreeNode findUpper(in KeyType compare)
> {
> TreeNode t = root;
> TreeNode erg = null; while (t) {
> if (t.key > compare)
> {
> // if (t > key) -> remember it
> // and descend left subtree
> erg = t;
> t = t.left;
> }
> else
> {
> // if (t < key) descend right subtree
> t = t.right;
> }
> }
> return erg;
> }
>
> /// find child node which is "below" given key
> TreeNode findLower (in KeyType compare)
> {
> TreeNode t = root;
> TreeNode erg = null; while (t) {
> if (t.key < compare)
> {
> // if (t < key) -> remember it
> // and descend right subtree
> erg = t;
> t = t.right;
> }
> else
> {
> // if (t >= key) descend left subtree
> t = t.left;
> }
> }
> return erg;
> }
>
> /// find child node which is above or equal to the given key
> TreeNode findUpperOrEq (in KeyType compare)
> {
> TreeNode t = root;
> TreeNode erg = null; while (t) {
> if (t.key < compare)
> {
> // if (t < key) descend right subtree
> t = t.right;
> }
> else
> {
> // if (t >= key) -> remember it
> // and descend left subtree
> erg = t;
> t = t.left;
> }
> }
> return erg;
> }
>
> /// find child node which is below or equal to the given key
> TreeNode findLowerOrEq (in KeyType compare)
> {
> TreeNode t = root;
> TreeNode erg = null; while (t) {
> if (t.key > compare)
> {
> // if t > key descend left subtree
> t = t.left;
> }
> else
> {
> // if (t <= key) -> remember it
> // and descend right subtree
> erg = t;
> t = t.right;
> }
> }
> return erg;
> }
>
> /// clear tree
> void clear() {
> root = null;
> }
>
> /// returns true when tree is empty
> bool isEmpty() {
> return (root is null);
> }
>
> /// determine height of tree
> int height() {
> return height(root);
> }
>
> /// return number of nodes maintained by tree
> int count() { return count(root);
> }
>
> /// return node with minimum key
> TreeNode getMin() { return getMin(root);
> }
>
> /// return node with maximum key
> TreeNode getMax() {
> return getMax(root);
> }
>
> int opApply(int delegate(inout TreeNode) dg) {
> return applyForward(root, dg);
> }
>
> int opApplyReverse(int delegate(inout TreeNode) dg) {
> return applyReverse(root, dg);
> }
>
> /// debug output; prints tree sideways
> void PrintSideWays()
> {
> _PrintSideWays(root, 0);
> }
>
> void _PrintSideWays (in TreeNode child, in int space)
> {
> while(child) {
> space += 2;
> _PrintSideWays(child.left, space);
> for (int idx=0; idx < space; ++idx) printf(" ");
> printf("%*s\n", child.key);
> child = child.right;
> }
> }
>
> }
>
> import std.stdio;
>
> void main()
> {
> RedBlackTree!(char[], int) foo = new RedBlackTree!(char[], int);
>
> foo["Sally"]= 11;
> foo["Joe"] = 12;
> foo["Jill"] = 13;
> foo["Mary"] = 14;
> foo["Jack"] = 15;
> foo["Carl"] = 16;
> foo["Nina"] = 17;
> foo["Tim"] = 18;
> foo.PrintSideWays();
>
> foreach(v; foo)
> {
> writefln(v.key, " ", v.value);
> }
>
> writefln(" ");
>
> foreach_reverse(v; foo)
> {
> writefln(v.key, " ", v.value);
> }
>
> foo.remove("Mary");
>
> writefln(" ");
> foreach(v; foo)
> {
> writefln(v.key, " ", v.value);
> }
>
> writefln(" ");
>
> writefln("Age of Jill: ", foo.find("Jill").value);
>
> writefln("Upper(James): ", foo.findUpper("James").key);
> writefln("Lower(James): ", foo.findLower("James").key);
>
> writefln("findUpperOrEq(Nino): ", foo.findUpperOrEq("Nino").key);
> writefln("findUpperOrEq(Nina): ", foo.findUpperOrEq("Nina").key);
> writefln("findLowerOrEq(Nino): ", foo.findLowerOrEq("Nino").key);
> writefln("findLowerOrEq(Nina): ", foo.findLowerOrEq("Nina").key);
>
> }
Yes, thank you! I'll give it a try when I get the chance. I really didn't want an intrusive tree. Looks like you have an interesting interface too, although why not have an interface like...
RedBlackTree!(int) tree;
tree.add(4);
tree.add(3);
tree.add(7);
tree.remove(7);
int b = tree.find(7);
? Just wondering how you came up with the interface :)
~ Clay
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to clayasaurus | clayasaurus wrote:
> Yes, thank you! I'll give it a try when I get the chance. I really didn't want an intrusive tree. Looks like you have an interesting interface too, although why not have an interface like...
>
> RedBlackTree!(int) tree;
>
> tree.add(4);
> tree.add(3);
> tree.add(7);
>
> tree.remove(7);
>
> int b = tree.find(7);
>
> ? Just wondering how you came up with the interface :)
>
> ~ Clay
Walter suggested it in a previous post. I think he wanted the same interface as AAs have.
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright schrieb: > Sean Kelly wrote: > >> The C++ set is implemented as a balanced binary tree, and some sort of binary tree is probably your best bet if you want a sorted container with decent lookup performance. > > > C++'s balanced binary trees are red-black trees. I had just proposed that someone do a red-black tree implementation for D. It's not hard, the algorithms are well known. It just takes a little time to sit down and do it. http://www.cs.fiu.edu/~weiss/dsj3/code/weiss/nonstandard/RedBlackTree.java (a generic implementation) My roots are in Modula/Oberon, but if somebody is willing to translate the Java sources into D I am willing to help in translating/implementing the *missing* remove-method (-as good as possible-) , and some helper methods from Modula 3 into D. Björn // *Java basic impl.* public class RedBlackTree<AnyType extends Comparable<? super AnyType>> { /** * Construct the tree. */ public RedBlackTree( ) { nullNode = new RedBlackNode<AnyType>( null ); nullNode.left = nullNode.right = nullNode; header = new RedBlackNode<AnyType>( null ); header.left = header.right = nullNode; } /** * Compare item and t.element, using compareTo, with * caveat that if t is header, then item is always larger. * This routine is called if is possible that t is header. * If it is not possible for t to be header, use compareTo directly. */ private final int compare( AnyType item, RedBlackNode<AnyType> t ) { if( t == header ) return 1; else return item.compareTo( t.element ); } /** * Insert into the tree. * @param item the item to insert. * @throws DuplicateItemException if item is already present. */ public void insert( AnyType item ) { current = parent = grand = header; nullNode.element = item; while( compare( item, current ) != 0 ) { great = grand; grand = parent; parent = current; current = compare( item, current ) < 0 ? current.left : current.right; // Check if two red children; fix if so if( current.left.color == RED && current.right.color == RED ) handleReorient( item ); } // Insertion fails if already present if( current != nullNode ) throw new DuplicateItemException( item.toString( ) ); current = new RedBlackNode<AnyType>( item, nullNode, nullNode ); // Attach to parent if( compare( item, parent ) < 0 ) parent.left = current; else parent.right = current; handleReorient( item ); } /** * Remove from the tree. * @param x the item to remove. * @throws UnsupportedOperationException if called. */ public void remove( AnyType x ) { throw new UnsupportedOperationException( ); } /** * Find the smallest item the tree. * @return the smallest item or null if empty. */ public AnyType findMin( ) { if( isEmpty( ) ) return null; RedBlackNode<AnyType> itr = header.right; while( itr.left != nullNode ) itr = itr.left; return itr.element; } /** * Find the largest item in the tree. * @return the largest item or null if empty. */ public AnyType findMax( ) { if( isEmpty( ) ) return null; RedBlackNode<AnyType> itr = header.right; while( itr.right != nullNode ) itr = itr.right; return itr.element; } /** * Find an item in the tree. * @param x the item to search for. * @return the matching item or null if not found. */ public AnyType find( AnyType x ) { nullNode.element = x; current = header.right; for( ; ; ) { if( x.compareTo( current.element ) < 0 ) current = current.left; else if( x.compareTo( current.element ) > 0 ) current = current.right; else if( current != nullNode ) return current.element; else return null; } } /** * Make the tree logically empty. */ public void makeEmpty( ) { header.right = nullNode; } /** * Print all items. */ public void printTree( ) { printTree( header.right ); } /** * Internal method to print a subtree in sorted order. * @param t the node that roots the tree. */ private void printTree( RedBlackNode<AnyType> t ) { if( t != nullNode ) { printTree( t.left ); System.out.println( t.element ); printTree( t.right ); } } /** * Test if the tree is logically empty. * @return true if empty, false otherwise. */ public boolean isEmpty( ) { return header.right == nullNode; } /** * Internal routine that is called during an insertion * if a node has two red children. Performs flip and rotations. * @param item the item being inserted. */ private void handleReorient( AnyType item ) { // Do the color flip current.color = RED; current.left.color = BLACK; current.right.color = BLACK; if( parent.color == RED ) // Have to rotate { grand.color = RED; if( ( compare( item, grand ) < 0 ) != ( compare( item, parent ) < 0 ) ) parent = rotate( item, grand ); // Start dbl rotate current = rotate( item, great ); current.color = BLACK; } header.right.color = BLACK; // Make root black } /** * Internal routine that performs a single or double rotation. * Because the result is attached to the parent, there are four cases. * Called by handleReorient. * @param item the item in handleReorient. * @param parent the parent of the root of the rotated subtree. * @return the root of the rotated subtree. */ private RedBlackNode<AnyType> rotate( AnyType item, RedBlackNode<AnyType> parent ) { if( compare( item, parent ) < 0 ) return parent.left = compare( item, parent.left ) < 0 ? rotateWithLeftChild( parent.left ) : // LL rotateWithRightChild( parent.left ) ; // LR else return parent.right = compare( item, parent.right ) < 0 ? rotateWithLeftChild( parent.right ) : // RL rotateWithRightChild( parent.right ); // RR } /** * Rotate binary tree node with left child. */ private static <AnyType> RedBlackNode<AnyType> rotateWithLeftChild( RedBlackNode<AnyType> k2 ) { RedBlackNode<AnyType> k1 = k2.left; k2.left = k1.right; k1.right = k2; return k1; } /** * Rotate binary tree node with right child. */ private static <AnyType> RedBlackNode<AnyType> rotateWithRightChild( RedBlackNode<AnyType> k1 ) { RedBlackNode<AnyType> k2 = k1.right; k1.right = k2.left; k2.left = k1; return k2; } private static class RedBlackNode<AnyType> { // Constructors RedBlackNode( AnyType theElement ) { this( theElement, null, null ); } RedBlackNode( AnyType theElement, RedBlackNode<AnyType> lt, RedBlackNode<AnyType> rt ) { element = theElement; left = lt; right = rt; color = RedBlackTree.BLACK; } AnyType element; // The data in the node RedBlackNode<AnyType> left; // Left child RedBlackNode<AnyType> right; // Right child int color; // Color } private RedBlackNode<AnyType> header; private RedBlackNode<AnyType> nullNode; private static final int BLACK = 1; // BLACK must be 1 private static final int RED = 0; // Used in insert routine and its helpers private RedBlackNode<AnyType> current; private RedBlackNode<AnyType> parent; private RedBlackNode<AnyType> grand; private RedBlackNode<AnyType> great; // Test program public static void main( String [ ] args ) { RedBlackTree<Integer> t = new RedBlackTree<Integer>( ); final int NUMS = 400000; final int GAP = 35461; System.out.println( "Checking... (no more output means success)" ); for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS ) t.insert( i ); if( t.findMin( ) != 1 || t.findMax( ) != NUMS - 1 ) System.out.println( "FindMin or FindMax error!" ); for( int i = 1; i < NUMS; i++ ) if( t.find( i ) != i ) System.out.println( "Find error1!" ); } } // *Modula 3 Remove* PROCEDURE Delete (t: T; k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN = VAR curr := t.root; rep, repCh: Node; BEGIN (* find the node to delete (if any *) WHILE curr # t.nil DO CASE t.keyCompare(k, curr.k) OF | -1 => curr := curr.l | 1 => curr := curr.r | 0 => EXIT END END; IF curr = t.nil THEN RETURN FALSE END; (* locate replacement node and one of its children *) IF curr.l = t.nil OR curr.r = t.nil THEN rep := curr ELSE rep := Successor(t, curr) END; IF rep.l # t.nil THEN repCh := rep.l ELSE repCh := rep.r END; (* splice out "rep" node *) repCh.p := rep.p; IF rep.p = t.nil THEN t.root := repCh ELSE IF rep = rep.p.l THEN repCh.p.l := repCh ELSE repCh.p.r := repCh END END; (* save value of node to be deleted *) v := curr.v; (* copy "rep" fields into "curr" if they are different *) IF rep # curr THEN curr.k := rep.k; curr.v := rep.v END; (* rebalance tree if necessary *) IF rep.color = Color.Black THEN DeleteFixup(t, repCh) END; DEC(t.num); RETURN TRUE END Delete; PROCEDURE DeleteFixup (t: T; ch: Node) = VAR p, sib: Node; BEGIN WHILE ch # t.root AND ch.color = Color.Black DO p := ch.p; IF ch = p.l THEN (* "ch" is the left child of "p" *) sib := p.r; <* ASSERT sib # t.nil *> IF sib.color = Color.Red THEN (* case 1 *) sib.color := Color.Black; p.color := Color.Red; LeftRotate(t, p, sib); sib := p.r END; <* ASSERT sib.color = Color.Black AND sib # t.nil *> IF sib.l.color = Color.Black AND sib.r.color = Color.Black THEN (* case 2 *) sib.color := Color.Red; ch := p ELSE IF sib.r.color = Color.Black THEN (* case 3 *) <* ASSERT sib.l.color = Color.Red *> sib.l.color := Color.Black; sib.color := Color.Red; RightRotate(t, sib, sib.l); sib := p.r END; <* ASSERT sib.r.color = Color.Red *> (* case 4 *) sib.color := p.color; p.color := Color.Black; sib.r.color := Color.Black; LeftRotate(t, p, sib); ch := t.root END ELSE (* "ch" is the right child of "p" *) sib := p.l; <* ASSERT sib # t.nil *> IF sib.color = Color.Red THEN (* case 1 *) sib.color := Color.Black; p.color := Color.Red; RightRotate(t, p, sib); sib := p.l END; <* ASSERT sib.color = Color.Black AND sib # t.nil *> IF sib.r.color = Color.Black AND sib.l.color = Color.Black THEN (* case 2 *) sib.color := Color.Red; ch := p ELSE IF sib.l.color = Color.Black THEN (* case 3 *) <* ASSERT sib.r.color = Color.Red *> sib.r.color := Color.Black; sib.color := Color.Red; LeftRotate(t, sib, sib.r); sib := p.l END; <* ASSERT sib.l.color = Color.Red *> (* case 4 *) sib.color := p.color; p.color := Color.Black; sib.l.color := Color.Black; RightRotate(t, p, sib); ch := t.root END END END; ch.color := Color.Black END DeleteFixup; Indeed, a wild mixture!!! |
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright schrieb:
> .....that someone do a red-black tree implementation for D. It's not hard, the algorithms are well known. It just takes a little time to sit down and do it.
Hi algorithm fans, maybe you can help a bit. I own the Algorithms and Data Structures books from Wirth and Sedgewick. (Both antik/antique ... means Pascal, still true! and of course a little bit outdated) If you have a hint what is worth reading now (except C++ books) Thanks in advance. Björn
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to KlausO | KlausO wrote:
> clayasaurus wrote:
>> Yes, thank you! I'll give it a try when I get the chance. I really didn't want an intrusive tree. Looks like you have an interesting interface too, although why not have an interface like...
>>
>> RedBlackTree!(int) tree;
>>
>> tree.add(4);
>> tree.add(3);
>> tree.add(7);
>>
>> tree.remove(7);
>>
>> int b = tree.find(7);
>>
>> ? Just wondering how you came up with the interface :)
>>
>> ~ Clay
>
> Walter suggested it in a previous post. I think he wanted the same interface as AAs have.
Oh, alright :)
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to BLS | BLS wrote:
> Walter Bright schrieb:
>
>> .....that someone do a red-black tree implementation for D. It's not hard, the algorithms are well known. It just takes a little time to sit down and do it.
>
> Hi algorithm fans, maybe you can help a bit. I own the Algorithms and Data Structures books from Wirth and Sedgewick. (Both antik/antique ... means Pascal, still true! and of course a little bit outdated) If you have a hint what is worth reading now (except C++ books) Thanks in advance. Björn
Personally, I like Sedgewick's "Algorithms in C++" series the best. And the "Algorithms in Java" versions appear similar, so I think they're probably just as good. I find the writing succinct and clear, and performance is discussed thoroughly and supported by actual test numbers.
Also, while I don't like the writing style in M.A. Weiss' first edition "Data Structures and Problem Solving with C++" as much as Sedgewick, it does cover some topics that Sedgewick doesn't. Weiss' more recent edition for Java (and I assume C++), however, is about half as thick and its clarity has suffered from all the editing. But it does still cover the same range of topics, so it may be worth a look.
Sean
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to KlausO | KlausO wrote: > KlausO wrote: >> >> Seems to be an interesting task to get used to the new foreach >> features. I'll give it a try. >> >> Klaus > > Ok, I rewrote the intrusive version to a nonintrusive one. > It has almost the interface you mentioned. > > Please note: IMHO, there are some points where the template > needs some refinement. > > 1. A third template parameter "bool duplicatesAllowed" should be > added and handled in insert/remove via "static if". Then you > have multisets at hand ! > > 2. Sometimes it exposes an interface to the internally used nodes > to be able to access the key/value pairs. A special iterator > class which could encapsulate the "not found/not inserted" > state would be better. > > 3. The insert via opIndexAssign/find via opIndex is IMHO not > optimal because you could not return the "not found/not inserted" > state. There are some possibilities to resolve this: > > a) Throw an exception (IMHO, ugly) This is a lot better than returning an empty node, though. If you can tell me why it crashed, then do it :) > > Using insert like > if (foo.insert("Jill", 13)) > looks better to me. You could implement both and let the programmer decide. It also might be neat to implement opIn_r, so you can use... if (!("Jill" in tree)) tree.add("Jill"); |
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | Sean Kelly schrieb:
> Personally, I like Sedgewick's "Algorithms in C++" series the best. And the "Algorithms in Java" versions appear similar, so I think they're probably just as good. I find the writing succinct and clear, and performance is discussed thoroughly and supported by actual test numbers.
>
> Also, while I don't like the writing style in M.A. Weiss' first edition "Data Structures and Problem Solving with C++" as much as Sedgewick, it does cover some topics that Sedgewick doesn't. Weiss' more recent edition for Java (and I assume C++), however, is about half as thick and its clarity has suffered from all the editing. But it does still cover the same range of topics, so it may be worth a look.
>
>
> Sean
Thanks for your answere Sean,
Thinking about your answere and the nature of Java (no pointers) I guess that Sedgewick's "Algorithms in Java" could fullfill my needs because I would like to translate a lot of (pseudo) code like this :
nodepointer is POINTER to node;
node is record
begin
d is interger;
lnode is nodepointer;
rnode is nodepointer;
data is Anytype;
end
into D classes.
Maybe you know about the meanwhile dead PM3 Modula 3 efforts ... wish we have such a HUGE lib in D. Hope that comparing pointerless Java examples will help to translate Modula sources into D. Ahem...
Björn
btw :I wonder wether Sedgewick is still using tons of academic terms (means showing how clever he is) or is he meanwhile able to produce some output a human-beeing can read.
|
October 23, 2006 Re: Equivilent of STL Set in D ? ... | ||||
---|---|---|---|---|
| ||||
Posted in reply to BLS | BLS wrote:
> btw :I wonder wether Sedgewick is still using tons of academic terms (means showing how clever he is) or is he meanwhile able to produce some output a human-beeing can read.
I find Sedgewick to be quite readable, but his material is a bit more technical than some of the other texts. I personally like this because it makes for good reference material, but if I were teaching the subject I might choose a book that doesn't jump into the middle of things quite so quickly.
For comparison, my wife took an algorithm analysis course recently that used Weiss' Java book. She found the descriptions in it confusing, but thought my 1st ed. C++ copy of the same book (same topics but much longer) was excellent. So I'd be inclined to recommend Weiss except his recent editions don't seem as clear as his earlier editions, as a result of some heavy editing to reduce page count.
I wish I could suggest others, but aside from Knuth those are the only algorithms books I've actually kept.
Sean
|
Copyright © 1999-2021 by the D Language Foundation