shithub: snippets

Download patch

ref: 6669ea8ed594d206d16171b7034125deeca6b806
parent: 909d29b906e31a14522bcd843743e742ef7a388a
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Thu Oct 24 15:42:00 EDT 2024

treewalk.c: Morris traversal using threading (pre-, in- and post-order)

--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@
 * `msr.c` MSR reading tool
 * `nanosec.c` nanosec(), a replacement for (way more expensive) nsec()
 * `qt.[ch]` [QP tries](https://dotat.at/prog/qp/README.html)
+* `treewalk.c` Morris traversal using threading (pre-, in- and post-order)
 * `xml.[ch]` XML parser, works as a streaming parser as well
 
 * `clear` removes all program output from the terminal, leaving only commands used
--- /dev/null
+++ b/treewalk.c
@@ -1,0 +1,189 @@
+/*
+ * Demonstrates how simple recursive traversing a non-balanced tree can
+ * eat your stack (fast).  Demonstrates an iterative O(N) complexity +
+ * O(1) space traversal to be used instead if your tree may look like a
+ * very long stick.
+ *
+ * Read the code.
+ *
+ * 7c -FTVw treewalk.c && 7l treewalk.7 && ./7.out [options]
+ */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+
+typedef struct Tree Tree;
+
+struct Tree {
+	int v;
+	Tree *left;
+	Tree *right;
+};
+
+enum {
+	PreOrder,
+	InOrder,
+	PostOrder,
+};
+
+static Biobuf *b;
+static int simple;
+static int mode = PreOrder;
+
+/*
+ * A tree that is on https://en.wikipedia.org/wiki/Tree_traversal#Depth-first_search
+ * (as of 2024/10/24). It's to verify the correctness with.
+ */
+static Tree stC = {'C'}, stE = {'E'}, stH = {'H'};
+static Tree stA = {'A'}, stD = {'D', &stC, &stE}, stI = {'I', &stH};
+static Tree stB = {'B', &stA, &stD}, stG = {'G', nil, &stI};
+static Tree st = {'F', &stB, &stG};
+
+static void
+visit(Tree *t)
+{
+	Bprint(b, simple ? "%c " : "%d\n", t->v);
+}
+
+/* nothing special here */
+static void
+walkrecursive(Tree *t)
+{
+	if(mode == PreOrder)
+		visit(t);
+	if(t->left)
+		walkrecursive(t->left);
+	if(mode == InOrder)
+		visit(t);
+	if(t->right)
+		walkrecursive(t->right);
+	if(mode == PostOrder)
+		visit(t);
+}
+
+/*
+* To morris-traverse in post-order we have to visit nodes in reverse
+* upon discovering an earlier created thread.  This function flips the
+* subtree in-place, then visits each node while flipping the subtree
+* back to what it was.  It still does not allocate any extra space.
+*/
+static void
+postwalk(Tree *a, Tree *b)
+{
+	Tree *v, *vr, *vr2;
+
+	if(a != b){
+		for(v = a, vr = v->right; vr != b; v = vr, vr = vr2){
+			vr2 = vr->right;
+			vr->right = v;
+		}
+		vr->right = v;
+		for(v = b, vr = v->right; vr != a; v = vr, vr = vr2){
+			visit(v);
+			vr2 = vr->right;
+			vr->right = v;
+		}
+		visit(v);
+		vr->right = v;
+	}
+	visit(a);
+}
+
+/* this one is more interesting */
+static void
+walkmorris(Tree *t)
+{
+	Tree *p, t0;
+
+	if(mode == PostOrder){
+		/* in post-order we need to have a fake root to return to */
+		t0.v = 'X';
+		t0.left = t;
+		t0.right = nil;
+		t = &t0;
+	}
+
+	for(; t ;){
+		if(t->left){
+			for(p = t->left; p->right && p->right != t; p = p->right);
+			if(p->right){
+				if(mode == InOrder)
+					visit(t);
+				else if(mode == PostOrder)
+					postwalk(t->left, p);
+				p->right = nil;
+				t = t->right;
+			}else{
+				if(mode == PreOrder)
+					visit(t);
+				p->right = t;
+				t = t->left;
+			}
+		}else{
+			if(mode != PostOrder)
+				visit(t);
+			t = t->right;
+		}
+	}
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	Tree *t;
+	int n, i, num = 1<<16;
+	int nol, nor, morris;
+
+	nol = nor = simple = morris = 0;
+	ARGBEGIN{
+	case 'l': /* no left nodes will be set */
+		nol++;
+		break;
+	case 'r': /* no right nodes will be set */
+		nor++;
+		break;
+	case 'm': /* use iterative traversal */
+		morris++;
+		break;
+	case 'i':
+		mode = InOrder;
+		break;
+	case 'p':
+		mode = PostOrder;
+		break;
+	case 's': /* use a small and simple tree */
+		simple++;
+		break;
+	default:
+		fprint(2, "usage: %s [-i | -p] [-l | -r] [-m] [-s]\n", argv0);
+		threadexitsall("usage");
+	}ARGEND
+
+	if(nol && nor)
+		sysfatal("it wouldn't even be a list, dumbass");
+
+	b = Bfdopen(1, OWRITE);
+	if(simple){
+		t = &st;
+	}else{
+		t = calloc(num, sizeof(*t));
+		for(i = 0, n = 1; i < num; i++){
+			if(!nol && n < num)
+				t[i].left = &t[n++];
+			if(!nor && n < num)
+				t[i].right = &t[n++];
+			t[i].v = i;
+		}
+	}
+
+	if(morris)
+		walkmorris(t);
+	else
+		walkrecursive(t);
+	if(simple)
+		Bprint(b, "\n");
+
+	Bterm(b);
+	threadexitsall(nil);
+}