stl的stack怎么用,stl基本容器

  stl的stack怎么用,stl基本容器

  Yyds干货库存

  写在这前面应该是我们STL的最后一篇博客了。今天,有三种数据结构,即堆栈、队列和优先级队列。从某种意义上说,它们不叫容器,而是容器适配器。我们先互相了解一下。

  容器适配器。当我们看他们的模板参数时,你会明白第二个参数是一个容器。所谓容器适配器,就是我们自己不实现,而是用另一种数据结构来实现。

  在这里,我想给你解释一下什么是适配器。在我们的日常生活中,我们知道mainland China的民用电压是220v,但当你给手机充电时,它不是200v。这是因为你的手机充电器改变了电压。这个充电器是一个适配器。也就是说,一个东西可以用,但可能功能多一点,我们只需要拿出一小部分。

  计算机也是如此。我们可以用一种封装另一种。我们直接重用他们原来的方法,就不自己写了。

  这里我们不会演示如何使用堆栈。一切都很简单。让我们看看它们是如何实现的。

  这里,我们直接用一个数据结构来模拟实现栈。至于下面的德缺,暂且不提。

  模板类T,类容器=std:deque T

  类堆栈

  {

  公共:

  无效推送(常数x) {

  _ con . push _ back(x);

  }

  void pop() {

  _ con . pop _ back();

  }

  const T top() {

  return _ con . back();

  }

  bool empty() {

  return _ con . empty();

  }

  size_t size() {

  return _ con . size();

  }

  私人:

  Container _ con

  };在这里测试一下吧。我们使用标准库来测试它。

  #包括堆栈

  int main()

  {

  stack int,vector int s;

  s . push;

  s . push(2);

  s . push(3);

  s . push(4);

  而(!s.empty()

  {

  cout s . top()endl;

  s . pop();

  }

  返回0;

  }

  是的,你没有看错。如果你不想用deque做容器,我们也可以选择其他的。但是你选择的数据结构必须有对应的函数,这也是STL中数据结构的函数名大多相同的优势。

  这和stack是一样的,stack是使用的另一种数据结构。排队没什么好谈的。

  模板类T,类容器=std:deque T

  类别队列

  {

  公共:

  无效推送(常数x) {

  _ con . push _ back(x);

  }

  void pop() {

  _ con . pop front();

  }

  const T top() {

  return _ con . front();

  }

  bool empty() {

  return _ con . empty();

  }

  size_t size() {

  return _ con . size();

  }

  T back() {

  return _ con . back();

  }

  const back()const {

  return _ con . back();

  }

  T front() {

  return _ con . front();

  }

  const T front()const

  return _ con . front();

  }

  私人:

  Container _ con

  };Priority_queue使用这个优先级队列,它本质上是一个底层的堆。我们今天的重点就是这个内容。

  我们可以先看看他们的接口。都比较简单。

  先简单用一下。这个默认好像是大优先,就是建一个大堆。

  #包括iostream

  #包括队列

  使用命名空间std

  int main()

  {

  priority _ queue int p;

  p . push(1);

  p . push(2);

  p . push(0);

  p . push(-1);

  p . push(1100);

  而(!p.empty())

  {

  cout p . top()“”;

  p . pop();

  }

  返回0;

  }

  我们想知道我们是否能建一个小堆。标准库中有一个模仿函数更大,可以帮助我们建立一个小堆。至于模仿功能,我们只需要先知道它的用法就可以了。以后再分享。

  int main()

  {

  priority_queue int,vector int,greater int p;

  p . push(1);

  p . push(2);

  p . push(0);

  p . push(-1);

  p . push(1100);

  而(!p.empty())

  {

  cout p . top()“”;

  p . pop();

  }

  返回0;

  }

  Priority_queue模拟现在我们还可以模拟priority_queue是如何实现的。它的本质是一个数组,也就是一个堆。这里不讨论如何向上或向下调整。我们已经在数据结构里和大家分享过了,这里就不赘述了。我们主要看模仿功能的使用。

  优先级队列也是一个容器适配器。

  模板类T

  无结构

  {

  布尔运算符()(常数t1,常数t2)常数

  {

  返回t1 t2

  }

  };

  模板类T

  结构更大

  {

  布尔运算符()(常数x,常数y)常数

  {

  返回x y;

  }

  };

  模板类T,类容器=std:vector T,类比较=bit:less T

  类别优先级_队列

  {

  公共:

  优先级队列()

  {

  }

  私人:

  Container _ con

  };推到堆里

  无效推送(常数x)

  {

  _ con . push _ back(x);

  adjustUp(_ con . size()-1);

  }从堆中弹出()

  无效弹出()

  {

  断言(!_ con . empty());

  std:swap(_con[0],_ con[size()-1]);

  _ con . pop _ back();

  adjust dwon(0);

  }top()查看堆叠的元素

  const T top()

  {

  return _ con[0];

  }调整我们的数据进出堆需要调整,如果我们写下堆中元素的优先级,代码的灵活性就不行了。比如我们要一个小堆,你给我一个现成的堆。这是一个问题。我们无法控制它。早期可能用函数指针实现,但是函数指针的可读性太差。我们逐渐把它改成了模仿功能。

  仿函数我们先来了解一下仿函数。我们来看看这样的结构。

  结构我的结构

  {

  布尔运算符()(常量整数x,常量整数y)

  {

  返回x y;

  }

  };我们重载了()操作符,这使得我们的调用看起来像是函数调用,所以我们称之为模仿函数。

  int main()

  {

  MyStruct comfunc

  cout comfunc(1,2)endl;

  返回0;

  }

  void adjust won(size _ t parent)

  {

  比较comFunc

  size _ t child=2 * parent 1;

  while(子尺寸())

  {

  if(child 1 _ con . size()com func(_ con[child],_con[child 1])

  {

  孩子;

  }

  if(com func(_ con[父级],_ con[子级]))

  {

  STD:swap(_ con[父级],_ con[子级]);

  父母=孩子;

  child=2 * child 1;

  }

  其他

  {

  打破;

  }

  }

  }

  void adjustUp(int child)

  {

  //先建一个大堆

  比较comFunc

  size _ t parent=(child-1)/2;

  while(子0)

  {

  if(com func(_ con[父级],_ con[子级]))

  {

  STD:swap(_ con[父级],_ con[子级]);

  孩子=父母;

  parent=(child-1)/2;

  }

  其他

  {

  打破;

  }

  }

  }仿函数最大的好处就是代替了函数指针。对于自定义类型,我们也可以单独编写它们的模仿函数,但是要求自定义类型重载等号运算符。但是我们也知道C支持C语言。我们之前学习qsort的时候用到了函数指针,那么这里也可以传入函数指针吗?可以,但是需要一些方法。

  我们先假设可以像最初的qsort一样使用它。

  bool comFare(int x,int y)

  {

  返回x y;

  }

  int main()

  {

  bit:priority_queue int,vector int,bool(*)(int,int)p;

  p . push(1);

  p . push(3);

  p . push(2);

  返回0;

  }

  现在我们要困惑了。我们的第三个模板参数是函数指针类型,可以看作是内置类型。编译器为自动调用它的构造函数初始化nullptr。所以编译器会报错。这里我们开始尴尬了。我们应该怎么做才能传入我们想要的比较函数?我们在这里不能使用模板参数,但是构造函数可以。

  我们把模板参数可以实例化的对象看作是类中的一个成员变量,所以在构造函数的时候初始化它。我们需要修改函数。

  这样,我们可以像这样使用优先级队列。

  bool comFare(int x,int y)

  {

  返回x y;

  }

  int main()

  {

  bit:priority_queue int,vector int,bool(*)(int,int)p(com fare);

  //bit:priority _ queue int p(com fare);

  p . push(1);

  p . push(3);

  p . push(2);

  p . push(0);

  p . push(-1);

  而(!p.empty())

  {

  cout p . top()“”;

  p . pop();

  }

  返回0;

  }

  我们来试试之前的用法。能用吗?可以找到,后面需要解释原理。

  int main()

  {

  //bit:priority_queue int,vector int,bool(*)(int,int)p(com fare);

  bit:priority _ queue int p;

  p . push(1);

  p . push(3);

  p . push(2);

  p . push(0);

  p . push(-1);

  而(!p.empty())

  {

  cout p . top()“”;

  p . pop();

  }

  返回0;

  }

  我先解释一下传入的函数指针的类型。我们知道,实例化对象将调用默认的构造函数,如下所示。而且我们实例化对象的时候传入参数,第三个模板参数的类型就变成了函数指针的类型。这里的类型互相匹配。

  这样我们就可以理解为,如果不传入函数指针类型,那么第三个模板参数默认是它的默认值,也就是仿函数类型west。编译器自动调用构造函数,并实例化模仿函数类型的对象,这就是为什么它可以被使用。我们知道我们可以使用函数指针,但如果有一个简单的函数指针,为什么还要用它呢?

  这里的Sort,我们还想看看仿函数的应用,最好的体现就是这个sort函数,这是STL算法库中提供的一个函数。当我们看迭代器类型时,我们让你看到它。这里不做详细介绍,只说模仿功能相关的知识。

  int main()

  {

  向量int v;

  五. push _ back(1);

  五. push _ back(2);

  五. push _ back(3);

  五、push _ back(13);

  v . push _ back(-1);

  sort(v.begin()、v . end());

  for (int val : v)

  {

  cout val“”;

  }

  返回0;

  }

  我们注意到默认是升序。我们能把它们按降序排列吗?是的,需要使用模仿功能。

  int main()

  {

  向量int v;

  五. push _ back(1);

  五. push _ back(2);

  五. push _ back(3);

  五、push _ back(13);

  v . push _ back(-1);

  sort(v.begin(),v.end(),greater int());

  for (int val : v)

  {

  cout val“”;

  }

  返回0;

  }

  看上面排序中的参数,我们发现传入的是仿函数的匿名对象,和前面的有点不一样。我们可以反过来理解模仿函数的具体用途,即作为判断是上升还是下降的条件。

  为什么在这里传入对象,我们需要解释为什么传入对象而不是简单的传入类型。第一点是,我们可能必须自己定义变量,这意味着如果我们传入类型,我们需要在类型中重载一些运算符。而且,我们的比较方法是固定的。

  结构货物

  {

  string _ name

  双_价;

  size _ t _ saleNum

  //.

  布尔运算符(常量商品g)常量

  {

  return _ price g. _ price

  }

  布尔运算符(常量商品g)常量

  {

  return _ price g. _ price

  }

  };

  int main()

  {

  商品gds[4]={{ 苹果,2.1,1000},{ 香蕉,3.0,200},{ 橘子,2.2,300},{ 菠萝,1.5,50 } };

  sort(gds,gds 4,greater Goods());

  返回0;

  }

  但是这里的比较规则已经确定了,只能按价格来比较,那么如果要按销量来比较呢?这是一个问题。所以这里我们传入对象,我们可以将每个比较规则封装到一个类中,并实例化它。一般来说,小于就是小于,对应升序。

  结构货物

  {

  string _ name

  双_价;

  size _ t _ saleNum

  //.

  };

  无结构价格

  {

  布尔运算符()(常数商品g1,常数商品g2)常数

  {

  返回g1。_price g2。_价格;

  }

  };

  构建更高的价格

  {

  布尔运算符()(常数商品g1,常数商品g2)常数

  {

  返回g1。_price g2。_价格;

  }

  };

  struct LessSaleNum

  {

  布尔运算符()(常数商品g1,常数商品g2)常数

  {

  返回g1。_saleNum g2。_ saleNum

  }

  };

  巨大结构

  {

  布尔运算符()(常数商品g1,常数商品g2)常数

  {

  返回g1。_saleNum g2。_ saleNum

  }

  };

  int main()

  {

  商品gds[4]={{ 苹果,2.1,1000},{ 香蕉,3.0,200},{ 橘子,2.2,300},{ 菠萝,1.5,50 } };

  cout ======================== endl;

  for(int I=0;I 4;我)

  {

  cout gds[i]。_name price : gds[i]。_price saleNum: gds[i]._ saleNum endl

  }

  cout ======================== endl;

  sort(gds,gds 4,LessSaleNum());

  cout ======================== endl;

  for(int I=0;I 4;我)

  {

  cout gds[i]。_name price : gds[i]。_price saleNum: gds[i]._ saleNum endl

  }

  cout ======================== endl;

  sort(gds,gds 4,GreaterSaleNum());

  cout ======================== endl;

  for(int I=0;I 4;我)

  {

  cout gds[i]。_name price : gds[i]。_price saleNum: gds[i]._ saleNum endl

  }

  cout ======================== endl;

  返回0;

  }

  理解德缺这里我们需要了解一下德缺。这也是数据结构,但是他没什么好讲的。今天我们只看他的底层结构。

  我们知道,如果物理内存是连续的,那么CPU访问的速度是非常快的。对于列表,这是一个节点。我们可以这样想吗?假设我们把一个很小的碎片空间看成一个向量,几个很小的空间连成一个列表。这样会在一定程度上提高效率。

  这里我们还需要一个数组来记录每个碎片空间的地址。

  首先我说这个想法很好,但是实际效率很低。一般我们不这么做。

  反向迭代器我们前面的列表只实现了正向迭代器。这里我们看到了适配器。这里我们可以看看反向迭代器,它是由正向迭代器改编而来的。

  我们先来看看源代码。

  我们知道方向迭代器的方向是不同的或者——其他都是一样的,所以C站在顶层模式看它们。我们将在这里实现反向迭代器。首先,我们将实现一个相当令人沮丧的版本,然后我们将与您讨论上述版本是如何实现的。

  反向迭代器我们重点说一下解引用,这涉及到我们之前的list的设计原则。

  模板类迭代器,类引用,类指针

  结构反向迭代器

  {

  Iterator _ it

  typedef Reverse _ Iterator Iterator,Ref,Ptr Self

  反向迭代器(Iterator it)

  :_它(它)

  {}

  引用运算符*()

  {

  迭代器tmp=_ it

  return *(tmp);

  }

  Ptr操作员-()

  {

  return(运算符*);

  }

  Self运算符()

  {

  -_ it;

  返回* this

  }

  自运算符-()

  {

  _ it

  返回* this

  }

  布尔运算符!=(常数自我s)

  {

  return _it!=s. _ it

  }

  };正如我们前面谈到的,list的迭代器是以这种方式用end和begin设计的。

  同样,这里也需要对称设计,所以对称的结果应该是这样的。当我们取消引用时,我们可以取消引用前一个节点的值。

  reverse_iterator rbegin()

  {

  返回迭代器(end());

  }

  reverse _ iterator render()

  {

  返回迭代器(begin());

  }

  分析完反向迭代器,我们还是要再讲一下反向迭代器。虽然上面我们已经实现了,但是这里还有一个小问题。我们传入了三个模板参数,但是我看到标准库中只有一个。我觉得和它一样。让我们看看标准迭代器是如何实现的。

  前向迭代器中需要存在一些东西,这样我们才能完成迭代器提取,

  模板类迭代器

  结构反向迭代器

  {

  Iterator _ it

  typedef Reverse_iterator迭代器自身;

  反向迭代器(Iterator it)

  :_它(它)

  {}

  迭代器:引用运算符*()

  {

  迭代器tmp=_ it

  return *(tmp);

  }

  迭代器:指针运算符-()

  {

  return(运算符*);

  }

  Self运算符()

  {

  -_ it;

  返回* this

  }

  自运算符-()

  {

  _ it

  返回* this

  }

  布尔运算符!=(常数自我s)

  {

  return _it!=s. _ it

  }

  };

  要知道正向迭代器本质上是一个模板,我们在模板中寻找特定的参数,这是编译器不允许的。这里我们需要一个关键字。

  我们之前见过关键字typename,就不说原始函数了。在这里,直接说明你为什么这么做,为什么能解决问题。首先,我们知道正向迭代器本来就是一个模板类。我们只能通过在模板类中查找类型来获得虚拟类型。关键字typename可以告诉编译器这是模板类中的类型。你需要等待一段时间,等到这个类型被实例化后,才能得到这个类型。

  typename迭代器:引用运算符*()

  {

  迭代器tmp=_ it

  return *(tmp);

  }

  typename迭代器:指针运算符-()

  {

  return(运算符*);

  }

  我们也可以在逆向类中重命名它,这样更简单。

  typedef typename迭代器:reference Ref

  typedef typename Iterator:pointer Ptr;

  反向迭代器(Iterator it)

  :_它(它)

  {}

  引用运算符*()

  {

  迭代器tmp=_ it

  return *(tmp);

  }

  Ptr操作员-()

  {

  return(运算符*);

  }

  我们在这里讲这个关键词,主要是为了让大家更好的理解。至于迭代器,这里先说,后面再讲迭代器提取原理,更痛苦。

  让我们先看看下面的代码,

  #包括iostream

  #包含列表

  使用命名空间std

  模板类T

  作废清单_打印(清单T l)

  {

  list T:const _ iterator cit=l . c begin();

  而(cit!=l.cend())

  {

  cout * cit“”;

  cit

  }

  }

  int main()

  {

  list int l;

  l . push _ back(1);

  l . push _ back(1);

  l . push _ back(1);

  l . push _ back(1);

  l . push _ back(1);

  l . push _ back(1);

  list _ print(l);

  列表字符串lstr

  lstr . push _ back( QQ QQ QQ );

  lstr . push _ back( QQ QQ QQ );

  lstr . push _ back( QQ QQ QQ );

  lstr . push _ back( QQ QQ QQ );

  lstr . push _ back( QQ QQ QQ );

  list _ print(lstr);

  返回0;

  }

  这是因为列表是一个模板,我们去模板,它们的类型是编译器不允许的,所以我们需要在这里添加一个typename。

  模板类T

  作废清单_打印(清单T l)

  {

  typename list T:const _ iterator cit=l . c begin();

  而(cit!=l.cend())

  {

  cout * cit“”;

  cit

  }

  }

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • office2010激活密钥大全 怎么永久激活office2010
  • project2010产品密钥免费_project2010激活密钥永久激活码
  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • chatgpt是什么?为什么这么火?
  • ChatGPT为什么注册不了?OpenAI ChatGPT的账号哪里可以注册?
  • OpenAI ChatGPT怎么注册账号?ChatGPT账号注册教程
  • chatgpt什么意思,什么是ChatGPT ?
  • CAD中怎么复制图形标注尺寸不变,CAD中怎么复制图形线性不变
  • cad中怎么创建并使用脚本文件,cad怎么运行脚本
  • cad中快速计算器的功能,cad怎么快速计算
  • cad中快速修改单位的方法有哪些,cad中快速修改单位的方法是
  • cad中心点画椭圆怎么做,cad轴测图怎么画椭圆
  • CAD中常用的快捷键,cad各种快捷键的用法
  • 留言与评论(共有 条评论)
       
    验证码: