shithub: asif

Download patch

ref: 99e8d835b687b9e946293b817d2e2b81ce76907e
parent: 599f5d31aae2b1f3b09df815e5fc70ccd6cf387a
author: qwx <qwx@sciops.net>
date: Thu Jun 2 19:12:27 EDT 2022

pheap: finally fix

some assumptions were wrong, others were unverified.  three pointers
are used to represent the graph: parent, left (children), right (older
sibling).  siblings are stored as a linked list, with the most recent
one being in front.  most importantly, parent is either the previous
sibling in the list, or if it's the first node, the parent in the
tree.  essentially, this is a doubly linked list, with the parent
pointer helping efficiency despite the overload.  the biggest source
of issue was managing links correctly.

mergequeue takes the roots of two heaps.  a root may not by definition
have a parent or sibling (right pointer), but the left trees must be
taken care of.  mergepairs takes the *root* of a heap (when popping,
the root node is slashed off), splits, then merges recursively from
left to right.  here right nodes must be handled correctly to avoid
dropping entire branches of the tree and maintain parent links
correctly.  split trees are cut off from each other; the first two
heaps are merged together, and a third is recursively split and merged
the same way.  finally, decreasekey must preserve and fix the siblings
list, cut off the node, then merge the new heap with the root.
description subject to potential bugs, ymmv.

all relatively simple issues...  interesting future directions: smooth
heaps (2018 Kozma and Saranurak), related slim heaps.

--- a/pheap.c
+++ b/pheap.c
@@ -21,6 +21,9 @@
 	if(p == queue)
 		fprint(2, "pheap::checkheap %#p\n", p);
 	assert(p == queue || p->parent != nil && p->parent != p);
+	assert(p->parent == nil || p->parent->right == p || p->parent->left == p);
+	if(p->parent != nil && p->parent->left == p)
+		assert(p->parent->n <= p->n);
 	for(q=p; q!=nil; q=q->right)
 		checkheap(q->left, queue);
 }
@@ -37,7 +40,7 @@
 	for(i=0; i<level; i++)
 		fprint(2, "\t");
 	for(q=p; q!=nil; q=q->right){
-		fprint(2, "[%#p,%.5f]", q, q->n);
+		fprint(2, "[%#p %.5f] — ", q, q->n);
 		printright(q->left, level+1);
 	}
 	fprint(2, "\n");
@@ -73,11 +76,15 @@
 		return a;
 	else if(a->n < b->n){
 		b->right = a->left;
+		if(a->left != nil)
+			a->left->parent = b;
 		a->left = b;
 		b->parent = a;
 		return a;
 	}else{
 		a->right = b->left;
+		if(b->left != nil)
+			b->left->parent = a;
 		b->left = a;
 		a->parent = b;
 		return b;
@@ -85,27 +92,21 @@
 }
 
 static Pairheap *
-mergepairs(Pairheap *a, Pairheap *q)
+mergepairs(Pairheap *a)
 {
 	Pairheap *b, *c;
 
 	if(a == nil)
 		return nil;
-	if(a->parent != nil && a->parent->left == a)
-		a->parent->left = nil;
 	a->parent = nil;
-	assert(a->parent == nil || a->parent->left != a);
 	b = a->right;
 	if(b == nil)
 		return a;
 	a->right = nil;
-	if(b->parent != nil && b->parent->left == b)
-		b->parent->left = nil;
-	b->parent = nil;
-	assert(b->parent == nil || b->parent->left != b);
 	c = b->right;
+	b->parent = nil;
 	b->right = nil;
-	return mergequeue(mergequeue(a, b), mergepairs(c, q));
+	return mergequeue(mergequeue(a, b), mergepairs(c));
 }
 
 void
@@ -129,8 +130,9 @@
 	if(p == nil)
 		return nil;
 	dprint(Logparanoid, "pheap::popqueue %#p %.6f\n", p, p->n);
-	*queue = mergepairs(p->left, p);
+	*queue = mergepairs(p->left);
 	debugqueue(queue);
+	p->left = p->right = nil;
 	return p;
 }
 
@@ -137,25 +139,25 @@
 void
 decreasekey(Pairheap *p, double Δ, Pairheap **queue)
 {
-	Pairheap *q;
-
 	dprint(Logparanoid, "pheap::decreasekey %#p %.6f Δ %.6f\n", p, p->n, Δ);
-	p->n -= Δ;
-	if(p->parent != nil && p->n < p->parent->n){
-		assert(p->parent->left != nil);
-		if(p->parent->left == p)
-			p->parent->left = p->right;
-		else{
-			for(q=p->parent->left; q!=nil && q!=p; q=q->right)
-				if(q->right == p)
-					break;
-			assert(q != nil);
-			q->right = p->right;
-		}
-		p->parent = nil;
-		*queue = mergequeue(p, *queue);
-		debugqueue(queue);
+	if(p->parent == nil){
+		p->n -= Δ;
+		return;
 	}
+	if(p->parent->left == p){
+		assert(p->parent->n <= p->n);
+		p->parent->left = p->right;
+	}else{
+		assert(p->parent->right == p);
+		p->parent->right = p->right;
+	}
+	if(p->right != nil)
+		p->right->parent = p->parent;
+	p->right = nil;
+	p->parent = nil;
+	p->n -= Δ;
+	*queue = mergequeue(p, *queue);
+	debugqueue(queue);
 }
 
 Pairheap *