算法与数据结构(五)

news2024/11/27 3:45:22

一、c++二叉树结构

在这里插入图片描述

typedef struct BiNode 
{
	int data;  //结点内值域
	struct BiNode* lchild, * rchild; //左孩子 右孩子
} BiNode, * BiTree;

或者:

 
class Node
{
public:
	char data;// data表示该节点代表的内容;
	Node* L;//左子树
	Node* R;//右子树
}; 
Node* T;//创个指针,指向二叉树的根节点;

二、用递归和非递归两种方式实现二叉树的先序、中序、后序遍历

有如下的树:
在这里插入图片描述

递归方法:
先序遍历:
先遍历头,再遍历左子树的结点、再遍历右子树的结点,遍历结果如下:
在这里插入图片描述
递归方法代码如下:

void preOrder(Node*& T)
{
	if (T == NULL)
	{
		return;//如果该结点为空就返回;
	}
 	//递归遍历 
	else
	{
		cout << T->data << " ";  //先输出根节点的那个数字
		preOrder(T->L);          //然后访问左孩子,开始递归
		preOrder(T->R);          //左孩子访问完成,开始右孩子递归
	}
}

以上代码输出即为先遍历
递归序的遍历结果为:
在这里插入图片描述
我们只需要在递归遍历过程中数字第一次出现时打印即可。
非递归:
思路:
先将根节点放到栈中
1、然后从栈中弹出一个结点cur
2、打印cur;
3、先右再左(如果有),否则什么也不做
4、循环1-3,直至栈中没有数据为止。

