【C++】stack、queue和priority_queue的模拟实现

在本篇博客中,作者将会讲解STL中的stackqueuepriority_queue的模拟实现,同时还会带大家了解一下deque这个容器。

一.什么是适配器

STL中一共有6大组件:容器,适配器,空间配置器,仿函数,迭代器,算法

其中像vector、list这种数据结构叫做容器,而像stack、queue这种数据结构叫做适配器

为什么呢?


因为stack和queue是通过deque这个容器转换过来的,也就是说,将deque容器的成员函数转换stackqueue成员函数

通过查看C++的手册,发现stack和queue类中有一个模板,其中第二个模板参数就是deque,即在stack和queue类中,是通过deque这个容器来实现的。


可能看到这里,还有同学不懂适配器到底是什么,那么在这里通过几张图来解释一下。 


在解释之前,我们来看看deque这个容器的具体的成员函数。

可以发现,deque这个容器的成员函数特别的多。 


接下来我们再看看stack和queue的成员函数。 

 

可以发现deque的成员函数特别的多,而stack和queue的成员函数特别的少,但是在stack和queue的实现中又用到了deque,所以要减少deque的成员函数来实现stack和queue。


如下图:

二.stack的模拟实现 

在C++库中,stack和queue都是通过使用deque容器来实现的,但是为了能够便于大家理解,在stack的模拟实现中,我们使用vector这个容器来实现,而在queue的模拟实现中,我们使用list这个容器来实现,实现完后,我们再来简单的了解一下deque。

stack的基本成员函数

首先我们来梳理一下stack的基本成员函数,看看这个数据结构需要用到那些功能。

empty:判断是否为空

size:求数据个数

top:取栈顶数据

push:入栈

pop:出栈

剩下的两个暂时不讲解。

stack模拟实现

梳理完stack的基本成员函数后,我们就可以来实现一下了。 

#pragma once
#include<iostream>
#include<vector>
using namespace std;namespace My_Stack
{//使用模板来给一个vector参数template<class T,class Container=vector<T>>class stack{public:stack(){}//入栈void push(const T& val){_s.push_back(val);}//出栈void pop(){_s.pop_back();}//判断是否为空bool empty() const{return _s.empty();}//获取数据个数size_t size() const{return _s.size();}//取栈顶数据T& top(){return _s.back();}//const类型取栈顶数据const T& top() const{return _s.back();}private:Container _s;};void Test1(){stack<int> s1;s1.push(1);s1.push(2);s1.push(3);s1.push(4);while (!s1.empty()){cout << s1.top() << " ";s1.pop();}}
}

三.queue的模拟实现

在模拟实现queue时,我们使用list这个容器来实现它,因为list提供的成员函数比较适合queue。 

queue的基本成员函数 

在实现queue之前,同样的,我们先来看看queue的基本成员函数。

empty:判断队列是否为空

size:获取数据个数

front:获取队头数据

back:获取队尾数据

push:入队列

pop:出队列

queue的模拟实现 

#pragma once
#include<iostream>
#include<list>namespace My_queue
{//模板参数给一个list容器template<class T,class Container=list<T>>class queue{public:queue(){}//入队列void push(const T& val){_l.push_back(val);}//出队列void pop(){_l.pop_front();}//取对头T& front(){return _l.front();}const T& front() const{return _l.front();}//取队尾T& back(){return _l.back();}const T& back() const{return _l.back();}//判断是否为空bool empty() const{return _l.empty();}//获取数据个数size_t size() const{return _l.size();}private:Container _l;};void Test1(){queue<int> q1;q1.push(1);q1.push(2);q1.push(3);q1.push(4);cout << q1.size() << endl;while (!q1.empty()){cout << q1.front() << " ";q1.pop();}}
}

代码写到这里,stack和queue的模拟实现就基本完成了,但是在c++的STL中,实现stack和queue是通过使用deque这个容器来实现的

四.deque的简单介绍

