【C++标准模版库】模拟实现vector+迭代器失效问题

news2024/9/25 19:18:24

模拟实现vector

  • 一.vector成员变量
  • 二.构造函数
    • 1.无参(默认)构造
    • 2.有参构造
    • 3.拷贝构造
      • 1.传统写法
      • 2.现代写法
  • 三.vector对象的容量操作
    • 1.size
    • 2.capacity
    • 3.clear
    • 4.empty
    • 5.reserve
    • 6.resize
  • 四.vector对象的访问及遍历操作
    • 1.operator[]
    • 2.实现迭代器:begin+end
  • 五.vector对象的增删查改操作
    • 1.operator
      • 1.传统写法
      • 2.现代写法
    • 2.push_back
    • 3.pop_back
    • 4.insert
      • 迭代器失效:类似野指针
      • 迭代器失效:位置意义改变
    • 5.erase
  • 六.源代码
    • 1.vector.h

一.vector成员变量

成员变量:

  1. iterator _start:指向vector的起始位置。
  2. iterator _finish:指向vector的最后一个有效数据的下一个位置。
  3. iterator _end_of_storage:指向vector可容纳最大数据个数的下一个位置。

大体结构如下:

在这里插入图片描述

namespace xzy
{
	//有模版不能分离到.h与.cpp文件,否则报链接错误
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
}

二.构造函数

1.无参(默认)构造

类内函数拷贝构造,系统就不再提供默认构造,无法vector<int> v; 需要自己提供默认构造,C++11中vector() = default; 强制生成默认构造。

//C++11 强制生成默认构造
//vector() = default;

vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{}

2.有参构造

  1. 迭代器区间构造:例如用list对象的迭代器区间构造vector对象。作用:例如由于list对象排序性能较低,利用vector的迭代器区间初始化list对象中相同的数据进行排序,可以提高性能。
//类模版的成员函数可以是函数模版
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
int main()
{
	list<int> l1(10, 1); //数据类型要求匹配,例如:都是int
	vector<int> v1(l1.begin(), l1.end());
}
  1. n个值为val初始化:
vector(size_t n, const T& val = T())
{
	reserve(n);
	for (size_t i = 0; i < n; i++)
	{
		push_back(val);
	}
}

在这里插入图片描述

解决办法:

在这里插入图片描述

3.拷贝构造

1.传统写法

vector(const vector<T>& v)
{
	_start = new T[v.capacity()];
	//memmove(_start, v._start, v.size() * sizeof(T));
	for (size_t i = 0; i < v.size(); i++)
	{
		_start[i] = v._start[i];
	}
	_finish = _start + v.size();
	_end_of_storage = _start + v.capacity();
}

更好的写法:遍历vector进行尾插。

vector(const vector<T>& v)
{
	reserve(v.size());
	for (auto& e : v)
	{
		push_back(e);
	}
}

2.现代写法

void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_end_of_storage, v._end_of_storage);
}

vector(const vector<T>& v)
{
	vector<T> tmp(v.begin(), v.end());
	swap(tmp);
}

三.vector对象的容量操作

1.size

size_t size() const
{
	return _finish - _start;
}

2.capacity

size_t capacity() const
{
	return _end_of_storage - _start;
}

3.clear

void clear()
{
	_finish = _start;
}

4.empty

bool empty() const
{
	return _start == _finish;
}

5.reserve

扩容时:先开辟新空间,再将旧空间拷贝到空间,释放旧空间,最后修改数据。

但是这里存在一个坑,如下:

在这里插入图片描述

初始_start 和 _finish 为缺省值 nullptr。
_finish = _start + size() = _start + _finish - _start = _finish = nullptr——>程序崩溃。

正确代码如下:

void reserve(size_t n)
{
	//第一种解决方法
	//if (n > capacity())
	//{
	//	T* tmp = new T[n];
	//	memmove(tmp, _start, sizeof(T) * size());
	//	delete[] _start;
	//	_finish = tmp + size();
	//	_start = tmp;
	//	_end_of_storage = _start + n;
	//}

	//第二种解决方法
	//if (n > capacity())
	//{
	//	size_t old_size = size();
	//	T* tmp = new T[n];
	//	memmove(tmp, _start, sizeof(T) * size());
	//	delete[] _start;
	//	_start = tmp;
	//	_finish = _start + old_size;
	//	_end_of_storage = _start + n;
	//}

	//第三种解决方法
	if (n > capacity())
	{
		//先保存有效数据个数
		size_t old_size = size();
		//开空间
		T* tmp = new T[n];
		//拷贝数据
		for (size_t i = 0; i < old_size; i++)
		{
			tmp[i] = _start[i];
		}
		//释放旧空间
		delete[] _start;
		//更新数据
		_start = tmp;
		_finish = _start + old_size;
		_end_of_storage = _start + n;
	}
}