class Solution {
public:
    void preorderTraversal(TreeNode* root) {
    	if(root != NULL){
    	//申请一个临时栈
        stack<TreeNode*> st;
        //将根节点首先压入栈中
        st.push(root);
   		//如果栈不为空,则开始循环
        while(!st.empty())
        {		//弹出栈中的一个结点
                root = st.pop();
                //打印弹出的结点
                cout << root->val << " ";
                //如果叶结点不为空,则开始先将出栈节点的右结点和左结点先后压入栈中
                if(root->right != NULL)
                {
                	st.push(root->right);
				}
                if(root->left != NULL)
                {
                	st.push(root->left);
				}
		}
	}
}		

中序遍历:
先遍历左,再遍历头的结点、再遍历右子树的结点,遍历结果如下:
在这里插入图片描述
递归方法代码如下:

void inOrder(Node*& T)
{
	if (T == NULL)
		return;
	else
	{
		inOrder(T->L);
		cout << T->data << " ";
		inOrder(T->R);
	}
}

非递归方法:
思路:
1、将压入结点下的每颗子树整棵树左边界进栈(第一次先从根节点开始),对于下图,进栈的为(1,2,4)
2、弹出结点,弹出的过程中对弹出结点打印
3、压入弹出节点的右结点
4、循环以上两个步骤
在这里插入图片描述

class Solution {
public:
    void inOrderTraversal(TreeNode* root) {
    	if(root != NULL){
    	//申请一个临时栈
        stack<TreeNode*> st;
   		//如果栈不为空,则开始循环
        while(!st.empty() || root != NULL)
        {	
        	//如果当前结点不为空,则一直将其左结点压栈	
			if(root != NULL){
				st.push(root);        		
                root = root->left;
                }
            //否则从栈中弹出一个结点,并打印,将其右结点返回
			else{
				root = st.pop();
                cout << root->val << " ";
                root = root->right;
			}
		}
	}
}            
	

后序遍历:
先遍历左,再遍历右、再遍历头结点,遍历结果如下:
在这里插入图片描述
递归方法代码如下:

 
void posOrder(Node*& T)
{
	if (T == NULL)
		return;
	else
	{
		posOrder(T->L);
		posOrder(T->R);
		cout << T->data << " ";
	}
}

非递归方法:
思路:
先将根节点放到栈中
1、然后从栈中弹出一个结点cur
2、将弹出的结点放到一个回收栈中;
3、先左再右(如果有),否则什么也不做
4、循环1-3,直至栈中没有数据为止。
5、挨个从回收栈中弹出数据并打印

class Solution {
public:
    void posOrderTraversal(TreeNode* root) {
    	if(root != NULL){
    	//申请一个临时栈
        stack<TreeNode*> st1;
        stack<TreeNode*> st2;
        //将根节点首先压入栈中
        st1.push(root);
   		//如果栈不为空,则开始循环
        while(!st1.empty())
        {		//弹出栈中的一个结点
                root = st1.pop();
                //将弹出的结点压入回收栈中
                st2.push(root);
                //如果叶结点不为空,则开始先将出栈节点的左结点和右结点先后压入栈中
                if(root->left != NULL)
                {
                	st1.push(root->left);
				}
                if(root->right != NULL)
                {
                	st1.push(root->right);
				}
		}
		//将回收栈中的数据打印
		while(!st2.empty())
		{
			cout<<st2.pop()->val<< " ";
		}
	}
}		

三、二叉树的宽度优先遍历(常见题目:求一棵二叉树的宽度)

在这里插入图片描述
有如上的树。宽度优先遍历输出为:1,2,3,4,5,6,7,8,9
思路:
本题我们使用队列的方式来解决:
1、首先将根节点放到队列中
2、弹出队列中的第一个结点,然后打印该结点,并将该节点的左右子节点先后放到队列中(没有左或者右则直接跳过放另外一个结点)。
3、重复步骤二知道队列为空

#include<stdio.h>
#include<vector>
#include<queue>
//树的机构体
struct TreeNode
{
 int val;
 TreeNode* left;
 TreeNode* right;
 TreeNode(int x): val(x), left(NULL), right(NULL){}
};
//开始进行宽度优先遍历
void BFS(TreeNode* root)
{
	//实例化一个队列
	 std::queue<TreeNode*> Q;
	
	//将根节点压入队列中
	 Q.push(root);
	//如果该队列非空,则一直循环操作
	 while (!Q.empty())
	 {
		//将第一个元素返回并打印
		  TreeNode* node = Q.front();
		  printf("[%d]", node->val);
		//弹出第一个元素
		  Q.pop();
		//开始先放左结点再放右节点的操作
		  if (node->left)
		  {
		   	Q.push(node->left);
		  }
		  if (node->right)
		  {
		   	Q.push(node->right);
		  }
	 }
}
//初始化树
int main()
{
 TreeNode a(1);
 TreeNode b(2);
 TreeNode c(5);
 TreeNode d(3);
 TreeNode e(4);
 TreeNode f(6);
 a.left = &b;
 a.right = &c;
 b.left = &d;
 b.right = &e;
 c.right = &f;
 BFS(&a);
 return 0;
}

四、求二叉树的最大宽度

思路:
1)、首先,我们需要知道每一个节点的层数:每一个节点的层数是其父节点层数+1所以,我们在push一个节点的孩子节点进入队列时,可以保存其孩子节点的层级:当前层级+1。
2)、其次,我们需要获取每一层的宽度,并所有层级宽度取最大值返回。因为宽度遍历方式相同层级的节点肯定是依次输出遍历,我们就很方便对某层宽度进行计数。

int MaxWidth(TreeNode* root)
{
	int widMax = 0; //记录结果:最大宽度
	if (nullptr == root)
	{
		return 0;
	}
	queue<TreeNode*> nodeQueue; //遍历二叉树的辅助队列
	unordered_map< TreeNode*, int> levelMap; //记录每个节点所在层数的哈希表
	//根节点入队列
	nodeQueue.push(root);
	levelMap[root] = 1; //根节点定义为在第1层
	int curLevel = 1; //当前所在层数
	int curWidth = 0; //当前层记录的宽度
	while (!nodeQueue.empty())
	{
		TreeNode* temp = nodeQueue.front();
		//弹出队首节点
		nodeQueue.pop();
		//获取队首节点的层数,如果和上一个队首节点的层数curLevel相同,则curWidth+1;否则,说明已经开始进入下一层,则重置curWidth并更新curLevel
		if (levelMap[temp] == curLevel)
		{
			++curWidth;
		}
		//已经进入下一层
		else
		{
			//在进入下一层结点之前先要保存最大宽度
			widMax = curWidth > widMax ? curWidth : widMax;
			//将宽度置一
			curWidth = 1;
			//重置层数
			curLevel = levelMap[temp];
		}
 
		if (temp->left != NULL)
		{
			nodeQueue.push(temp->left);
			//保存当前节点左孩子的层数:当前层数+1
			levelMap[temp->left] = curLevel + 1;
		}
		if (temp->right != NULL)
		{
			nodeQueue.push(temp->right);
			//保存当前节点右孩子的层数:当前层数+1
			levelMap[temp->right] = curLevel + 1;
		}
	}
	return widMax;
}

我们也可以不使用哈希表来实现这种操作。
思路:
1)进队列
2)cur_end记录最后一个结点
next_end记录每次进队列的结点
curlevel记录每层结点个数
3)每当出来的结点不等cur_end时,curlevel++;
每当出来的结点等于cur_end时,curlevel++,并将next_end赋给cur_end,next_end置为NULL,
max更新与curlevel比较,curlevel置为0