在上面提到,stack和queue其实是通过使用deque这个容器来实现的,但是我们实现的时候为了便于大家理解,所以使用了vector和list来实现,那么现在,我们先来简单的了解一下的deque这个容器。

deque的基本介绍

首先,deque是一个顺序的存储结构,和vectorlist一样,都是顺着顺序来存储数据的,那么它们有什么区别吗,又或者说,为什么会出现deque这种数据结构?

在讲解之前,我们先来看看vector和list的缺点

vector和list的缺点

vector:头插头删效率低(因为要挪动数据)。

list:不能支持随机访问(在访问某个结点前,要先去遍历list去找到它)。

deque的出现

所以为了解决这两个容器的缺点,人们发明出了deque这种数据结构,也叫双端队列,这种队列头插头删尾插尾删时间效率为O(1),同时还能支持随机访问(但是它并不是真正意义上的随机访问)。

deque的结构

那么deque是如何实现的呢?deque通过数组指针指针数组来实现的。

通过这样的结构来实现,使deque的头插头删尾插尾删的效率非常的高,但是deque不适合遍历,因为deque是分开的连续空间,导致在其遍历时非常麻烦,具体的细节在这里不做讲解。 

deque实现stack和queue

所以为什么stack和queue要使用deque这个容器来实现,因为stack只用到了尾插尾删queue只用到了尾插和头删,正好都利用到了deque的优点,而deque的缺点没有涉及到,所以stack和queue的实现用到了deque这个容器。 

以下是使用deque来实现stack和queue。

namespace deque_stack
{template<class T, class Container = deque<T>>class stack{public:stack(){}//入栈void push(const T& val){_s.push_back(val);}//出栈void pop(){_s.pop_back();}//判断是否为空bool empty() const{return _s.empty();}//获取数据个数size_t size() const{return _s.size();}//取栈顶数据T& top(){return _s.back();}//const类型取栈顶数据const T& top() const{return _s.back();}private:Container _s;};void Test1(){stack<int> s1;s1.push(1);s1.push(2);s1.push(3);s1.push(4);while (!s1.empty()){cout << s1.top() << " ";s1.pop();}}
}
namespace deque_queue
{template<class T,class Container = deque<T>>class queue{public:queue(){}//入队列void push(const T& val){_l.push_back(val);}//出队列void pop(){_l.pop_front();}//取对头T& front(){return _l.front();}const T& front() const{return _l.front();}//取队尾T& back(){return _l.back();}const T& back() const{return _l.back();}//判断是否为空bool empty() const{return _l.empty();}//获取数据个数size_t size() const{return _l.size();}private:Container _l;};void Test1(){queue<int> q1;q1.push(1);q1.push(2);q1.push(3);q1.push(4);while (!q1.empty()){cout << q1.front() << " ";q1.pop();}}
}

五.priority_queue的模拟实现

接下来,我们进入到priority_queue的模拟实现。

如果你查过queue的手册,会发现queue下面还有一个priority_queue的东西。

这个叫优先队列,简单的说也就是

对于堆的结构和各种操作,可以参考下面这篇博客

【C语言】堆的实现(建堆、堆的基本操作、堆排序、TOK问题)详解_堆 编程-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/EWIAW_ilove/article/details/135045451?spm=1001.2014.3001.5501

这篇博客详细的讲解了堆的实现,以及各种讲解,在这里,我们直接给出priority_queue的模拟实现并做简单的解释。

默认生成大堆

在实现之前,我们来看看priority_queue的成员函数。


默认情况下,我们生成的堆都是大堆,但是有时候也会用到小堆,大堆和小堆的区别就是,在代码中的比较反过来就行了,但是具体怎么实现呢?

这个时候要用到STL中的仿函数来实现。

在具体讲解建小堆前,我们先来看看大堆的实现。

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
namespace My_priority
{template<class T,class Container = vector<T>>class priority_queue{public:priority_queue(){}//从尾部插入数据void push(const T& val){_pq.push_back(val);//先在vector的尾部插入新数据//后进行一个向上调整AdjustUp();}void AdjustUp()//向上调整算法{int child = size() - 1;int parent = (child - 1) / 2;while (parent >= 0 && _pq[child] > _pq[parent]){swap(_pq[child], _pq[parent]);child = parent;parent = (child - 1) / 2;}}//删除堆顶数据void pop(){assert(size());swap(_pq[0], _pq[size() - 1]);//交换堆顶和尾部数据_pq.pop_back();//删除尾部数据//向下调整AdjustDown();}void AdjustDown()//向下调整算法{int parent = 0;int child = parent * 2 + 1;//默认child给左孩子while (child < size()){if ((child + 1) < size() && (_pq[child] < _pq[child + 1]))//如果右孩子存在并且大于左孩子,则child给右孩子{child += 1;}if (_pq[child] > _pq[parent]){swap(_pq[child], _pq[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}//判断是否为空bool empty() const{return _pq.empty();}//求数据个数size_t size() const{return _pq.size();}//取堆顶数据const T& top() const{assert(size());return _pq[0];}private:Container _pq;};
}

仿函数 

在上面的实现中,默认情况下能生成只大堆,那么如果我们想要生成小堆,该怎么办呢?

在解释之前,我们先来看看仿函数这个东西。


仿函数如字面意思,是一个模仿的函数,即仿函数不是真正意义上的函数,其实它是一个由一个类通过重载()实现的。如下我实现了一个用于比较小于的仿函数。