但是第一种与第二种方法在某些vector容器数据为自定义类型时存在浅拷贝问题如下图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
同理:拷贝构造使用的memmove也存在此问题。

6.resize

修改有效数据的个数时:若传入的参数小于有效数据的个数:删除数据即修改_finish即可;若传入的参数大于有效数据的个数:插入数据前考虑是否扩容。

void resize(size_t n, const T& val = T())
{
	if (n < size())
	{
		_finish = _start + n;
	}
	else
	{
		reserve(n);
		while (_finish < _start + n)
		{
			*_finish = val;
			++_finish;
		}
	}
}

在这里插入图片描述

四.vector对象的访问及遍历操作

1.operator[]

T& operator[](size_t i)
{
	assert(i >= 0 && i < size());
	return _start[i];
}

const T& operator[](size_t i) const
{
	assert(i >= 0 && i < size());
	return _start[i];
}

2.实现迭代器:begin+end

typedef T* iterator;
typedef const T* const_iterator;

iterator begin()
{
	return _start;
}
iterator end()
{
	return _finish;
}

const_iterator begin() const
{
	return _start;
}
const_iterator end() const
{
	return _finish;
}

五.vector对象的增删查改操作

1.operator

1.传统写法

vector<T>& operator=(const vector<T>& v)
{
	if (this != &v)
	{
		delete[] _start;
		
		_start = new T[v.capacity()];
		//memcpy(_start, v._start, v.size() * sizeof(T)); //当vector<string>时存在问题
		for(size_t i = 0; i < v.size(); i++)
		{
			_start[i] = v._start[i];
		}
		_finish = _start + v.size();
		_end_of_storage = _start + v.capacity();
	}
	return *this;
}

更好的写法:遍历vector进行尾插。

vector<T>& operator=(const vector<T>& v)
{
	if (this != &v)
	{
		clear();
		reserve(v.size());
		for (auto& e : v)
		{
			push_back(e);
		}
	}
	return *this;
}

2.现代写法

void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_end_of_storage, v._end_of_storage);
}

//类内可以用类名替代类型:vector& operator=(vector tmp)
vector<T>& operator=(vector<T> tmp)
{
	swap(tmp);
	return *this;
}

2.push_back

尾插时:先检查容量,再进行尾插。

void push_back(const T& x)
{
	//容量满了——>扩容
	if (_finish == _end_of_storage)
	{
		reserve(capacity() == 0 ? 4 : 2 * capacity());
	}
	//尾插
	*_finish = x;
	++_finish;
}

3.pop_back

尾删时:先检查是否有有效数据,再进行尾删。

void pop_back()
{
	assert(!empty());
	--_finish;
}

4.insert

插入时:先检查容量,再整体右移一位,最后插入。

迭代器失效:类似野指针

实现插入操作有一个坑造成迭代器失效问题,如下图:

在这里插入图片描述

修改后的代码能解决上面的问题,但是又存在另一个迭代器失效的问题,如下图:

在这里插入图片描述

迭代器失效:位置意义改变

在这里插入图片描述

由于不知道insert函数内是否存在扩容,不能确保pos是否为野指针,解决方法:可以在insert函数中返回pos用于接受。代码如下:

iterator insert(iterator pos, const T& x)
{
	assert(pos >= _start);
	assert(pos <= _finish);
	
	//容量满了——>扩容
	if (_finish == _end_of_storage)
	{
		//记录pos的相对位置防止迭代器失效
		size_t len = pos - _start;
		reserve(capacity() == 0 ? 4 : 2 * capacity());
		pos = _start + len;
	}
	//整体后移一位
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}
	//插入数据
	*pos = x;
	++_finish;
	
	return pos;
}

5.erase

同理erase也会遇到的迭代器失效:位置意义改变,在vs2022中的vector(SLT)不接收erase的返回值强行访问,程序崩溃。接收erase的返回值访问,程序正常运行。

iterator erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos < _finish);
	
	iterator it = pos + 1;
	while (it != end())
	{
		*(it - 1) = *it;
		++it;
	}
	--_finish;
	
	return pos;
}

