解决二叉树遍历相关问题(过程中深入一下C++递归程序栈编译和执行)

news2024/11/27 3:43:52

解决二叉树遍历相关问题(过程中深入一下C++递归程序栈编译和执行)

首先,事情是这样的:问题是求二叉树的根节点到某个节点的路径。
方法自然很多:树的后序遍历,图的BFS、DFS遍历等等。
这里,为了快速搞定代码先,就首先考虑了改造递归方式的后序遍历。
简单思路如下:
① 后续遍历,左右根
② 如果遍历到了目标节点:那么此时程序递归栈中,正常的话,从栈底到顶,应该刚好是待求路径
③ 在②的情况下,接下来得防止递归进入某个右子树,以至存好的路径被破坏。(左边不用考虑,因为后序遍历,待求节点的“左边”节点的递归,必然早已出了程序栈)

代码如下:

typedef struct BiTNode{
	char data;
	struct BiTNode * lchild, *rchild;
} BTNode, *BiTree;
//树的,先序,后序,中序,
///后序:(比如:求根到某个节点的路径) 
bool flag = true;
char way[10000];
int i=0;
void PostOrder(BTNode* root, BiTree T, BTNode* destination){
	BiTree p = T;
	if(p == destination) flag = false; //如果 p指向了目标节点地址 
	if(p!=NULL){
		PostOrder(root,p->lchild,destination);
		if(flag){
			PostOrder(root,p->rchild,destination);
		}
	}
	if(!flag){//从找到目标节点之后,开始启动这个分支代码: 
		if(p!=NULL){
			way[i++]=p->data;//反向记录路径 
		}
		if(p == root) {//此时代表:找到目标元素后,程序栈已经回溯到树根,此时,倒序输出 way数组之后,即可退出程序 
			i--;
			for(i;i>=0;i--){
				cout<<way[i]<<" ";
			} 
			return; 
		}
	}
}

首先,必须承认,递归+条件判断,执行时间就是一个超级慢速的过程,不过这不是今天讨论的key。
按说,我这个思路应该是没毛病的,主函数调用测试代码如下:
在这里插入图片描述

输出应为:1 2 3(查找到node3的路径)

int main(){

	BTNode node = {'1',NULL,NULL};
	BiTree T = &node;
	
	BTNode node2 = {'2',NULL,NULL};;
	T->rchild = &node2;
	
	BTNode node3 = {'3',NULL,NULL};;
	BiTree T3 = T->rchild;
	T3->lchild = &node3;
	
	PostOrder(T,T,T3->lchild);
}

在这里插入图片描述
OK,代码介绍完了。接下来,开始聊递归中的一些问题:

值传递 VS 地址传递

首先,如果我的函数是这样定义的话:(值传递,函数里的判断使用&目标值)

void PostOrder(BiTree root,BiTree T, BTNode destination){
	if(p == &destination) flag = false; //如果 p指向了目标节点地址 
	。。。//其他内容省略
}

结果必然是错的,因为值传递,只是简单的将其值copy一份,传递给了子递归,这时候地址已经不是原来destination的地址了(我测试的,地址是随着函数嵌套在累减),(Java的spring中的BeanCopy功能,可以类比此过程)
测试代码:

void PostOrder(BiTree root,BiTree T, BTNode destination){
	BiTree p = T;
	if(p == &destination) flag = false; //如果 p指向了目标节点地址 
	cout<<"p="<<p<<"\t\t"<<"&destination="<<&destination<<endl;
	if(p!=NULL){
		PostOrder(root,p->lchild,destination);
		if(flag){
			PostOrder(root,p->rchild,destination);
		}
	}
	if(!flag){//从找到目标节点之后,开始启动这个分支代码: 
		if(p!=NULL){
			way[i++]=p->data;//反向记录路径 
		}
		if(p == root) {//此时代表:找到目标元素后,程序栈已经回溯到树根,此时,倒序输出 way数组之后,即可退出程序 
			i--;
			for(i;i>=0;i--){
				cout<<way[i]<<" ";
			} 
			return; 
		}
	}
}

int main(){

	BTNode node = {'1',NULL,NULL};
	BiTree T = &node;
	
	BTNode node2 = {'2',NULL,NULL};;
	T->rchild = &node2;
	
	BTNode node3 = {'3',NULL,NULL};;
	BiTree T3 = T->rchild;
	T3->lchild = &node3;
	
	PostOrder(T,T,node3);
//	PostOrder(T,T,T3->lchild);
}