 同样的,大于的比较我们也可以通过这种方式实现。

如下:        

 

 模拟实现大小堆

有了这两个仿函数,我们就可以通过模板的方式来控制生成的是大堆还是小堆了。 

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>using namespace std;namespace My_priority
{//用于比较小于的仿函数template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}	 };//用于比较大于的仿函数template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};//模板参数有三个:类型参数、堆底层的容器、仿函数,仿函数默认给lesstemplate<class T,class Container = vector<T>,class Compare = less<T>>class priority_queue{public:priority_queue(){}//从尾部插入数据void push(const T& val){_pq.push_back(val);//先在vector的尾部插入新数据//后进行一个向上调整AdjustUp();}void AdjustUp()//向上调整算法{Compare com;//创建仿函数对象int child = size() - 1;int parent = (child - 1) / 2;while (parent >= 0 && com(_pq[parent],_pq[child])){swap(_pq[child], _pq[parent]);child = parent;parent = (child - 1) / 2;}}//删除堆顶数据void pop(){assert(size());swap(_pq[0], _pq[size() - 1]);//交换堆顶和尾部数据_pq.pop_back();//删除尾部数据//向下调整AdjustDown();}void AdjustDown()//向下调整算法{Compare com;//创建仿函数对象int parent = 0;int child = parent * 2 + 1;//默认child给左孩子while (child < size()){if ((child + 1) < size() && com(_pq[child] , _pq[child + 1]))//如果右孩子存在并且大于左孩子,则child给右孩子{child += 1;}if (com(_pq[parent] , _pq[child])){swap(_pq[child], _pq[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}//判断是否为空bool empty() const{return _pq.empty();}//求数据个数int size() const{return _pq.size();}//取堆顶数据const T& top() const{assert(size());return _pq[0];}private:Container _pq;};
}

