C++笔记---二叉搜索树

1. 二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

• 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值。

• 若它的右子树不为空,则右子树上所有结点的值都大于等于根结点的值。

• 它的左右子树也分别为二叉搜索树。

• 二叉搜索树中可以支持插入相等的值,也可以不支持插入相等的值,具体看使用场景定义,后续我们学习map/set/multimap/multiset系列容器底层就是二叉搜索树,其中map/set不支持插入相等值,multimap/multiset支持插入相等值。

除了支不支持插入相同的值以外,二叉搜索树还可以按照其带的值分为两类:key和key/value。

key:只带一个值key

key/value:带两个相绑定的值,构建二叉树与进行查找时都以key为准。 

这两种搜索二叉树的实现大同小异,接下来的介绍中我们以key类型为例,key/value类型的只需要在结点的结构中加上value即可(函数随之微调)。

2. 二叉搜索树的性能分析

二叉搜索树查找的时间复杂度与其高度相同。

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其高度为: O(log_{2}N)

最差情况下,二叉搜索树退化为单支树(或者类似单支),其高度为: O(N/2)

所以综合而言二叉搜索树增删查改时间复杂度为: O(N)

那么这样的效率显然是无法满足我们需求的,为了使二叉搜索树的搜索效率足够高,我们需要使得二叉搜索树尽可能地接近完全二叉树,于是就有了我们之后会讲到的平衡二叉搜索树,以及AVL树和红黑树等。

另外需要说明的是,二分查找也可以实现 O(logN) 级别的查找效率,但是二分查找有两大缺陷:

1. 需要存储在支持下标随机访问的结构中,并且有序。

2. 插入和删除数据效率很低,因为存储在下标随机访问的结构中,插入和删除数据一般需要挪动数据。这里也就体现出了平衡二叉搜索树的价值。

 3. 二叉搜索树的实现

3.1 结点定义及二叉搜索树类的框架

template<class K>
struct BSTreeNode
{
	K _key;
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;

	BSTreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
    //强制生成默认构造
	BSTree() = default;
    //拷贝构造
	BSTree(const BSTree& tree);
    //析构
	~BSTree();
    //赋值重载
	BSTree& operator=(BSTree tree);
    //插入
	bool Insert(const K& key);
	//查找
	Node* Find(const K& key);
    //删除
	bool Erase(const K& key);
	//中序遍历
	void InOrder();

private:
    //辅助拷贝构造进行递归拷贝
	Node* Construct(Node* root);
    //辅助析构函数进行递归释放结点
	void Destroy(Node* root);
    //辅助进行递归中序遍历
	void _InOrder(Node* root);

private:
	Node* _root = nullptr;
};

3.2 默认成员函数

3.2.1 构造函数

由于我们给了_root缺省值,所以编译器自动生成的默认构造函数就够用。

而拷贝构造需要我们自己实现深拷贝,即访问被拷贝的树的全部结点并拷贝链接。

对于树形结构,访问一棵树的全部结点,递归永远是最方便的选择。可是,如何在类内部设计递归函数呢?毕竟需要直接将树结点的指针作为参数才好实现递归。

这时,我们就可以选择在递归函数的外面套一层成员函数的壳

BSTree() = default;

BSTree(const BSTree& tree)
{
	_root = Construct(tree._root);
}
Node* Construct(Node* root)
{
	if (root == nullptr)
		return nullptr;

	Node* copy = new Node(root->_key);
	copy->_left = Construct(root->_left);
	copy->_right = Construct(root->_right);
	return copy;
}
3.2.2 赋值重载

有了拷贝构造,就可以考虑现代写法:

BSTree& operator=(BSTree tree)
{
	swap(_root, tree._root);
	return *this;
}
3.2.3 析构函数

递归访问每个结点并释放,同理需要套一层外壳:

~BSTree()
{
	Destroy(_root);
	_root = nullptr;
}
void Destroy(Node* root)
{
	if (root == nullptr)
		return;

	Destroy(root->_left);
	Destroy(root->_right);
	delete root;
}

3.3 二叉搜索树的插入

1. 树为空,则直接新增结点,赋值给root指针。

2. 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位置,插入新结点。

3. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑一致性,插入相等的值不要一会往右走,一会往左走)

下面的代码是不支持插入相等的值的情形: 

bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	
	Node* cur = _root, *parent = nullptr;
	while (cur)
	{
		parent = cur;
		if (key < cur->_key)
			cur = cur->_left;
		else if (key > cur->_key)
			cur = cur->_right;
		else
			return false;
	}

	cur = new Node(key);
	if (key < parent->_key)
		parent->_left = cur;
	else
		parent->_right = cur;

	return true;
}

 3.4 二叉搜索树的查找

1. 从根开始比较,查找x,x比根的值大则往右边走查找,x比根值小则往左边走查找。

2. 最多查找高度次,走到到空,还没找到,这个值不存在。

3. 如果不支持插入相等的值,找到x即可返回。

4. 如果支持插入相等的值,意味着有多个x存在,一般要求查找中序的第一个x。如下图,查找3,要找到1的右孩子的那个3返回。

个人认为,如果支持插入相同的值,最好是把相同的值插入到右子树。这样一来,按照前序遍历找到的第一个x就是中序遍历的第一个x(两种遍历方式都是最后查找右子树)。

 3.5 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回false。

如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为N)

1. 要删除结点N左右孩子均为空

2. 要删除的结点N左孩子位空,右孩子结点不为空

3. 要删除的结点N右孩子位空,左孩子结点不为空

4. 要删除的结点N左右孩子结点均不为空

对应以上四种情况的解决方案:

1. 把N结点的父亲对应孩子指针指向空,直接删除N结点(情况1可以当成2或者3处理,效果是一样的)

2. 把N结点的父亲对应孩子指针指向N的右孩子,直接删除N结点

3. 把N结点的父亲对应孩子指针指向N的左孩子,直接删除N结点

4. 无法直接删除N结点,因为N的两个孩子无处安放,只能用替换法删除。找N左子树的值最大结点LR(最右结点)或者N右子树的值最小结点RL(最左结点)替代N,因为这两个结点中任意一个,放到N的位置,都满足二叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转而变成删除R结点,R结点符合情况2或情况3,可以直接删除。

另外,在每种情况中,都要注意判断被删除的结点是否为根结点_root,因为在删除的过程中需要记录被删除结点的父节点,而根节点没有父节点,在通用逻辑下会导致对空指针的访问。

bool Erase(const K& key)
{
	Node* del = _root, *parent = nullptr;
	while (del && del->_key != key)
	{
		parent = del;
		if (key < del->_key)
			del = del->_left;
		else
			del = del->_right;
	}
	if (del == nullptr)
		return false;

	if (del->_left == nullptr)//左结点为空(或者左右都为空)
	{
		if (parent == nullptr)//被删除的是根结点
		{
			_root = del->_right;
		}
		else if (del->_key < parent->_key)
			parent->_left = del->_right;
		else
			parent->_right = del->_right;

		delete del;
	}
	else if (del->_right == nullptr)//右结点为空且左节点不为空
	{
		if (parent == nullptr)//被删除的是根结点
		{
			_root = del->_left;
		}
		else if (del->_key < parent->_key)
			parent->_left = del->_left;
		else
			parent->_right = del->_left;

		delete del;
	}
	else//左右结点都不为空
	{
		Node* replace = del->_right;
		parent = del;
		while (replace->_left)
		{
			parent = replace;
			replace = replace->_left;
		}
		del->_key = replace->_key;
		del->_value = replace->_value;
		if (parent->_right == replace)
			parent->_right = replace->_right;
		else
			parent->_left = replace->_right;
		delete replace;
	}

	return true;
}

3.6 完整代码

template<class K>
struct BSTreeNode
{
	K _key;
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;

	BSTreeNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	BSTree() = default;

	BSTree(const BSTree& tree)
	{
		_root = Construct(tree._root);
	}