在这里插入图片描述
这里分析一下地址累减的原因:必然是因为计算机底层的程序栈,栈底在高地址部分,然后嵌套的子函数依次在下面低地址部分。&destination相等的部分,是因为在程序递归树的同一层。
这里从计算机组成原理的角度考虑,大致就是如下图所示的底层存储逻辑:
在这里插入图片描述
其实从汇编语言层面理解,也可以:
调用上一层函数的变量的时候,指令一般是指定寄存器进行加字或字节的某整数倍
调用本层函数的变量的时候,指令一般是指定寄存器进行减字或字节的某整数倍

② 考虑问题的时候,不能将关键判断使用第一层调用传递的destination地址,也就是说,必须得保证某个传递地址一直保持不变的状态

直接来例子吧,代码如果这样写:

void PostOrder(BiTree T, BTNode *destination){
	BiTree p = T;
	if(p == destination) flag = false; //如果 p指向了目标节点地址 
	if(p!=NULL){
		PostOrder(p->lchild,destination);
		if(flag){
			PostOrder(p->rchild,destination);
		}
	}
	if(!flag){//从找到目标节点之后,开始启动这个分支代码: 
		if(p!=NULL){
			way[i++]=p->data;//反向记录路径 
		}
		if(p == T) {//此时代表:找到目标元素后,程序栈已经回溯到树根,此时,倒序输出 way数组之后,即可退出程序 
			i--;
			for(i;i>=0;i--){
				cout<<way[i]<<" ";
			} 
			return; 
		}
	}
}

之所以刚开始这样定义函数,我考虑的是等待代码中的左右两个分支问题搞定后,代码自己就回到首次调用了,而这时候的T地址就是最开始那个根,于是乎,我就暂时忽略了递归回溯过程中,T地址一步一步加回栈顶的过程中对分支结构下面那部分if代码的调用。
相信眼尖的一下就看到问题所在了,p==T恒成立,所以不会输出任何东西(因为i都嘎子负数了,还输出个锤子,想想晚上写代码就是脑子瓦特)。

OK,over,有很多CO底层知识,我这里没有深入去解释,但是确实CO知识的存储,对写代码时的理解程度会深一些,确有体会,比如写函数的时候,会想到分段式存储、编译后指令排列的可能顺序,递归过程和回溯过程中地址的传递和变化等等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1056144.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS 7 上编译和安装 SQLite 3.9.0

文章目录 可能报错分析详细安装过程 可能报错分析 报错如下&#xff1a; django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.7.17). 原因&#xff1a;版本为3.7.太低了&#xff0c;需要升级到3.9.0至少 详细安装过程 1.安装所需的…

c语言:通讯录管理系统(增删查改)

前言&#xff1a;在大多数高校内&#xff0c;都是通过设计一个通讯录管理系统来作为c语言课程设计&#xff0c;通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来&#xff0c;可以很好的锻炼学生的编程思维&#xff0c;本文旨在为通讯录管理系统的设计提供思路和…

【STM32基础 CubeMX】PWM输出

文章目录 前言一、PWM是什么&#xff1f;二、CubeMX配置PWM三、代码分析3.1 CubeMX生成代码3.2 PWM的几个库函数HAL_TIM_PWM_Start 3.3 PWM回调函数3.4 占空比占空比是什么__HAL_TIM_SET_COMPARE设置占空比 四、呼吸灯示例总结 前言 STM32微控制器是一系列功能强大的微控制器&…

unordered_map/unordered_set的学习[unordered系列]

文章目录 1.老生常谈_遍历2.性能测试3.OJ训练3.1存在重复元素3.2两个数组的交集Ⅱ3.3两句话中的不常见单词3.4两个数组的交集3.5在长度2N的数组中找出重复N次的元素 1.老生常谈_遍历 #pragma once #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <l…

红黑树(有图解)

目录 介绍 概念 性质 模拟实现 结点定义 插入 保证平衡的原因 一般情况 特殊情况(uncle为黑) uncle不存在 旋转方式 右旋 迭代器 -- 代码 介绍 概念 红黑树是一种自平衡的二叉搜索树 它是在每个节点上引入额外的颜色信息,通过对任何一条从根到叶子的路径…

项目管理之高效合作

