UVa 548二叉树的递归遍历

二叉树的遍历Traversal

n个结点就有n!个遍历方式,研究其中有规律的方式。可以联想到多维数组的排列,算数组存储地址。任何树、森林都可以和二叉树相互转化,因此会着重看二叉树。

一个二叉树的三个要素是根、左子树、右子树。抓住根的绝对位置和左、右相对位置,就知道根左右的前序,根右左的逆前序,左根右的中序,右根左的逆中序,左右根的后序,右左根的逆后序。这6种都是深度优先遍历DFS,因为它总是优先往深处访问。再加上层次遍历这种广度优先遍历,一共就有7种遍历方式。

给定前中后序遍历,推原树:围绕二叉树的三要素,找根和分两拨。因此只给前序和后序无法分两拨,无法唯一还原一棵二叉树。

二叉树采用二叉链表的存储结果,就仍然是两个类,一个结点类,一个链表类。root指针是找到第一个人住的房间就可以串套起来。


以前序遍历为例:

void A(*p) { // 遍历以p指针指向的结点为根结点的二叉树
    if (p != NULL) {
        ①cout<<p->data<<endl; // 根结点
        ②A(q); // A(p->lchild);
        ③A(r); // A(p->rchild);
    }
}

①②③三句话对应三要素。

递归,程序容易写,但是计算机难执行。每个结点都会指三次。第一次指向就输出,第二次指向是从左边回来,第三次指向是从右边退栈回来。指针从何处出发,一定回到何处。指针指向谁,谁就完成了,退栈。(刚进来,一战,二战)->退栈。这些都是计算机中的“废话”,一句都不能少,都至关重要。

所有的递归都可以换成迭代,还有一个公式可以这样做。通过分析递归程序工作过程,可以得到非递归版本程序。

指针偏好是往左走,逮住机会往左走,实在空了才往右做。

do {
    while (p != NULL) {
        cout<<p->data;
        push(p);
        p=p->lchild; // 往左走
    }
    if (栈不空) { // 实在不行走右边
        p=pop(s);
        p=p->rchild;
    }
} while ((栈不空) || (p != NULL));

中序就是把cout换个位置。

对比,递归版和循环版都存在堆栈,只不过循环版节约了堆栈,而且没有第三次指向。

后序遍历非递归算法是唯一一个不得不被迫使用goto语句的,如果不用,程序会很复杂。

写程序要注意,自顶向下,逐步求精;尽量采用结构化模块(单输入、单输出、无死循环、无死语句);清晰第一,效率第二;尽量少用goto语句;拿模块换模块。

应用二叉树遍历有很多实例,比如统计结点个数,统计叶结点个数,算二叉树高度。

所有递归程序都有条件(if)。嵌套调用,相互调用。从哪出发最后会回到哪儿,条件圈,堆栈圈,if结束,堆栈结束。


对于这道题目来说,首先根据中序遍历和后序遍历构造出这棵树,再搜索出从根结点到叶结点的一条最短路径。或者边构造边找。

连续读入未知个数的整数
int read_list(int* a) {
	int n = 0;
	char *p = s;
	int cnt = 0;
	for (;;) {
		int r = sscanf(p, "%d%n", &a[cnt], &n);
		if (r != 1) break;
		cnt++;
		p = p + n;
	}
	return cnt;
}

&n表示当前读的字符,下步跳多少,r就是返回值,读了多少整数。

分治法建树

采用静态结构,需要给每个结点分配房间。可以按照cnt的顺序一个一个来,该题目的特点是权值各不相同而且范围是(0,10000),所以房间号就是权值,权值就是房间号,可以省去一个data数组。

左子树遍历完了才会遍历右子树,所以左右子树各自是连在一起的。首先从后序遍历里面找到根,接着从中序遍历里面从左往右走到根,找到左子树,同时找到左子树的结点个数,再继续走就是右子树。后序遍历是左右根,所以从左往右走完那么多个结点个数就是左子树,再继续直到根之前就是右子树。