	~BSTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	BSTree& operator=(BSTree tree)
	{
		swap(_root, tree._root);
		return *this;
	}

	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		
		Node* cur = _root, *parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (key < cur->_key)
				cur = cur->_left;
			else if (key > cur->_key)
				cur = cur->_right;
			else
				return false;
		}

		cur = new Node(key);
		if (key < parent->_key)
			parent->_left = cur;
		else
			parent->_right = cur;

		return true;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key == cur->_key)
				return cur;
			else if (key < cur->_key)
				cur = cur->_left;
			else
				cur = cur->_right;
		}

		return nullptr;
	}

	bool Erase(const K& key)
	{
		Node* del = _root, *parent = nullptr;
		while (del && del->_key != key)
		{
			parent = del;
			if (key < del->_key)
				del = del->_left;
			else
				del = del->_right;
		}
		if (del == nullptr)
			return false;

		if (del->_left == nullptr)
		{
			if (parent == nullptr)
			{
				_root = del->_right;
			}
			else if (del->_key < parent->_key)
				parent->_left = del->_right;
			else
				parent->_right = del->_right;

			delete del;
		}
		else if (del->_right == nullptr)
		{
			if (parent == nullptr)
			{
				_root = del->_left;
			}
			else if (del->_key < parent->_key)
				parent->_left = del->_left;
			else
				parent->_right = del->_left;

			delete del;
		}
		else
		{
			Node* replace = del->_right;
			parent = del;
			while (replace->_left)
			{
				parent = replace;
				replace = replace->_left;
			}
			del->_key = replace->_key;
			del->_value = replace->_value;
			if (parent->_right == replace)
				parent->_right = replace->_right;
			else
				parent->_left = replace->_right;
			delete replace;
		}

		return true;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	Node* Construct(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* copy = new Node(root->_key);
		copy->_left = Construct(root->_left);
		copy->_right = Construct(root->_right);
		return copy;
	}

	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

private:
	Node* _root = nullptr;
};

4. 二叉搜索树key和key/value的使用场景

4.1 key搜索场景

只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。

key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,因为修改key会破坏搜索树结构。

场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进入。


场景2:检查一篇英文文章单词拼写是否正确,将词库中所有单词放入二叉搜索树,读取文章中的单词,查找是否在二叉搜索树中,不在则波浪线标红提示。

4.2 key/value搜索场景

每一个关键码key,都有与之对应的值value,value可以任意类型对象。

树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。

key/value的搜索场景实现的二叉树搜索树支持修改,但是同样不支持修改key,可以修改value。

场景1:简单中英互译字典,树的结构中(结点)存储key(英文)和vlaue(中文),搜索时输入英文,则同时查找到了英文对应的中文。

场景2:商场无人值守车库,入口进场时扫描车牌,记录车牌(key)和入场时间(value),出口离场时,扫描车牌,查找入场时间,用当前时间-入场时间计算出停车时长,计算出停车费用,缴费后抬杆,车辆离场。

场景3:统计一篇文章中单词(key)出现的次数(value),读取一个单词,查找单词是否存在,不存在这个说明第一次出现,(单词,1),单词存在,则++单词对应的次数。

void TestBSTree()
{
    // 四个单词的简单词典
	BSTree<string, string> dict;
	dict.Insert("insert", "插入");
	dict.Insert("erase", "删除");
	dict.Insert("left", "左边");
	dict.Insert("string", "字符串");

	string str;
	while (cin >> str)
	{
		auto ret = dict.Find(str);
		if (ret)
		{
			cout << str << ":" << ret->_value << endl;
		}
		else
		{
			cout << "单词拼写错误" << endl;
		}
	}


    // 统计水果出现次数
	string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
	// 统计水果出现的次
	BSTree<string, int> countTree;
	for (auto str : strs)
	{
		auto ret = countTree.Find(str);
		if (ret == nullptr)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_value++;
		}
	}

	countTree.InOrder();
}

 

 4.3 key/value版本二叉搜索树

template<class K, class V>
struct BSTreeNode
{
	K _key;
	V _value;
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;

	BSTreeNode(const K& key, const V& value)
		:_key(key)
		,_value(value)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	BSTree() = default;

	BSTree(const BSTree& tree)
	{
		_root = Construct(tree._root);
	}

	~BSTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	BSTree& operator=(BSTree tree)
	{
		swap(_root, tree._root);
		return *this;
	}

	bool Insert(const K& key, const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node(key, value);
			return true;
		}
		