实现删除vector中的偶数:

int main()
{
	std::vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

	auto it = v1.begin();
	while (it != v1.end())
	{
		if (*it % 2 == 0)
		{
			it = v1.erase(it);
		}
		else
		{
			it++;
		}
	}
	return 0;
}

六.源代码

1.vector.h

//#pragma once

#ifndef __VECTOR_H__
#define __VECTOR_H__

#include<iostream>
#include<assert.h>
using namespace std;

namespace xzy
{
	//有模版不能分离到.h与.cpp文件,否则报链接错误
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		//C++11 强制生成默认构造
		//vector() = default;

		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _end_of_storage(nullptr)
		{}

		vector(const vector<T>& v)
		{
			_start = new T[v.capacity()];
			//memmove(_start, v._start, v.size() * sizeof(T)); //当vector<string>时存在问题
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}

		/*vector(const vector<T>& v)
		{
			reserve(v.size());
			for (auto& e : v)
			{
				push_back(e);
			}
		}*/

		//类模版的成员函数可以是函数模版
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		void clear()
		{
			_finish = _start;
		}

		//vector<T>& operator=(const vector<T>& v)
		//{
		//	if (this != &v)
		//	{
		//		delete[] _start;
		//		_start = new T[v.capacity()];
		//		//memcpy(_start, v._start, v.size() * sizeof(T));//当vector<string>时存在问题
		//      for(size_t i = 0; i < v.size(); i++)
		//        {
		//			  _start[i] = v._start[i];
		//        }
		//		_finish = _start + v.size();
		//		_end_of_storage = _start + v.capacity();
		//	}
		//	return *this;
		//}

		//vector<T>& operator=(const vector<T>& v)
		//{
		//	if (this != &v)
		//	{
		//		clear();
		//		reserve(v.size());
		//		for (auto& e : v)
		//		{
		//			push_back(e);
		//		}
		//	}
		//	return *this;
		//}

		void swap(vector<int>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

		类内可以用类名替代类型:vector& operator=(vector tmp)
		vector<T>& operator=(vector<T> tmp)
		{
			swap(tmp);
			return *this;
		}

		~vector()
		{
			if (_start != nullptr)
			{
				delete[] _start;
			}
			_start = _finish = _end_of_storage;
		}

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		//以下有一个坑
		//void reserve(size_t n)
		//{
		//	if (n > capacity())
		//	{
		//		//开空间
		//		T* tmp = new T[n];
		//		//拷贝数据
		//		memmove(tmp, _start, sizeof(T) * size());
		//		//释放旧空间
		//		delete[] _start;
		//		//更新数据
		//		_start = tmp;
		//		_finish = _start + size(); 注意:size()已经不再是有效数据个数了,可以通过调试观察
		//		_end_of_storage = _start + n;
		//	}
		//}

		void reserve(size_t n)
		{
			//第一种解决方法:依旧存在错误
			//if (n > capacity())
			//{
			//	T* tmp = new T[n];
			//	memmove(tmp, _start, sizeof(T) * size());
			//	delete[] _start;
			//	_finish = tmp + size();
			//	_start = tmp;
			//	_end_of_storage = _start + n;
			//}

			//第二种解决方法:依旧存在错误
			/*if (n > capacity())
			{
				size_t old_size = size();
				T* tmp = new T[n];
				memmove(tmp, _start, sizeof(T) * size());
				delete[] _start;
				_start = tmp;
				_finish = _start + old_size;
				_end_of_storage = _start + n;
			}*/

			//第三种解决方法
			if (n > capacity())
			{
				//先保存有效数据个数
				size_t old_size = size();
				//开空间
				T* tmp = new T[n];
				//拷贝数据
				for (size_t i = 0; i < old_size; i++)
				{
					tmp[i] = _start[i];
				}
				//释放旧空间
				delete[] _start;
				//更新数据
				_start = tmp;
				_finish = _start + old_size;
				_end_of_storage = _start + n;
			}
		}

		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		size_t size() const
		{
			return _finish - _start;
		}

		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

		void push_back(const T& x)
		{
			//容量满了——>扩容
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}
			//尾插
			*_finish = x;
			++_finish;
		}

		bool empty()
		{
			return _start == _finish;
		}

		void pop_back()
		{
			assert(!empty());
			--_finish;
		}