序 一件事能不能做成&#xff0c;和你有什么关系&#xff1f;靠的是你的努力吗&#xff1f;还是说靠的只是一个运气&#xff1f; 就像买彩票一样&#xff0c;你觉得中奖和个人努力有没有关系&#xff1b;就像和高考一样&#xff0c;你觉得考上北大清华和个人努力有没有关系&…

IDEA git操作技巧大全,持续更新中

作者简介 目录 1.创建新项目 2.推拉代码 3.状态标识 5.cherry pick 6.revert 7.squash 8.版本回退 9.合并冲突 1.创建新项目 首先我们在GitHub上创建一个新的项目&#xff0c;然后将这个空项目拉到本地&#xff0c;在本地搭建起一个maven项目的骨架再推上去&#xff0…

两条链表相同位数相加[中等]

优质博文IT-BLOG-CN 一、题目 给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式存储的&#xff0c;并且每个节点只能存储一位数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。你可以假设除了数字0之外&#xff0c;这…

一文带你掌握 优先级队列

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;强烈推荐优质专栏: &#x1f354;&#x1f35f;&#x1f32f;C的世界(持续更新中) &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;…

Leetcode.965 单值二叉树

本专栏内容为&#xff1a;leetcode刷题专栏&#xff0c;记录了leetcode热门题目以及重难点题目的详细记录 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;八大排序汇总 &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&#x1f69a; &…

【算法练习Day9】用栈实现队列用队列实现栈

、​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 用栈实现队列用队列实…

代码随想录算法训练营第五十一天 |309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费、总结

一、309.最佳买卖股票时机含冷冻期 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;动态规划来决定最佳时机&#xff0c;这次有冷冻期&#xff01;| LeetCode&#xff1a;309.买卖股票的最佳时机含冷冻期_哔哩哔哩_bilibili 思考&#xff1a; 1.确定dp数组&…

创建型设计模式 原型模式 建造者模式 创建者模式对比

创建型设计模式 单例 工厂模式 看这一篇就够了_软工菜鸡的博客-CSDN博客 4.3 原型模式 4.3.1 概述 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 4.3.2 结构 原型模式包含如下角色&#xff1a; 抽象原型类&#xff1a;规定了…

FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制

FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg 命令视频录制 FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg 命令视频录制安装软件&#xff1a;Screen Capturer Recorder查看可用设备名字音视频录制录制视频&#xff08;默认参数&#xff09;录制声音&#xff08;默认参数&am…

计算机竞赛 深度学习疲劳检测 驾驶行为检测 - python opencv cnn

文章目录 0 前言1 课题背景2 相关技术2.1 Dlib人脸识别库2.2 疲劳检测算法2.3 YOLOV5算法 3 效果展示3.1 眨眼3.2 打哈欠3.3 使用手机检测3.4 抽烟检测3.5 喝水检测 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习加…

AJAX--Express速成

一、基本概念 1、AJAX(Asynchronous JavaScript And XML)&#xff0c;即为异步的JavaScript 和 XML。 2、异步的JavaScript 它可以异步地向服务器发送请求&#xff0c;在等待响应的过程中&#xff0c;不会阻塞当前页面。浏览器可以做自己的事情。直到成功获取响应后&#xf…

1558. 得到目标数组的最少函数调用次数

1558. 得到目标数组的最少函数调用次数 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 1558. 得到目标数组的最少函数调用次数 https://leetcode.cn/problems/minimum-numbers-of-function-calls-to-make-target…

毛玻璃用户卡交互

效果展示 页面结构组成 从效果展示可以看到&#xff0c;此效果都是比较常规的。主要的核心就是卡片的悬停效果。 CSS 知识点 backdrop-filter 回顾transitiontransform 页面基础布局实现 <section><div class"container"><div class"card&q…

Academic accumulation|社会创业研究:过去的成就和未来的承诺

文献来源&#xff1a;Saebi T, Foss N J, Linder S. Social entrepreneurship research: Past achievements and future promises[J]. Journal of management, 2019, 45(1): 70-95. 一、文章介绍 &#xff08;一&#xff09;文章主要包含什么&#xff1f; SE越来越受到学术界的…

凉鞋的 Unity 笔记 104. 测试所涉及的窗口

104. 测试所涉及的窗口 在上一篇&#xff0c;笔者简单介绍了检视器窗口&#xff0c;如图所示&#xff1a; 我们接着介绍上图中的最后一个部分内容&#xff0c;测试部分。 测试部分我们只做了一件非常简单的操作&#xff0c;就是点击了一下运行按钮&#xff0c;查看结果&#…