 六.所以源代码

stack.h

#pragma once
#include<iostream>
#include<vector>
#include<deque>
using namespace std;namespace My_Stack
{//使用模板来给一个vector参数template<class T,class Container=vector<T>>class stack{public:stack(){}//入栈void push(const T& val){_s.push_back(val);}//出栈void pop(){_s.pop_back();}//判断是否为空bool empty() const{return _s.empty();}//获取数据个数size_t size() const{return _s.size();}//取栈顶数据T& top(){return _s.back();}//const类型取栈顶数据const T& top() const{return _s.back();}private:Container _s;};void Test1(){stack<int> s1;s1.push(1);s1.push(2);s1.push(3);s1.push(4);while (!s1.empty()){cout << s1.top() << " ";s1.pop();}cout << endl;}void Test2(){stack<int> s1;s1.push(1);s1.push(2);s1.push(3);s1.push(4);int a = s1.top();s1.pop();s1.push(10);cout << a << endl;}
}namespace deque_stack
{template<class T, class Container = deque<T>>class stack{public:stack(){}//入栈void push(const T& val){_s.push_back(val);}//出栈void pop(){_s.pop_back();}//判断是否为空bool empty() const{return _s.empty();}//获取数据个数size_t size() const{return _s.size();}//取栈顶数据T& top(){return _s.back();}//const类型取栈顶数据const T& top() const{return _s.back();}private:Container _s;};void Test1(){stack<int> s1;s1.push(1);s1.push(2);s1.push(3);s1.push(4);while (!s1.empty()){cout << s1.top() << " ";s1.pop();}cout << endl;}
}

queue.h

#pragma once
#include<iostream>
#include<list>
#include<deque>
using namespace std;namespace My_queue
{template<class T,class Container=list<T>>class queue{public:queue(){}//入队列void push(const T& val){_l.push_back(val);}//出队列void pop(){_l.pop_front();}//取对头T& front(){return _l.front();}const T& front() const{return _l.front();}//取队尾T& back(){return _l.back();}const T& back() const{return _l.back();}//判断是否为空bool empty() const{return _l.empty();}//获取数据个数size_t size() const{return _l.size();}private:Container _l;};void Test1(){queue<int> q1;q1.push(1);q1.push(2);q1.push(3);q1.push(4);cout << q1.size() << endl;while (!q1.empty()){cout << q1.front() << " ";q1.pop();}cout << endl;}
}namespace deque_queue
{template<class T,class Container = deque<T>>class queue{public:queue(){}//入队列void push(const T& val){_l.push_back(val);}//出队列void pop(){_l.pop_front();}//取对头T& front(){return _l.front();}const T& front() const{return _l.front();}//取队尾T& back(){return _l.back();}const T& back() const{return _l.back();}//判断是否为空bool empty() const{return _l.empty();}//获取数据个数size_t size() const{return _l.size();}private:Container _l;};void Test1(){queue<int> q1;q1.push(1);q1.push(2);q1.push(3);q1.push(4);while (!q1.empty()){cout << q1.front() << " ";q1.pop();}cout << endl;}
}

priority.h

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>using namespace std;namespace My_priority
{//用于比较小于的仿函数template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}	 };//用于比较大于的仿函数template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};//模板参数有三个:类型参数、堆底层的容器、仿函数template<class T,class Container = vector<T>,class Compare = less<T>>class priority_queue{public:priority_queue(){}//从尾部插入数据void push(const T& val){_pq.push_back(val);//先在vector的尾部插入新数据//后进行一个向上调整AdjustUp();}void AdjustUp()//向上调整算法{Compare com;int child = size() - 1;int parent = (child - 1) / 2;while (parent >= 0 && com(_pq[parent],_pq[child])){swap(_pq[child], _pq[parent]);child = parent;parent = (child - 1) / 2;}}//删除堆顶数据void pop(){assert(size());swap(_pq[0], _pq[size() - 1]);//交换堆顶和尾部数据_pq.pop_back();//删除尾部数据//向下调整AdjustDown();}void AdjustDown()//向下调整算法{Compare com;int parent = 0;int child = parent * 2 + 1;//默认child给左孩子while (child < size()){if ((child + 1) < size() && com(_pq[child] , _pq[child + 1]))//如果右孩子存在并且大于左孩子,则child给右孩子{child += 1;}if (com(_pq[parent] , _pq[child])){swap(_pq[child], _pq[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}//判断是否为空bool empty() const{return _pq.empty();}//求数据个数int size() const{return _pq.size();}//取堆顶数据const T& top() const{assert(size());return _pq[0];}private:Container _pq;};void Test1(){//priority_queue<int> pq;priority_queue<int,vector<int>,greater<int>> pq;pq.push(15);pq.push(10);pq.push(8);pq.push(20);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;}
}

test.cpp

#include"stack.h"
#include"queue.h"
#include"priority.h"
int main()
{My_Stack::Test1();My_Stack::Test2();My_queue::Test1();deque_stack::Test1();deque_queue::Test1();My_priority::Test1();return 0;
}

 

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

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

相关文章

【码银送书第十九期】《图算法:行业应用与实践》

作者&#xff1a;嬴图团队 01 前言 在当今工业领域&#xff0c;图思维方式与图数据技术的应用日益广泛&#xff0c;成为图数据探索、挖掘与应用的坚实基础。本文旨在分享嬴图团队在算法实践应用中的宝贵经验与深刻思考&#xff0c;不仅促进业界爱好者之间的交流&#xff0c;…

RabbitMQ 是如何做延迟消息的 ?——Java全栈知识(15)

RabbitMQ 是如何做延迟消息的 &#xff1f; 1、什么是死信&#xff1f; 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff08;dead letter&#xff09;&#xff1a; 消费者使用 basic.reject 或 basic.nack 声明消费失败&#xff0c;并且消息的 reque…

2.4Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3-基础-Vue组件

初识Vue组件 Vue中的组件是页面中的一部分&#xff0c;通过层层拼装&#xff0c;最终形成了一个完整的组件。这也是目前前端最流行的开发方 式。下面是Vue3官方给出的一张图&#xff0c;通过图片能清楚的了解到什么是Vue中的组件。 图的左边是一个网页&#xff0c;网页分为了…

ECS弹性云服务器居然这么好用。

引言 在过去的十年里&#xff0c;云计算从一个前沿概念发展为企业和开发者的必备工具。传统的计算模型通常局限于单一的、物理的位置和有限的资源&#xff0c;而云计算则通过分布式的资源和服务&#xff0c;为计算能力带来了前所未有的"弹性"。 云弹性服务器——为什…

AAA、RADIUS、TACACS、Diameter协议介绍

准备软考高级时碰到的一个概念&#xff0c;于是搜集网络资源整理得出此文。 概述 AAA是Authentication、Authorization、Accounting的缩写简称&#xff0c;即认证、授权、记帐。Cisco开发的一个提供网络安全的系统。AAA协议决定哪些用户能够访问服务&#xff0c;以及用户能够…

精品干货 | 数据中台与数据仓库建设(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 数据中台与数据仓库建设 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 如需下载本方案PPT/WORD原格式&#xff0c;请加入微信扫描以下方案驿站知识星球&#xff0c;获取上万份PPT/WORD解决方…

PPO 学习笔记

用PPO算法求解整个神经网络在迭代过程中的梯度问题 每走一步就会得到一个新的状态&#xff0c;把这个状态传到网络里面&#xff0c;会得到一个 action&#xff0c;执行这个 action 又会到达一个新状态 policy 中由状态 st 生成动作 at&#xff0c;生成的这个 at 是由整个网络的…

什么是X电容和Y电容?

先补充个知识&#xff1a; 一、什么是差模信号和共模信号 差模信号&#xff1a;大小相等&#xff0c;方向相反的交流信号&#xff1b;双端输入时&#xff0c;两个信号的相位相差180度 共模信号&#xff1a;大小相等。方向相同。双端输入时&#xff0c;两个信号相同。 二、安规…

Redis探索之旅(基础)

目录 今日良言&#xff1a;满怀憧憬&#xff0c;阔步向前 一、基础命令 1.1 通用命令 1.2 五大基本类型的命令 1.2.1 String 1.2.2 Hash 1.2.3 List 1.2.4 Set 1.2.5 Zset 二、过期策略以及单线程模型 2.1 过期策略 2.2 单线程模型 2.3 Redis 效率为什么这么高 三…

cmake进阶:文件操作

一. 简介 前面几篇文章学习了 cmake的文件操作&#xff0c;写文件&#xff0c;读文件。文章如下&#xff1a; cmake进阶&#xff1a;文件操作之写文件-CSDN博客 cmake进阶&#xff1a;文件操作之读文件-CSDN博客 本文继续学习文件操作。主要学习 文件重命名&#xff0c;删…

python爬虫(一)之 抓取极氪网站汽车文章

极氪汽车文章爬虫 闲来没事&#xff0c;将极氪网站的汽车文章吃干抹尽&#xff0c;全部抓取到本地&#xff0c;还是有点小小的难度。不能抓取太快&#xff0c;太快容易被封禁IP&#xff0c;不过就算被封了问题也不大&#xff0c;大不了重启路由器&#xff0c;然后你的IP里面又…

文件夹加密软件哪个好?文件夹加密软件排行榜

许多人给小编说&#xff0c;我们公司想实现文件私自发出呈乱码状态&#xff0c;这说明公司逐渐认识到文件加密的重要性。 目前&#xff0c;加密软件已经广泛应用于企业办公、商业贸易、个人应用等多个领域&#xff0c;成为保护数据安全和隐私的重要手段。 为了保护企业机密&am…

OpenNJet评测,探寻云原生之美

在信息时代的大海上&#xff0c;云原生应用引擎如一艘航行于波涛之间的帆船&#xff0c;承载着创新的梦想和数字化的未来。本文将带领您登上这艘船&#xff0c;聚焦其中之一的OpenNJet&#xff0c;一同探寻其中的奥秘和精妙&#xff0c;领略其独特之美。 OpenNJet 内容浅析 O…

智慧工地)智慧工地标准化方案(107页)

2.2 设计思路 对于某某智慧工地管理系统的建设&#xff0c;绝不是对各个子系统进行简单堆砌&#xff0c;而是在满足各子系统功能的基础上&#xff0c;寻求内部各子系统之间、与外部其它智能化系统之间的完美结合。系统主要依托于智慧工地管理平台&#xff0c;来实现对众多子系统…

OpenNJet应用引擎——云原生时代的Web服务新选择

文章目录 OpenNJet应用引擎——云原生时代的Web服务新选择引言&#xff1a;数字化转型的推动力&#xff1a;OpenNJet应用引擎为什么选择OpenNJet&#xff1f; OpenNJet的核心优势1. 云原生功能增强2. 安全加固3. 代码重构与性能优化4. 动态加载机制5. 多样化的产品形态6. 易于集…

Python测试框架Pytest的参数化详解

上篇博文介绍过&#xff0c;Pytest是目前比较成熟功能齐全的测试框架&#xff0c;使用率肯定也不断攀升。 在实际工作中&#xff0c;许多测试用例都是类似的重复&#xff0c;一个个写最后代码会显得很冗余。这里&#xff0c;我们来了解一下pytest.mark.parametrize装饰器&…

后端接口返回二进制数据流,前端如何将其转换成对应的excel、csv和json文件格式并下载

本文主要是介绍在工作中遇到的后端接口返回一个二进制数据流&#xff0c;前端在界面上创建下载按钮并下载成对应格式的文件导出。 downloadData({start: startTime,end: endTime,exportType: 0, // 0-excel, 1-csv, 2-json }).then((res) > {download(res, startTime, endTi…

毕业设计:《基于 Prometheus 和 ELK 的基础平台监控系统设计与实现》

前言 《基于 Prometheus 和 ELK 的基础平台监控系统设计与实现》&#xff0c;这是我在本科阶段的毕业设计&#xff0c;通过引入 Prometheus 和 ELK 架构实现企业对指标与日志的全方位监控。并且基于云原生&#xff0c;使用容器化持续集成部署的开发方式&#xff0c;通过 Sprin…

通信系列:通信中如何度量消息中所包含的信息量?如何评估通信系统的性能?

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、通信中如何度量消息…

小吉/希亦/鲸立内衣洗衣机怎么样?深度测评谁更好用!

内衣洗衣机是近几年新兴的家电产品&#xff0c;以清洁效果好、除菌能力强&#xff0c;被很多人种草入手了&#xff01;但网上有不少人虽感兴趣&#xff0c;但不清楚如何选。担心买到质量差&#xff0c;清洗不干净的产品。作为一名家电测评博主&#xff0c;我今天特意围绕被问最…