判空条件就是中序遍历序列,中序遍历连一个结点都没有了,就堆栈回去。

// 中序遍历根据根划分两拨,后序遍历找根(分治) 
int build(int L1, int R1, int L2, int R2) { // L1-R1是中序遍历指针,L2-R2是后序遍历指针,对应二叉树 
	if (L1 > R1 || L2 > R2) {
		return 0;
	}
	int root = post_order[R2];
	int p = L1;
	while (in_order[p] != root) {
		p++;
	}
	int cnt = p - L1; // 左子树的结点个数
	lchild[root] = build(L1, p - 1, L2, L2 + cnt - 1);
	rchild[root] = build(p + 1, R1, L2 + cnt, R2 - 1);
	return root; 
}
搜索最优路径——递归遍历
void traverse(int root, int sum) {
	if (root == 0) {
		return;
	}
	sum = sum + root;
	if (lchild[root] == 0 && rchild[root] == 0) {
		if (sum < best_sum || (sum == best_sum && root < best_leaf)) {
			best_leaf = root;
			best_sum = sum;
		}
	}
	traverse(lchild[root], sum);
	traverse(rchild[root], sum);
}
完整代码
#include <cstdio>
#include <cstring>
#include <sstream>
#include <algorithm>
using namespace std;
const int MAXN = 10010;
char s[100010];
int in_order[MAXN];
int post_order[MAXN];
int lchild[MAXN];
int rchild[MAXN];
int best_leaf, best_sum;

bool is_blank(int c) {
	return c == '\n' || c == '\r' || c == '\t';
}
void Init() {
	best_sum = 0x3f3f3f3f;
	memset(lchild, 0, sizeof(lchild));
	memset(rchild, 0, sizeof(rchild));
	return;
}
int read_list(int* a) {
	int n = 0;
	char *p = s;
	int cnt = 0;
	for (;;) {
		int r = sscanf(p, "%d%n", &a[cnt], &n);
		if (r != 1) break;
		cnt++;
		p = p + n;
	}
	return cnt;
}
// 中序遍历根据根划分两拨,后序遍历找根(分治) 
int build(int L1, int R1, int L2, int R2) { // L1-R1是中序遍历指针,L2-R2是后序遍历指针,对应二叉树 
	if (L1 > R1 || L2 > R2) {
		return 0;
	}
	int root = post_order[R2];
	int p = L1;
	while (in_order[p] != root) {
		p++;
	}
	int cnt = p - L1; // 左子树的结点个数
	lchild[root] = build(L1, p - 1, L2, L2 + cnt - 1);
	rchild[root] = build(p + 1, R1, L2 + cnt, R2 - 1);
	return root; 
}

void traverse(int root, int sum) {
	if (root == 0) {
		return;
	}
	sum = sum + root;
	if (lchild[root] == 0 && rchild[root] == 0) {
		if (sum < best_sum || (sum == best_sum && root < best_leaf)) {
			best_leaf = root;
			best_sum = sum;
		}
	}
	traverse(lchild[root], sum);
	traverse(rchild[root], sum);
}


int main() {
	int first = 1;
	while (fgets(s, 100010, stdin) != NULL) {
		if (s[strlen(s) - 1] == '\n') {
			s[strlen(s) - 1] = 0;
		}
		if (is_blank(s[0]) || s[0] == '\0') {
			continue;
		}
		if (first == 1) {
			int node_num = read_list(in_order);
			Init();
			first = 0;
			continue;
		} 
		int node_num = read_list(post_order);
		first = 1;
		build(0, node_num - 1, 0, node_num - 1);
		traverse(post_order[node_num-1], 0);
		printf("%d\n", best_leaf);	
	}
	return 0;
} 

posted @ 2021-02-28 15:39  Mo_hw  阅读(73)  评论(0编辑  收藏  举报