		Node* cur = _root, *parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (key < cur->_key)
				cur = cur->_left;
			else if (key > cur->_key)
				cur = cur->_right;
			else
				return false;
		}

		cur = new Node(key, value);
		if (key < parent->_key)
			parent->_left = cur;
		else
			parent->_right = cur;

		return true;
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key == cur->_key)
				return cur;
			else if (key < cur->_key)
				cur = cur->_left;
			else
				cur = cur->_right;
		}

		return nullptr;
	}

	bool Erase(const K& key)
	{
		Node* del = _root, *parent = nullptr;
		while (del && del->_key != key)
		{
			parent = del;
			if (key < del->_key)
				del = del->_left;
			else
				del = del->_right;
		}
		if (del == nullptr)
			return false;

		if (del->_left == nullptr)
		{
			if (parent == nullptr)
			{
				_root = del->_right;
			}
			else if (del->_key < parent->_key)
				parent->_left = del->_right;
			else
				parent->_right = del->_right;

			delete del;
		}
		else if (del->_right == nullptr)
		{
			if (parent == nullptr)
			{
				_root = del->_left;
			}
			else if (del->_key < parent->_key)
				parent->_left = del->_left;
			else
				parent->_right = del->_left;

			delete del;
		}
		else
		{
			Node* replace = del->_right;
			parent = del;
			while (replace->_left)
			{
				parent = replace;
				replace = replace->_left;
			}
			del->_key = replace->_key;
			del->_value = replace->_value;
			if (parent->_right == replace)
				parent->_right = replace->_right;
			else
				parent->_left = replace->_right;
			delete replace;
		}

		return true;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	Node* Construct(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* copy = new Node(root->_key, root->_value);
		copy->_left = Construct(root->_left);
		copy->_right = Construct(root->_right);
		return copy;
	}

	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_key << ":" << root->_value << " ";
		_InOrder(root->_right);
	}

private:
	Node* _root = nullptr;
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构—双向链表

结构 带头链表里的头结点&#xff0c;实际为“哨兵位”&#xff0c;哨兵位结点不存储任何有效元素&#xff0c;只是站在这里“放哨 的” 实现双向链表 List.h #pragma once#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool…

“RISCV+AI”

概述 设计方案 主要有两种设计方案。 RISCV核ASIC RISCV核是标准的基于RISCV指令集的CPU设计&#xff0c;ASIC部分通常是基于RISCV自带的向量扩展指令集构建的向量处理器&#xff0c;或是自定义的矩阵计算单元。 根据CPUAI ASIC部件的接口可以分为紧耦合和松耦合的设计1。 …

基于python+django+vue的学生管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于协同过滤pythondjangovue…

【Python笔记】PyCharm大模型项目环境配置

一、PyCharm创建新项目 二、更新pip版本 ...>python.exe -m pip install --upgrade pip 三、生成所需requirements配置文件 ...>pip freeze > requirements.txt 四、安装所需组件requirements.txt ...>pip install -r requirements.txt

【Kubernetes】linux centos安装部署Kubernetes集群

【Kubernetes】centos安装Kubernetes集群 1、环境准备 系统centos7 配置yum源参考文章 Centos系统换yum源 yum -y update 步骤1-3是所有主机都要配置&#xff0c;主机名和hosts配置完后可以使用工具命令同步 1.1 主机 一主二从 主机名ipk8smaster192.168.59.148k8snode11…

Node.js 安装及项目实践

node.js安装 node安装&#xff0c;选择版本 一直next&#xff0c;或者自己修改路径&#xff0c;添加两个包 选择自己的安装的node的路径&#xff0c;cmd或者winr cmd 显示node与npm的版本号 node -vnpm -v可以跟着这个博客将node安装 2024最新版Node.js下载安装及环境配…

ZW3D二次开发_UI_非模板表单_设置表单显示位置

1.ZW3D弹出非模板表单时可以设置弹出位置&#xff08;居中、左下角、右上角等&#xff09; 2.假设已创建好非模板表单 3.在Form属性中添加form_pos属性 4.输入值 base,CTR,0.0 &#xff0c;如下图 也可以设置为其他值显示在不同的位置&#xff0c;如下 5.重新编译&#xff0c;…

新升级|优化航拍/倾斜模型好消息,支持处理多套贴图模型!

【天元轻量化软件】一直在不断地追求进步和完善&#xff0c;以满足更多用户的各种需求。 电脑登录天元官网免费体验&#xff1a;天元轻量化软件官网 本次我们对“智能PBR”功能进行了更新。更新后的“智能PBR”支持带多套贴图的模型进行使用。 本轮更新后&#xff0c;主要受益…

防火墙--NAT技术,基于源NAT,NAT服务器,双向NAT