		//void insert(iterator pos, const T& x)
		//{
		//	//容量满了——>扩容
		//	if (_finish == _end_of_storage)
		//	{
		//		reserve(capacity() == 0 ? 4 : 2 * capacity());
		//	}
		//	//整体后移一位
		//	iterator end = _finish;
		//	while (end > pos)
		//	{
		//		*end = *(end - 1);
		//		--end;
		//	}
		//	//插入数据
		//	*pos = x; //pos为野指针,迭代器失效
		//	++_finish;
		//}

		iterator insert(iterator pos, const T& x)
		{
			//容量满了——>扩容
			if (_finish == _end_of_storage)
			{
				//记录pos的相对位置防止迭代器失效
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + len;
			}
			//整体后移一位
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			//插入数据
			*pos = x;
			++_finish;

			return pos;
		}

		iterator erase(iterator pos)
		{
			iterator it = pos + 1;
			while (it != end())
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;

			return pos;
		}

		T& operator[](size_t i)
		{
			assert(i >= 0 && i < size());
			return _start[i];
		}

		const T& operator[](size_t i) const
		{
			assert(i >= 0 && i < size());
			return _start[i];
		}

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;

	};
}

#endif

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

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

相关文章

一次Linux端口冲突问题排查记录

更多技术博客&#xff0c;请关注微信公众号&#xff1a;运维之美 一、问题现象 实施同学反馈说我们的服务部署到客户环境后&#xff0c;运行正常&#xff0c;但是后台页面就是无法打开&#xff0c;出现访问白屏问题 从console报错看是其中一个接口响应存在问题&#xff0c;…

老兵永远跟党走,团结奋进新征程-百余老兵庆八一暨《永远闪光的军徽》新书发布座谈会在昆举行

7月30日上午,由中国红色文化研究会老山精神专业委员会主办,昆明乡羊香园承办的百余老兵庆祝“八一”建军节暨《永远闪光的军徽》新书发布座谈会在昆明滇池湖畔乡羊香园举行。 上午10时左右,老战士、曾担任云南省军区领导的黄光汉、陶昌廉、和志光、李继才、雷玉德、王永银老将军…

海思Hi35XX系列(一)环境搭建与挂载

小白一个&#xff0c;新的开发板刚到手有点懵&#xff0c;之前没弄过没有经验&#xff0c;简单记录一下吧 一般买开发板都会给带一个已经配置好的虚拟机文件&#xff0c;直接使用就可以 一、下载安装虚拟机与镜像文件 VMware-workstation16.1.0 我的镜像文件是官方文档资料…

亨廷顿小勇士必看!你的维生素补给站来啦~

Hey小伙伴们~&#x1f44b; 今天咱们来聊聊一个超勇敢的话题——亨廷顿舞蹈症宝贝们的健康小秘密&#xff01;&#x1f389; 虽然路途偶尔有点小颠簸&#xff0c;但记得哦&#xff0c;你们是最闪耀的星星✨&#xff01;让我们一起加油&#xff0c;用对维生素&#xff0c;守护这…

C#知识|文件与目录操作:对象的创建、保存、读取

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 面向对象编程的特点就是一切皆对象&#xff0c;操作的也是对象&#xff0c;本节学习文件与目录操作中&#xff0c;对象的保存&#xff1b; 以下为学习笔记。 01 对象的特点 ①&#xff1a;对象运行在内存中&#xff…

4.5K Stars!为 RAG 而生的数据工程神器

—1 大模型 RAG 的难题是什么&#xff1f; RAG 或者 Fine-tuning 微调作为大模型的增强技术&#xff0c;最核心的技术在于如何把企业的私有数据清洗转换成知识&#xff0c;企业中能够第一时间拿到的私有数据&#xff0c;往往是异构的、数据质量参差不齐&#xff0c;通过数据…

ICM-20948芯片详解(3)

接前一篇文章&#xff1a;ICM-20948芯片详解&#xff08;2&#xff09; 三、引脚图、信号描述及内部框图 1. 引脚图 ICM-20948的引脚图如下图所示&#xff1a; 2. 引脚详细描述 ICM-20948的引脚详细描述如下表所示&#xff1a; 注意&#xff1a;不支持在SCL/SCLK和nCS引脚保…

Linux系统窗口水印难点分析

给应用程序加水印是保护数据的一种方式&#xff0c;window上可以通过给进程通过注入的方法给进程的窗口创建一个同大小的副窗口&#xff0c;在副窗口上绘制水印内容&#xff0c;同时设置副窗口透明同时透传事件&#xff0c;这样就可以达到在源窗口上显示水印的效果且不影响程序…