#include<iostream>
#include<stdlib.h> 
#include<deque>  //插入标准库中的头文件
using namespace std;
 
typedef struct treenode
{
	char data;
	treenode *right;
	treenode *left;
}*Node;
 
//创建二叉树
void creat_tree(treenode *&rt)
{
	char ch;
	ch = getchar();
	if ('#' == ch) {
		rt = NULL;
	} else {
		rt = new treenode;
		rt->data = ch;
		creat_tree(rt->left);        //构造左子树
		creat_tree(rt->right);    //构造右子树    
	}
}
 
int noMapWeight(Node* head) {
	//如果没有结点,则返回
	if (head == NULL)
		return 0;
	
	//创建一个队列
	queue<Node*> q;
	q.push(head);//先将头装进去
	Node* cur_end = head; //定义当前层最后一个结点
	Node* next_end = NULL; //定义下一层最后一个节点
	int curlevel = 0, max = 0; //定义每层的节点数以及最大的节点数
	while (!q.empty()) {
		Node* out = q.front(); //取出第一个元素的值
		q.pop();//弹出第一个元素
		if (out->left != NULL) {//将左结点压入,并且下一层的最后一个结点赋值
			q.push(out->left);
			next_end = out->left;
		}	
		if (out->right != NULL) {//将右结点压入,并对下一层最后一个节点赋值
			q.push(out->right);
			next_end = out->right;
		}//如果输出的元素不为当前层最后一个结点,则宽度加一
		if (out != cur_end) {
			curlevel++;
		//否则宽度先加一,然后将将所有变量移动到最后一层,并且暂存最大值
		}else {
			curlevel++;
			cur_end = next_end;
			next_end = NULL;
			max = max < curlevel ? curlevel : max;
			curlevel = 0;
		}
	}
	return max;//返回最大值
}
 
int main() {
	treenode *root = NULL;
	int height, width;  //表示高度和最大宽度
	cout << "请输入二叉树,空值以#代表:" << endl;
	creat_tree(root);        //创建二叉树
	Complete_binary_tree(root,height,width);
	cout << "高度为:" << height << endl;
	cout<<"最大宽度为:" << width << endl;
	system("pause");
	return 0;
}

五、如何判断一颗二叉树是搜索二叉树

搜索二叉树:左侧结点都比头节点小,右侧结点都比头节点大。经典搜索二叉树没有重复值。
在这里插入图片描述
上图即为经典搜索二叉树。从上图可以看出,在中序遍历中,所有结点都是升序的,即为搜索二叉树。如果某一位置是降序的,则该树就不是搜索二叉树。
法一:直接通过递归的方式挨个子树遍历比较。

/*
* 概念:每个结点都比左儿子大,比右儿子小,严格的搜索二叉树没有重复的值
step:   1)中序遍历得到的数组的升序的
*/
int preData = INT_MIN;
bool isBST(Node* head) {
	if (head == NULL)
		return true;
	bool isLeftBST = isBST(head->left);
	if (!isLeftBST)//如果左树不是,就直接返回false
		return false;
	if (preData >= head->data)//如果根的data比左的小,就返回false
		return false;
	else {
		preData = head->data;
	}
	return isBST(head->right);
}

法二:将每次中序遍历的结点全放到list容器中,然后再判断list容器中结点的值是否为升序。

//判断是否为搜索二叉树2
#include<list>
#include<algorithm>
bool isBST2(Node* head) {
	if (head == NULL)
		return false;
	list<int> L;
	process(head, L);
	int sz = L.size();
	list<int>::iterator end = --L.end();
	for (list<int>::iterator it = L.begin();it != end;)
		if (*it >= *(++it))
			return false;
	return true;
}
void process(Node* head, list<int>& L) {
	if (head == NULL)
		return;
	process(head->left, L);
	L.push_back(head->data);
	process(head->right, L);
}

