数据结构-vector

来源:互联网 发布:卫生巾政治环境知乎 编辑:程序博客网 时间:2024/06/06 07:22

vector是不定长数组,也就是说它的长度是不固定的,简单地说就是“按需分配”。这听上去似乎有点麻烦,但在声明数组时如果我们并不清楚数组的长度,并且简单粗暴地使用#define MAXN 1000000会导致内存失去了梦想——就算不,仍有大量的内存成了咸鱼。这个时候,我们就需要vector数组。


先看一道例题:

从左到右有n个木块,编号为0~n-1,要求模拟以下四种操作:

  • move a onto b:把a和b上方的木块全部归位,然后把a摞在b上面。
  • move a over b:把a上方的木块全部归位,然后把a放在b所在木块堆的顶部。
  • pile a onto b:把b上方的木块全部归位,然后把a及上面的木块整体摞在b上面。
  • pile a over b:把a及上面的木块整体摞在b所在木块堆的顶部。

遇到quit时终止一组数据。a和b在同一堆的指令时非法指令,应当忽略。
所有操作结束后,输出每个位置的木块列表,按照从底部到顶部的顺序排列。
(出自UVa 101,小紫书p110)

由于每个木块堆的最大高度并没有限制,如果开一个a[n][n]的数组,显然浪费了很多空间,并且出题人很可能会故意卡MLE,于是我们需要一个“按需分配”数组。


链表

在介绍vector前,先提一下链表。
在c语言中,我们并没有c++丰富的STL,也就是说需要人工实现不定长数组:

    struct Node    {        int num;     //用于储存数据        Node* next;    };

这段代码声明了一个结构体Node,它包含了一个int型变量num和一个指针next,让我们有请灵魂画师来帮助我们理解。

链表块

我们的链表是由若干个这样的“小块”组成的,由于创建的“小块”之间没有任何联系,所以我们需要用一个指针next将它们连接到一起,也就是用next指向下一个“小块”,这样我们就能将它们“链”在一起。

链表

每一个next都指向下一个块的地址,我们只需要访问当前块的next储存的地址,就可以到达下一个块。

由于我们只用在需要的时候申请一个新的块,并把它和之前的链表连在一起,所以可以达到不定长数组的作用。

但显然,构造一个链表十分麻烦,并且鄙人已经基本忘了怎么写链表,所以我们需要一个C艹。(c语言的链表构建我会在之后补上(flag++))


vector

在C++中,我们可以用vector来代替链表,这需要

    #include <vector>

在vector中,我们(暂时)不需要再思考指针和它的小伙伴们,可以把vector看作一个正常的数组:

vector示意图

也就是说它的空间是“连续”的。

我们可以用

    vector <int> v;

声明一个vector数组,它的元素为int型。当然,你也可以换成double型或者string型甚至是结构体。

vector为我们提供了以下操作:

    v.push_back(n);  //将元素n加入到v的尾部    v.pop_back();    //将v的末端的元素删除    v.empty();       //判断数组是否为空,若为空则返回真,否则返回假    v.size();        //返回v中元素的数量    v.resize(n);     //将v的长度设为n

如果我们想访问v中的元素,只需要像数组一样访问下标即可:

    ans=v[i];    num=v[i]+v[i+1];

这时候,我们可以解决前面的例题了。


代码示例

#include <iostream>#include <string>#include <vector>using namespace std;const int MAXN=10000;vector <int> v[MAXN];int n;int find(int a,int& p,int& h)  //寻找木块a(或b)所在位置和高度{    p=v[a][0];    for(h=1;h<v[p].size();h++)    {        if(v[p][h]==a) return 0;    }}void clear(int p,int h)  //将位置p高度h的木块上方的所有木块归位{    for(int i=h+1;i<v[p].size();i++)    {        int j=v[p][i];        v[j][0]=j;    }    v[p].resize(h+1);}void move(int p1,h1,p2) //将位置p1高度h1的木块及上方的所有木块移到位置p2的顶端{    for(int i=h1;i<v[p1].size();i++)    {        int j=v[p1][i];        v[p2].push_back(j);        v[j][0]=p2;    }    v[p1].resize(h1);}int main(){    int a,b;    string s1,s2;    while(cin>>n>>s1)    {        for(int i=0;i<n;i++)  //初始化v        {            v[i].clear();            v[i].push_back(i); //v[i][0]保存木块i所在的位置        }        while(s1!="quit")        {            cin>>a>>s2>>b;            int p1,h1,p2,h2;            find(a,p1,h1);            find(b,p2,h2);            if(p1!=p2)            {                if(s2=="onto") clear(p2,h2);                if(s1=="move") clear(p1,h1);                move(p1,h1,p2);            }            cin>>s1;        }        for(int i=0;i<n;i++) //一组数据结束,打印木块列表        {            cout<<i<<":";            for(int j=1;j<v[i].size();j++)                cout<<v[i][j]<<" ";            cout<<endl;        }    }    return 0;}

鄙人用v[i][0]来保存木块i所在的位置(初始为i),这样在每次find的时候可以减少一次for循环查找木块i的位置,避免最坏情况下find为O(n²)而导致超时。


迭代器

还记得之前说的“我们(暂时)不需要再思考指针和它的小伙伴们”么?
是的,我们只是暂时不需要,但事实上还有个叫做“迭代器”的设定。
由于不(wo)可(jiu)抗(shi)力(lan)的因素,此部分之后再更新……


PS:
写这篇的时候临近国庆,于是我咕了将近半个月,虽然打算国庆结束后就补完迭代器的部分,然而我很快就发烧在床上瘫了两天…就这么先扔上来吧(强颜欢笑)

…什么?还有链表?

原创粉丝点击