易媒助手:神似融媒宝的自媒体运营工具,新人送7天中级VIP

自媒体运营工具中还有一个易媒助手&#xff0c;功能与融媒宝、蚁小二类似&#xff0c;免费用户可发5个账号&#xff0c;三者同时用就可发15个账号了&#xff0c;所以今天也给大家介绍下&#xff1a; 易媒助手简介 易媒助手于2017年开发&#xff0c;致力于成为中国更优秀的新媒…

springboot集成海康SDK,设备抓图,热成像仪设置多个点代码获取,以及针对红外图点击某一点获取该点温度的需求

本文会介绍java对海康sdk的三个功能&#xff1a; 1、用代码实时抓图 2、用代码获取热成像仪21个点的坐标及其实时温度 3、针对海康热成像仪抓取的热图能够随便点击任意一个点就能获取其温度的功能。 第一个功能&#xff0c;抓图 抓图 在海康提供的sdk中取流后抓图调用的是 …

ollama运行阿里云通义千问72B大模型

准备 安装ollama https://github.com/ollama/ollama 模型 合并gguf copy /B qwen2-72b-instruct-q5_k_m-00001-of-00002.gguf qwen2-72b-instruct-q5_k_m-00002-of-00002.gguf qwen2-72b-instruct-q5_k_m.gguf设置并启动 新建Modelfile FROM ./qwen2-72b-instruct-q5_k…

【课程总结】Day18:Seq2Seq的深入了解

前言 在上一章【课程总结】Day17&#xff08;下&#xff09;&#xff1a;初始Seq2Seq模型中&#xff0c;我们初步了解了Seq2Seq模型的基本情况及代码运行效果&#xff0c;本章内容将深入了解Seq2Seq模型的代码&#xff0c;梳理代码的框架图、各部分组成部分以及运行流程。 框…

想做linux内核开发,该怎么开始(上)

作为一名应届生在选择从事 Linux 内核开发这一职业领域时&#xff0c;需要系统地规划自己的职业道路&#xff0c;这将有助于你更准确地了解未来的发展方向并制定相应的学习和职业发展计划。在这篇文章中&#xff0c;我将向你介绍应届生在 Linux 内核开发领域的职业道路规划&…

O’Reilly

--江上往来人&#xff0c;但爱鲈鱼美。 --君看一叶舟&#xff0c;出没风波里。 OReilly OReilly出版社出版的技术类图书 俗称动物系列 应该是每个技术人员的必备手册。 OReilly动物系列&#xff08;中译本&#xff09; 简介" 动物系列作为 OReilly 书籍的典型代表被普遍…

【Apache Doris】周FAQ集锦:第 18 期

【Apache Doris】周FAQ集锦&#xff1a;第 18 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户…

基于级联深度学习算法在双参数MRI中检测前列腺病变的评估| 文献速递-AI辅助的放射影像疾病诊断

Title 题目 Evaluation of a Cascaded Deep Learning–based Algorithm for Prostate Lesion Detection at Biparametric MRI 基于级联深度学习算法在双参数MRI中检测前列腺病变的评估 Background 背景 Multiparametric MRI (mpMRI) improves prostate cancer (PCa) dete…

如何对我们要多次使用的页面进行一个抽取

有的时候,一个页面我们要多次使用,该怎么抽取呢? 创建一个文件夹,用于存放多次使用的页面 将要多次使用的组件(<template>)和风格(<style>)剪切出来,放入新建的页面 直接进行引用 导入 然后就可以使用

【FPGA设计】Vitis AI概述

一. Vitis AI简介 Vitis AI 是由 Xilinx&#xff08;现已被 AMD 收购&#xff09;提供的一套工具链和软件开发平台&#xff0c;用于简化和加速在基于 Xilinx FPGA 或自适应计算加速平台 (ACAP) 上部署深度学习推理应用的过程。Vitis AI 的目标是让开发者能够更容易地利用 FPGA…

python-素数回文数的个数(赛氪OJ)

[题目描述] 求 11 到 n 之间&#xff08;包括 n&#xff09;&#xff0c;既是素数又是回文数的整数有多少个。输入&#xff1a; 一个大于 11 小于 10000 的整数 n。输出&#xff1a; 11 到 n 之间的素数回文数个数。样例输入1 23 样例输出1 1 提示&#xff1a; 回文数指左右对…