法三:非递归过程中进行比较

bool isBST(node* head){
    if (head == NULL){
        return true;
    }
    stack<node*>newstack;
    int pre = INT_MIN;
    while (!newstack.empty() || head != NULL){
        if (head != NULL){
            newstack.push(head);
            head = head->left;
        }
        else{
            head = newstack.top();
            //cout << head->value << ",";
            if (pre < head->value){
                pre = head->value;
            }else{
                return false;
            }
            newstack.pop();
            head = head->right;
        }
    }
    return true;
}

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

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

相关文章

深度学习之卷积神经网络识别图片验证码实战案例(十)

案例背景&#xff1a;程序自动化的爬虫而无需人工介入是我们的最终目标。自动化爬虫避免不了自动登录的问题&#xff0c;在爬取XX数据的过程中&#xff0c;遇到登录图形验证码的识别的问题&#xff0c;那我们该如何攻破这种验证码呢&#xff1f; 字符验证码图片如下&#xff1a…

facenet, dlib人脸识别,人体检测,云数据库mysql,QQ邮箱,手机验证码,语音播报

目录 部分代码展示&#xff1a; 录入部分 识别部分​编辑 活体检测部分​编辑 同步到云数据库MySQL 其他操作 部分图片展示&#xff1a; 完整代码加ui链接&#xff1a; 涉及到的一些知识点的文章 部分代码展示&#xff1a; 录入部分 识别部分 活体检测部分 同步到云数…

峰终定律原理

峰终定律 峰终定律&#xff08; Peak–End Rule&#xff09;&#xff0c;是由丹尼尔卡尼曼&#xff08;2002年诺贝尔经济学奖获得者&#xff0c;心理学家&#xff09;提出的。 模型介绍 峰终定律是指如果在一段体验的高峰和结尾&#xff0c;体验是愉悦的&#xff0c;那么对整个…

走进机器学习

作者简介&#xff1a;本人是一名大二学生&#xff0c;就读于人工智能专业&#xff0c;学习过c&#xff0c;c&#xff0c;java&#xff0c;python&#xff0c;Mysql等编程知识&#xff0c;现在致力于学习人工智能方面的知识&#xff0c;感谢CSDN让我们相遇&#xff0c;我也会致力…

javaScript蓝桥杯---传送门

目录 一、介绍二、准备三、目标四、代码五、知识点六、完成 一、介绍 日常浏览网页的时候&#xff0c;我们会发现一个问题&#xff0c;当页面太长、内容太多的时候我们很难快速浏览到心仪的内容。为了解决这个烦恼&#xff0c;优秀的产品研发团队发明了一种类似传送门的功能&a…

对比分析:黑盒测试 VS 白盒测试

一、引言 在软件开发过程中&#xff0c;测试是确保产品质量的关键环节。其中&#xff0c;黑盒测试和白盒测试是两种常见的测试方法。本文将详细解析这两种测试方法的定义、特点&#xff0c;同时通过具体示例进行对比分析。 二、黑盒测试 黑盒测试&#xff0c;又称功能测试&…

2023最新性能测试面试题合集含答案,看完拿个20Koffer不是问题

1、描述一下你们公司的性能测试流程&#xff1f; 1&#xff09;分析性能需求&#xff08;用户使用最频繁的场景进行测试&#xff09;确定性能指标&#xff08;例如&#xff1a;事务通过率100%&#xff0c;top99%是5秒&#xff0c;最大并发是2000&#xff0c;CPU和内存都是70%以…

Git教程笔记

概念 Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件&#xff08;Java类、xml文件、html页面等&#xff09;在软件开发过程中被广泛使用。 Git常用命令 Git全局设置 获取Git仓库 工作区、暂存区、版本库 概念 Git工作区中文件的状态 工作区中…

ROS EKF 机器人位姿估计功能包:robot_pose_ekf | 仿真环境实践

ROS EKF 机器人位姿估计功能包&#xff1a;robot_pose_ekf | 仿真环境实践 在仿真下使用robot_pose_ekf 在仿真下使用robot_pose_ekf 仿真环境为 一个无人机&#xff0c;具备3D POSE里程计数据&#xff0c;和imu数据。 将robot_pose_ekf.launch文件进行如下更改 <launc…

C++ unordered_map 性能优化