文章目录 防火墙--NAT技术一、基于源NAT**方式**&#xff1a;NAT No-PATNAPT出接口地址方式Smart NAT三元组 NAT 二、基于服务器的NAT多出口场景下的NAT Server 三、双向NAT 防火墙–NAT技术 基于源NAT&#xff1a;用于将内部网络的私有IP地址转换为公共IP地址&#xff0c;以便…

51单片机应用开发---数码管的控制应用

实现目标 1、掌握数码管结构、驱动原理及应用&#xff1b; 2、掌握数码管段码表推导&#xff1b; 3、会编程让开发板8个数码管动态显示。 一、什么是数码管&#xff1f; 1.数码管定义 数码管&#xff0c;也称为LED数码管&#xff0c;基本单元是发光二极管(LED)。分为七段数…

【机器学习】--- 自监督学习

1. 引言 机器学习近年来的发展迅猛&#xff0c;许多领域都在不断产生新的突破。在监督学习和无监督学习之外&#xff0c;自监督学习&#xff08;Self-Supervised Learning, SSL&#xff09;作为一种新兴的学习范式&#xff0c;逐渐成为机器学习研究的热门话题之一。自监督学习…

某思CMS V10存在SQL注入漏洞

Fofa: product"魅思-视频管理系统" 框架:ThinkPHP 5,6 1 漏洞分析&复现 位于 /controller/Api.php 控制器中的getOrderStatus 方法POST传入&#xff0c;然后直接拼接了 orderSn 变量到 where 查询中&#xff0c;导致漏洞产生. /** * 查询订单支付状态 */ pub…

服务器——装新的CUDA版本的方法

服务器——装新的CUDA版本 一、进入 CUDA 版本列表二、根据自己服务器&#xff0c;选择对应的版本和配置三、使用管理员用户&#xff0c;运行下载和安装命令四、查看显卡驱动是否安装4.1 若安装了显卡驱动4.2 若显卡驱动没安装 参考文章 一、进入 CUDA 版本列表 CUDA Toolkit …

sqlgun靶场攻略

步骤一&#xff1a;打开页面 步骤二&#xff1a;测试回显点 -1union select 1,2,3# 步骤三&#xff1a;查看数据库名 -1union select 1,2,database()# 步骤四&#xff1a;查看表名 -1union select 1,2,group_concat(table_name) from information_schema.tables where table…

Double Write

优质博文&#xff1a;IT-BLOG-CN 一、存在的问题 为什么需要Double Write&#xff1a; InnoDB的PageSize是16kb&#xff0c;其数据校验也是针对这16KB来计算的&#xff0c;将数据写入磁盘是以Page为单位的进行操作的。而计算机硬件和操作系统&#xff0c;写文件是以4KB作为基…

ASR(自动语音识别)识别文本效果的打分总结

ASR(自动语音识别)识别文本效果的打分总结 1. 词错误率(WER, Word Error Rate)2. 字正确率(W.Corr, Word Correct)3. 编辑距离(Edit Distance)4. 特定错误率5. 句子错误率(SER, Sentence Error Rate)6. 基于模型的评估方法对于ASR(自动语音识别)识别文本效果的打分…

SHT30温湿度传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.工作原理 三、程序设计 main.c文件 sht30.h文件 sht30.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 SHT30是一种常见的温湿度传感器&#xff0c;是一款完全校准的线性化的温湿度数字传感器&#xff0…

C++初阶:STL详解(三)——vector的介绍和使用

✨✨小新课堂开课了&#xff0c;欢迎欢迎~✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C&#xff1a;由浅入深篇 小新的主页&#xff1a;编程版小新-CSDN博客 前言&#xff1a; 前面我们刚刚了解了strin…

Jetpack Compose Side Effects in Details 副作用的详细信息

What is Side Effect’s? 副作用是什么&#xff1f; Side Effects is a change in the state of the application that occurs outside the scope of the composable function and is not related to the UI. In non-UI related state changes, our screen may recompose mor…

【C语言零基础入门篇 - 6】:数组、字符和字符串带你探索无限可能

文章目录 数组一维数组一维数组的定义一维数组的初始化 字符数组二维数组二维数组存汉字 字符串相关函数小结 数组 特点&#xff1a; 在同一个数组中&#xff0c;所有元素都是同一个类型。可以是int、char、float、double等类型。数组是一种构造类型&#xff0c;是一批数据的…