shithub: snippets

ref: 6669ea8ed594d206d16171b7034125deeca6b806
dir: /treewalk.c/

View raw version
/*
 * 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);
}