一 插入加速 unordered_map 在桶满时自动进行 rehash 操作。手动调用 rehash 函数可以手动调整 桶数量。 rehash 函数被调用时&#xff0c;需要注意以下几点&#xff1a; rehash 函数可能会造成 unordered_map 的迭代器失效。如果我们在重新哈希后仍需要继续迭代 unordered_…

机器学习实战六步法之数据预处理(五)

要落地一个机器学习的项目&#xff0c;是有章可循的&#xff0c;通过这六个步骤&#xff0c;小白也能搞定机器学习。 看我闪电六连鞭&#xff01;&#x1f923; 数据的预处理通常包括 5 个步骤&#xff0c;如下&#xff1a;这个是比较完整的一个步骤&#xff0c;不同的算法可能…

内网隧道代理技术(一)之内网隧道代理概述

内网隧道代理技术 内网转发 在渗透测试中&#xff0c;当我们获得了外网服务器&#xff08;如web服务器&#xff0c;ftp服务器&#xff0c;mali服务器等等&#xff09;的一定权限后发现这台服务器可以直接或者间接的访问内网。此时渗透测试进入后渗透阶段&#xff0c;一般情况…

FreeRTOS(8)----任务通知

一&#xff0c;任务通知的简介 相对于之前的信号量&#xff0c;事件组等&#xff0c;所谓的任务通知核心就是一个32位的无符号整数和8位的通知状态 任务通知可以通过如下方法更新接收任务的通知值&#xff1a; ● 不覆盖接收任务的通知值 ( 如果上次发送给接收任务的通知还没…

MyBatisPlus总结(1.0)

MyBatis-Plus MyBatis-Plus介绍 MyBatis-Plus&#xff08;简称MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生 特性 无侵入&#xff1a;只做增强不做改变&#xff0c;引入它不会对现有工程产生影…

为何波卡被称为Layer 0?

理解区块链的技术本质&#xff0c;将揭示加密货币运行轨迹的神秘面纱。了解这背后的原理&#xff0c;将为你带来全新的视角&#xff0c;让你对加密货币的奇妙世界充满无尽的好奇。 波卡是一个内部互连的区块链平台&#xff0c;被赋予技术堆栈元协议或Layer 0的定义&#xff0c…

Golang 基础案例集合:中文拼音转换、解析二维码、压缩 zip、执行定时任务

前言 曾经&#xff0c;因为不够注重基础吃了好多亏。总是很喜欢去看那些高大上的东西&#xff0c;却忽略了最基本的东西。然后会错误的以为自己懂的很多&#xff0c;但是其实是沙堆中筑高台&#xff0c;知道很多高大上的架构&#xff0c;但是基础的东西却不太了解。我觉得&…

PySpark实战指南:大数据处理与分析的终极指南【上进小菜猪大数据】

上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货。 大数据处理与分析是当今信息时代的核心任务之一。本文将介绍如何使用PySpark&#xff08;Python的Spark API&#xff09;进行大数据处理和分析的实战技术。我们将探讨PySpark的基本…

P2[1-2]STM32简介(stm32简介+ARM介绍+片上外设+命名规则+系统结构+引脚定义+启动配置+最小系统电路+实物图介绍)

1.1 stm32简介 解释:ARM是核心部分,程序的内核,加减乘除的计算,都是在ARM内完成。 智能车方向:循迹小车,读取光电传感器或者摄像头数据,驱动电机前进和转弯。 无人机:用STM32读取陀螺仪加速度计的姿态数据,根据控制算法控制电机的速度,从而保证飞机稳定飞行。 机…

maven的pom文件

maven项目中会有pom文件&#xff0c; 当新建项目时候&#xff0c; 需要添加我们需要的依赖包。所以整理了一份比较常用的依赖包的pom&#xff0c;方便以后新建项目或者添加依赖包时copy且快捷。不需要的依赖可以删掉&#xff0c;避免首次远程拉取失败和缩小项目打包大小。 <…

爆料,华为重回深圳,深圳第二个硅谷来了-龙华九龙山未来可期

房地产最重要的决定因素&#xff1a;科技等高附加值产业&#xff01;过去几年&#xff0c;发生的最大的变化就是——科技巨头对全球经济的影响力越来越大&#xff0c;中美之间的博弈&#xff0c;由贸易战升级为科技战&#xff0c;就是基于此原因。人工智能、电子信息技术产业、…