PAT 1130 Infix Expression——什么才是DFS?由“柳神遍历”写法引发的思考

本文深入解析柳神在DFS遍历算法上的独特写法,对比传统遍历方式,强调节点形态识别的重要性,尤其在处理中缀表达式与字符串输出场景下。通过具体实例,如非字符串仿写,展示如何利用STL队列和stringstream实现更高效的数据缓存与类型转换。
常见遍历写法

或者说书上一般的范例写法
此处特指王道,天勤等考研递归写法

void  PrintTree(BiTree* T) {
	if (T) {
		PrintTree(T->lchild);
		PrintTree(T->rchild);
		printf("%d ", T->key);
	}
}
柳神遍历代码

源地址:https://www.liuchuo.net/archives/3798

string ans = dfs(root);
string dfs(int root) {
    if (a[root].l == -1 && a[root].r == -1)
        return a[root].data;
    if (a[root].l == -1 && a[root].r != -1)
        return "(" + dfs(a[root].r) + a[root].data   + ")";
    if (a[root].l != -1 && a[root].r != -1)
        return"(" + dfs(a[root].l) + dfs(a[root].r)  + a[root].data + ")";
   
}

何为DFS?

DFS脱胎于图的一种遍历算法,
常见会将其类比于树的先序算法
广义开来甚至类比于中序算法,后序算法
其基本思想是:任选一个结点,检查这个点所有的邻点,递归访问其中未被访问的点

写法剖析——比较柳神与传统写法

传统的写法仅能保证遍历全部的点;并不具备识别点类型的功能
而柳神的写法是以三角形为视角,重视节点形态的写法
原因是这道题有特殊地方,中缀表达式其中对于
1.“-”和“()”的处理 **
2.字符串输出
如果有详细研究的同学会发现
要求1都与树的节点形状有关**,因此可以随时变更遍历方法
要求2.直接导致了可以进行 return ++这种字符串返回拼接时的**“简洁,高端”写法**
以下是我的非字符串的仿写

仿写版1
queue<int> qu;
PrintTree(root,qu);
void  PrintTree(BiTree* T,queue<int>& qu) {//左中右
	if (T->lchild == NULL && T->rchild == NULL) {
		qu.push(T->key);
		return ;
	}
	if (T->lchild == NULL && T->rchild != NULL) {
		PrintTree(T->rchild,qu);//哪边不空访问哪边
		qu.push(T->key);
		return;
	}
	if (T->lchild != NULL && T->rchild == NULL) {
		PrintTree(T->lchild, qu);
		qu.push(T->key);
		return;
	}
	if (T->lchild != NULL && T->rchild != NULL) {//此段表明为后序遍历,其他遍历则调整push位置
		PrintTree(T->lchild, qu);
		PrintTree(T->rchild, qu);
		qu.push(T->key);
		return;
	}
}

结论:简单说,在判断叶节点形态的同时,需要有一个缓存数据的地方。此处用stl—queue缓存

仿写版2
void  PrintTree(BiTree* T, stringstream &stream, string &result) {//左中右
	if (T->lchild == NULL && T->rchild == NULL) {
		stream << T->key<<" ";
		//stream>> result;
		return ;
	}
	if (T->lchild == NULL && T->rchild != NULL) {
		PrintTree(T->rchild, stream, result);//哪边不空访问哪边
		stream << T->key << " ";
		//stream >> result;
		return;
	}
	if (T->lchild != NULL && T->rchild == NULL) {
		PrintTree(T->lchild, stream, result);
		stream << T->key << " ";
		//stream >> result;
		return;
	}
	if (T->lchild != NULL && T->rchild != NULL) {//此段表明为后序遍历,其他遍历则调整push位置
		PrintTree(T->lchild, stream, result);
		PrintTree(T->rchild, stream, result);
		stream << T->key << " ";
		//stream >> result;
		return;
	}
}
getline(stream, result);//读取一整行,直到换行符结束,输出时换行符是被吃掉的;endl(换行+刷新流)等价于“\n”;
	cout << result << endl;

结论:此处用stringstream缓存,原因在于stringstream可以进行类型转换,在某些题可能有用,其次要注意 “流”传递要用引用

后记

这种写法还是十分值得借鉴与模仿及扩展的,因为更多的题目并不会傻乎乎地只要求你遍历一下,更多的隐含要求会基于叶节点形状来考察

### C++ 中 `for (char c : infix)` 的含义和用法 `for (char c : infix)` 是 C++11 引入的一种范围基循环(Range-based for loop),用于遍历容器中的每一个元素。这种语法简化了传统的迭代操作,使得代码更加简洁易读。 #### 语法解析 该语句的核心部分如下: - **`char c`**: 表示每次循环时提取的一个元素,这里是一个字符类型的变量。 - **`: infix`**: 表示要遍历的目标范围,在这里是名为 `infix` 的对象。通常它可以是一个数组、字符串或者 STL 容器(如 `std::vector`, `std::list` 等)[^1]。 完整的语法形式为: ```cpp for (element_type element : range_expression) { // 循环体 } ``` 其中: - **`element_type`** 是目标范围内单个元素的数据类型; - **`range_expression`** 可以是任何能够返回首尾迭代器的对象,比如数组或标准库容器。 #### 工作原理 当执行 `for (char c : infix)` 时,实际上会调用以下等价的传统写法: ```cpp for(auto it = infix.begin(); it != infix.end(); ++it){ char c = *it; // 循环体逻辑... } ``` 这意味着程序通过迭代器访问集合内的每一项,并将其赋值给局部变量 `c` 进行处理。 #### 使用场景实例 假设我们有一个字符串 `infix="example"` ,那么可以通过下面的方式打印出它的每个字母: ```cpp #include <iostream> #include <string> int main() { std::string infix = "example"; for(char c : infix){ std::cout << c << "\n"; } return 0; } ``` 以上代码片段展示了如何利用增强型for循环逐一输出字符串中的各个字符[^2]。 #### 注意事项 如果希望修改原始数据集里的项目,则需声明引用类型作为循环变量,例如 `for(char &c : infix)` 。这样做的好处是可以直接改变对应位置上的实际数值而不仅仅是副本[^3]。
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值