问题
I'm having a problem implementing this method in java. I'm specifically implementing the algorithm FINDINTERSECTIONS
in Computational Geometry 3rd Edition using an AVL BST tree for the status. The description from the book is shown below:
The problem I'm having is implementing step 5 in HANDLEEVENTPOINT
. When the event point is an intersection, the status is no longer totally ordered there, because for intersection lines, they cross at their intersection point and need to be swapped in the status. Since the BST I'm using is an AVLTree, the delete method fails because the rebalancing method requires proper ordering of the elements (i.e. the delete method assumes the tree is properly ordered, and performs rotations with respect to the order in order to maintain log(n) height). Also, the status I'm using stores the data in the nodes instead of the leaves as shown in the figure. If I understand correctly, the book says that either kind of tree can be used.
回答1:
First off use a leaf version of a balanced binary search tree whether red-black or AVL. I used red-black.
Get Peter Brass's book on advanced data structures because you will have trouble finding anything on these leaf trees in virtually all the standard algorithm / data structure books. I believe they are also called exogenous trees.
http://www-cs.engr.ccny.cuny.edu/~peter/
Also, you can look at "Algorithms and Data Structures: The Basic Toolbox" by Mehlhorn and Sanders which goes into the "sorted sequence" data structure. They create these with the help of only leaf trees when trees are used. These are also some of the folks that developed LEDA.
Also look at the LEDA book online bc it has a chapter on how to implement this algorithm and how to handle ALL the "problem cases." I think this is chapter 9 and is a bit hard to follow maybe because English is not the native tongue of the authors ... PITA!!
http://people.mpi-inf.mpg.de/~mehlhorn/LEDAbook.html
You can doubly link the leaf nodes data items together and you have created a sorted sequence with the tree as a navigation structure to the linked list of items. That is how LEDA and in think CGAL do this.
Duplicate items are handled differently in the event queue than the sweep line status structure. For the event queue, just add to a leaf a linked list of items (see Brass's book). Here each leaf corresponds to an event point and has a list of all segments with a starting-end-point this that same as the event point. So some will have empty lists like intersection-event-points and ending-event-points. At least that is how some implementations do this.
For the sweep status structure. Overlapping parallel segments are differentiated by say segment ids. They do not talk about these in the book you are reading/referencing. However, the LEDA book tells you how to handle these. So even though sweep status trees comparator says two segments have the same end-point and orientation, the comparator breaks the tie by using the segments indexes in the segments database, array or whatever.
Some more important points:
Pool points! This common pool of points are basic and then make up the segments and are used in all the data structures. Using the pool allows one to test for point equality by just testing for identity! This avoids using a comparator which slows things down and can introduce errors.
It is key that you avoid using the tree comparators as much as possible.
When checking if segments belong to the same bundle or are members of the three sets you are having a question about (i.e, start, end or interesect with and event point on the sweep-line), DO NOT USE THE COMPARATOR.
Instead, use that fact that segments belonging to the same bundle can have some "information property" say in the list that either points to the event queue when a segment intersects an event point, or points to the successor item in the list if the segment overlaps the successor, or points to null otherwise. So you will need some cross-linking between the event queue with the sweepline status structure. Your sets and bundles are the very fast and easy to find. Go to the start or end of the linked-list associate with the status tree and go through it item by item with a very simple test.
BOTTOM LINE. Get the sorted sequence / balanced binary tree data structure right and work on that a lot before implementing the rest of Bentley-Ottmann.
This is really the key and that book does not point that out at all but unfortunately that isn't it's intent since this implementation is tricky. Also, note that the book augments the navigation tree with an extra link in the internal nodes of the tree that point to associated leaf nodes. This just makes find a bit faster but may not be apparent if you are not familiar with leaf trees. A key in a leaf tree is often found twice, at the leaf node and elsewhere in an internal node of the tree.
FINALLY
Packages like LEDA/CGAL use exact arithmetic for things to work well. It took LEDA developers 10 years to get things right and that was mostly was due to using exact arithmetic. You maybe OK with a basic cross-product test used for orientation but if you need an exact version then you can find Prof. Jonathan Shewchuk exact arithmetic package on his site.
I guess your book just left all this out as an "exercise for the reader/student." LOL.
回答2:
UPDATE: In your posted algorithm from that book, the swap for reversing intersecting segment order is done via delete and then with a re-insert. LEDA uses reverse_items() for these swaps. It's a more efficient way of doing sub-sequence reversals of nodes and items without the use of the comparator. Search for _rs_tree.c to see LEDA source or see below.
// reverse a subsequence of items, assuming that all keys are
// in the correct order afterwards
//
void rs_tree::reverse_items( rst_item pl, rst_item pr )
{
int prio ;
register rst_item ppl = p_item(pl), // pred of pl
ppr = s_item(pr), // succ of pr
ql, qr ;
while( (pl!=pr) && (pl!=ppl) ) { // pl and pr didnt't
// met up to now
// swap all of pl and pr except the key
// swap parents
ql = parent(pl) ; qr = parent(pr) ;
if( pl==r_child(ql) )
r_child(ql) = pr ;
else
l_child(ql) = pr ;
if( pr==r_child(qr) )
r_child(qr) = pl ;
else
l_child(qr) = pl ;
parent(pl ) = qr ; parent(pr) = ql ;
// swap left children
ql = l_child(pl) ; qr = l_child(pr) ;
if( ql != qr ) { // at least one exists
l_child(pl) = qr ; parent(qr) = pl ;
l_child(pr) = ql ; parent(ql) = pr ;
}
// swap right children
ql = r_child(pl) ; qr = r_child(pr) ;
if( ql != qr ) { // at least one exists
r_child(pl) = qr ; parent(qr) = pl ;
r_child(pr) = ql ; parent(ql) = pr ;
}
// swap priorities
prio = pl->prio ; pl->prio = pr->prio ;
pr->prio = prio ;
// swap pred-succ-ptrs
s_item(ppl) = pr ; p_item(ppr) = pl ;
ql = pl ; pl = s_item(pl) ; // shift pl and pr
qr = pr ; pr = p_item(pr) ;
s_item(ql) = ppr ; p_item(qr) = ppl ;
ppl = qr ; ppr = ql ; // shift ppl and ppr
}
// correct "inner" pred-succ-ptrs
p_item(ppr) = pl ; s_item(ppl) = pr ;
if( pl==pr ) { // odd-length subseq.
p_item(pl) = ppl ; s_item(pr) = ppr ;
}
}
ADDITIONALLY: Sorted sequence data structures can use AVL trees, ab-trees, red-black trees, splay trees, or skip lists. An ab-tree with a = 2, b = 16 fared best in speed comparison of search-trees used in LEDA**.
** S. Naber. Comparison of search-tree data structures in LEDA. Personal communication.
来源:https://stackoverflow.com/questions/26243952/implementing-bentley-ottmann-algorithm-with-an-avl-tree