C++中STL详解(一)

一 vector

Vector通常叫做变长数组或长度根据需要而自动改变的数组。

要使用vector需要添加头文件

#include <vector>

并在头文件下面添加语句

using namespace std;

1 vector的定义

//单独定义一个vector
vector<typename> name;

这其实是定义了一维数组,只不过数组长度可以根据长度变化,节省空间。其中typename可以为任意类型,如Int/double/char/结构体也可以是STL标准容器如vector/set/queue等。如果tyename也是一个STL容器,定义的时候记得在>>符号之间加上空格,避免有的编译器把它看作移位操作,导致编译错误。

//typename为一个容器,其实可以看作二维数组
vector<vector<int> > my_vector;

vector数组的定义(vector数组可以看做2个维数都可变长的二维数组)

vector<typename> arrayname[arraysize];
//举例如下
vector<int> vi[100];

其中vi[0]--vi[99]都是一个vector容器.

这种写法与上面vector<vector<int> > my_vector的写法的区别,这种写法已经固定了一个维度的长度,而另一个不是。

请注意 初始化操作

#include <vector>

using namespace std;

int main()
{
    vector<int> vi(5);
    for(vector<int>::iterator it=vi.begin();it!=vi.end();it++)
        printf("%d ",*it);
    printf("\n");

    vector<int> vec(5,2);
    vector<int> vec2(vec);
    vector<int> vec3(vec2.begin(),vec2.begin()+2);
    int a[3]={3,6,9};
    vector<int> vec4(a,a+3);

    for(vector<int>::iterator it2=vec3.begin();it2!=vec3.end();it2++)
        printf("%d ",*it2);
    printf("\n");

    for(vector<int>::iterator it3=vec4.begin();it3!=vec4.end();it3++)
        printf("%d ",*it3);
    printf("\n");

    return 0;

}

详见博客

扫描二维码关注公众号,回复: 5143853 查看本文章

2 vector容器元素的访问

一般vector有两种访问方式:通过下标访问和通过迭代器访问。

(1)通过下标访问

与访问普通数组元素一样。对于容器

vector<int> apple;

可以直接访问apple[index]即可,当然index的下标是从0到apple.size()-1;访问这个范围之外的元素都会出错。

(2)通过迭代器访问

迭代器看起来像是一个类似指针的东西,它的定义为

vector<typename>::iterator it;
//迭代器可以实现自增自减操作  ++i,i++,--i,i--
//注意,并不是所有的STL容器的迭代器都可以自增或自减
#include <cstdio>
#include <vector>

using namespace std;

int main()
{
    vector<int> vi;
    for(int i=1;i<=5;i++)
        vi.push_back(i);
    //Begin()取vi的首元素地址,end()取尾元素地址的下一个地址
    vector<int>::iterator it=vi.begin();
    //注意 vector不支持it<vi.end()只能用!=代替
    for(;it!=vi.end();it++)
        printf("%d ",*it);

    printf("\n");
    //也可以用
    it=vi.begin();
    for(int i=0;i<5;i++)
        printf("%d ",*(it+i));
    return 0;
}

注意:在STL容器中。只有vector和string中才允许使用vi.begin()+3这种迭代器加上整数的写法。

3 vector常用函数

(1)push_back()

vector<int> vi;
vi.push_back(x); //在vector后面添加一个元素x,时间复杂度为O(1)

(2) pop_back()

vector<double> hui;
hui.pop_back();   //不带参数,从末尾删除vector的尾元素,时间复杂度为O(1)

(3)size()

vector<float> length;
//size()用来获取vector中元素个数,时间复杂度为O(1),size()的返回类型为unsigned类型,不过一般来说用%d不会出很大问题。
printf("%d",length.size());

(4) clear()

//clear()用来清空vector中所有元素,时间复杂度为O(N),其中N为vector元素个数。
vector<int> number;
number.clear();

(5) insert()

//insert(it,x)用于向vector的任意迭代器it处插入一个元素x,时间复杂度为O(N)
vector<int> numbers;
numbers.insert(numbers.begin()+2,4);   //将4插入到numbers[2]的位置

(6) erase()

erase()有2种用法:删除单个元素,删除一个区间内所有元素。时间复杂度均为O(N)

1) 删除单个元素

erase(it)即删除迭代器为it处的元素。

#include <cstdio>
#include <vector>

using namespace std;

int main()
{
    vector<int> vi;
    for(int i=1;i<=5;i++)
        vi.push_back(i);
    vector<int>::iterator it=vi.begin();
    //注意 vector不支持it<vi.end()只能用!=代替
    vi.erase(it+2);  //删除vi[2]处的元素,即删除3
    for(int j=0;j<vi.size();j++)
    {
        printf("%d ",vi[j]);
    }
    return 0;
}

2)删除一个区间中所有元素

erase(first,last)即删除[first,last)内所有元素。

#include <cstdio>
#include <vector>

using namespace std;

int main()
{
    vector<int> vi;
    for(int i=1;i<=5;i++)
        vi.push_back(i);

    vi.erase(vi.begin(),vi.end());  //删除所有元素
    printf("%d\n",vi.size());
    return 0;
}

 

4 vector的常见用途

(1)存储数据

vector本身可以作为数组使用,而且在一些元素个数不很确定的场合可以很好地节省空间。

(2)用邻接表存储图

此处先留白,后续补实例

二 queue

queue可以看作数据结构中的队列,在STL中主要是一个先进先出的容器。

1 queue的定义

#include <queue>
using namespace std;

queue<typename> vi;   //typename可以是任意基本类型或容器

2 queue内容器元素的访问

由于队列本来就是一种先进先出的的限制性数据结构,因此在STL中只可以通过front()来访问队首元素或者通过back()来访问队尾元素。不可以随机访问也不可以使用迭代器。

#include <cstdio>
#include <queue>

using namespace std;

int main()
{
    queue<int> q;
    for(int i=1;i<=5;i++)
        q.push(i);   //push是进队操作
    printf("%d %d\n",q.front(),q.back());
    return 0;
}

3 queue常见函数

(1) push()

push(x) 将x元素入队,时间复杂度为O(1)

(2) front(),back()

分别获得队首元素和队尾元素,时间复杂度为O(1)

(3) pop()

令队首元素出队,时间复杂度为O(1)

#include <cstdio>
#include <queue>

using namespace std;

int main()
{
    queue<int> q;
    for(int i=1;i<=5;i++)
        q.push(i);   //push是进队操作
    q.pop();  //出队操作
    printf("%d\n",q.front());
    return 0;
}

(4) empty()

检测队列是否为空,若是,返回True,否则返回false,时间复杂度为O(1)

#include <cstdio>
#include <queue>

using namespace std;

int main()
{
    queue<int> q;
    for(int i=1;i<=5;i++)
        q.push(i);   //push是进队操作
    q.pop();  //出队操作
    if(q.empty()==false)
        printf("%d\n",q.front());
    else
        printf("no element in q\n");
    return 0;
}

(5) size()

返回queue中的元素,时间复杂度为O(1).

4 清空队列的操作

queue没有clear()函数

清空可以有3种方式

第一种:直接用空的队列对象赋值

第二种:遍历出队列

第三种:使用swap,这种是最高效的,定义clear,保持STL容器的标准

#include <cstdio>
#include <queue>

using namespace std;

int main()
{
    queue<int> q;
    for(int i=1;i<=5;i++)
        q.push(i);   //push是进队操作
    printf("%d\n",q.size());

    q=queue<int>();
    printf("%d \n",q.size());

    q.push(3);
    printf("%d\n",q.size());

    while(!q.empty())
        q.pop();
    printf("%d\n",q.size());

    q.push(4);
    printf("%d\n",q.size());

    queue<int> empty;
    swap(q,empty);
    printf("%d\n",q.size());

}

5 queue的常见用途

在实现广度优先遍历的时候,可以用到队列,不妨用STL中queue代替。

在使用pop()或front()函数前,必须用empty()来判断队列是否为空,否则可能出错。

STL中还有2个容器和队列有关,分别是双端队列(deque)和优先队列(priority_queue)。前者是首尾皆可插入和删除的队列,后者是使用堆实现的默认将当前队列的最大元素置于队首的容器。

 

三 priority_queue

priority_queue又名优先队列,其底层是用堆来实现的,在优先队列中,队首元素一定是当前队列中优先级最高的那一个。当往优先队列中添加元素时,优先队列的底层数据结构堆会随时调整结构,使得每次队首元素都是优先级最大的。

1 priority_queue的定义

#include <queue>
using namespace std;
priority_queue<typename> q;

2 元素的访问

#include <cstdio>
#include <queue>

using namespace std;

int main()
{
    priority_queue<int> q;
    q.push(3);
    q.push(2);
    q.push(6);
    printf("%d",q.top());
    return 0;
}

3 常用函数实例

(1)push()

push(x)将x入队,时间复杂度位O(logN),N位当前优先队列中元素个数。

(2)top()

获得队首元素,时间复杂度位O(1)

(3)pop()

令队首元素出队,时间复杂度位O(logN),N位当前优先队列中元素个数。

(4)empty()

判断当前队列是否为空,返回true为空,时间复杂度位O(1)

(5)size()

返回优先队列中元素个数,时间复杂度位O(1)

4 元素优先级的设置

(1)  基本数据类型的优先级设置

基本数据类型指int,double,char型等可以直接使用的数据类型,优先队列对它们的优先级设置一般是数字大的优先级越高,因此队首元素是优先队列元素最大的那个。对基本类型来说,有两种优先队列的定义。

priority_queue<int> q;
priority_queue<int,vector<int>,less<int> >q;

第二种定义方式多出了2个参数,一个是vector<int>,另一个是less<int>。其中vector<int>指的是承载底层数据结构堆的容器,若第一个参数是double,则第二个参数就相应变为vector<double>。第三个参数less<int>则是对第一个参数的比较类,less<int>表示数字大的优先级越大,而greater<int>表示数字小的优先级越大。

因此,若想让优先队列把最小元素放在队首,只需定义:

priority_queue<int,vector<int>,greater<int>> q;

.如下所示:

#include <cstdio>
#include <queue>

using namespace std;

int main()
{
    priority_queue<int,vector<int>,greater<int> > q;
    q.push(3);
    q.push(2);
    q.push(6);
    printf("%d",q.top());
    return 0;
}

(2)结构体的优先级设置

struct fruit{
    string name;
    int price;
};

如果希望按照水果价格的高低进行优先级的排序,便需要重载小于号“<”。

struct fruit{
    string name;
    int price;
    friend bool operator < (fruit f1,fruit f2)
    {
        return f1.price<f2.price;
    }
};

结构体中增加了一个友元函数。(只可以重载小于号,重载大于号会发生编译错误,f1>f2等价于f2<f1,f1==f2等价于!(f1<f2)&&!(f2<f1))这里重载小于号还是小于的含义,因此以价格高的水果位优先级高(priority_queue永远把优先级高的元素放在前面)

下面重载小于号是大于的含义,以价格低的水果位优先级高

#include <iostream>
#include <queue>
#include <string>

using namespace std;

struct fruit{
    string name;
    int price;
    friend bool operator < (const fruit &f1,const fruit &f2)
    {
        return f1.price>f2.price;    //把价格低的优先级设为高,即队列中存放的是价格低到价格高的
    }
};

int main()
{
    priority_queue<fruit> q;

    fruit f1,f2,f3;
    f1.name="peach";
    f1.price=3;

    f2.name="apple";
    f2.price=4;

    f3.name="orange";
    f3.price=5;

    q.push(f1);
    q.push(f2);
    q.push(f3);
    cout<<q.top().name<<" "<<q.top().price<<endl;
    return 0;
}

四 deque

deque容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据:push_front()。

1 deque的定义

#include <deque>
using namespace std;
deque<int> c;

2 deque中元素的访问

与vector一样,deque也有2中访问方式,随机访问和迭代器访问

(1) 随机访问

c.at(pos)返回索引为pos的位置的元素,会执行边界检查,如果越界抛出out_of_range异常

c.operator[]下标运算符重载

c.front()返回c容器的第一个元素

c.back()返回c容器的最后一个元素

#include <cstdio>
#include <deque>

using namespace std;

int main()
{
    deque<int> dq;
    for(int i=1;i<=5;i++)
        dq.push_back(i);
    deque<int>::iterator it=dq.begin();
    printf("%d\n",dq.at(2));
    printf("%d\n",dq[2]);
    printf("%d\n",dq.front());
    printf("%d\n",dq.back());
    return 0;
}

(2) 迭代器访问

c.begin()返回指向第一个元素的迭代器

c.end()返回指向最后一个元素下一个位置的迭代器

c.rbegin()返回指向反向队列的第一个元素的迭代器(即原队列的最后一个元素)

c.rend()返回指向反向队列的最后一个元素的下一个位置(即原队列的第一个元素的前一个位置)

注意要使用反向迭代器时,必须要重新定义反向迭代器!reverse_iterator

#include <cstdio>
#include <deque>

using namespace std;

int main()
{
    deque<int> dq;
    for(int i=1;i<=5;i++)
        dq.push_back(i);
    deque<int>::iterator it=dq.begin();
    printf("正序:");
    for(;it!=dq.end();it++)
    {
        printf("%d ",*it);
    }
    printf("\n");

    deque<int>::reverse_iterator id=dq.rbegin();
    printf("倒序:");
    for(;id!=dq.rend();id++)
    {
        printf("%d ",*id);
    }

    return 0;
}

3 deque常见函数

(1)  c.size()/c.resize()/c.max_size()

第一个返回deque实际拥有的元素个数  第二个重新定义deque的大小   第三个返回容器可能存放元素的最大数值

#include <cstdio>
#include <deque>

using namespace std;

int main()
{
    deque<int> dq;
    for(int i=1;i<=5;i++)
        dq.push_back(i);
    deque<int>::iterator it=dq.begin();
    printf("size: %d %d\n",dq.size(),dq.max_size());

    dq.resize(dq.size()+5);
    printf("resize :%d %d\n",dq.size(),dq.max_size());

    dq.resize(15);
    printf("reresize :%d %d\n",dq.size(),dq.max_size());

    return 0;
}

(2)  c.clear()/c.empty

清除容器中所有元素

判断容器是否为空,若是,返回true

(3)  c.insert()

c.insert(pos,num)在pos位置插入元素num

c.insert(pos,n,num)在pos位置插入n个元素num

c.insert(pos,beg,end)在pos位置插入区间为[beg,end)的元素

#include <cstdio>
#include <deque>

using namespace std;

int main()
{
    deque<int> dq;
    for(int i=1;i<=5;i++)
        dq.push_back(i);

    printf("size: %d %d\n",dq.size(),dq.max_size());

    dq.insert(dq.end(),6);
    dq.insert(dq.begin(),0);
    dq.insert(dq.end(),3,7);

    int a[4]={8,9,10,11};
    dq.insert(dq.end(),a,a+2);
    deque<int>::iterator it=dq.begin();
    for(;it!=dq.end();it++)
        printf("%d ",*it);
    return 0;
}

(4)  c.erase()

c.erase(pos)删除pos位置的元素c.erase(beg,end)删除区间为[beg,end)的元素

c.erase(beg,end)删除区间为[beg,end)之间的元素

#include <cstdio>
#include <deque>

using namespace std;

int main()
{
    deque<int> dq;
    for(int i=1;i<=5;i++)
        dq.push_back(i);
    dq.erase(dq.begin());
    dq.erase(dq.begin(),dq.begin()+2);

    deque<int>::iterator it=dq.begin();
    for(;it!=dq.end();it++)
        printf("%d ",*it);
    return 0;

}

(5)  c.push_back(num)/c.pop_back()/c.push_front(num)/c.pop_front()

分别为从后端入队,出队,从前端入队,出队

出队前需判断是否队列为空。

(6)  c.swap()/swap()

c1.swap(c2)交换容器c1,c2;

swap(c1,c2)同上。

#include <cstdio>
#include <deque>

using namespace std;

int main()
{
    deque<int> dq1;
    for(int i=1;i<=5;i++)
        dq1.push_back(i);

    deque<int> dq2;
    for(int i=1;i<=5;i++)
        dq2.push_back(i*2);

    dq1.swap(dq2);
    deque<int>::iterator it=dq1.begin();
    for(;it!=dq1.end();it++)
        printf("%d ",*it);
    return 0;
}

可参见博客

五 stack

stack在数据结构中是一种先进后出的数据结构,在STL中是一个先进后出的容器。

1 stack的定义

#include <stack>
using namespace std;
stack<typename> s;   //typename可以是任意基本类型或容器

2 stack容器内元素的访问

由于栈本身就是一种后进先出的数据结构,在STL的stack容器中只可以通过top()来访问栈顶元素,不可以随机访问,当然也不可以使用迭代器。

#include <cstdio>
#include <stack>

using namespace std;

int main()
{
    stack<int> s;
    for(int i=1;i<=5;i++)
    {
        s.push(i);  //压栈处理
    }
    printf("%d\n",s.top());  //获得栈顶元素
    return 0;
}

3 常用函数

(1) push()

push(x)将元素x入栈,时间复杂度为O(1)

(2) top()

top()获得栈顶元素,时间复杂度为O(1)

(3) pop()

pop()弹出栈顶元素,时间复杂度为O(1)

(4) empty()

检验是否为空,为空返回true 否则返回false  时间复杂度为O(1)  在弹出元素之前或取栈顶元素之前要判断是否为空,不然会出错。

(5) size()

返回stack内元素个数,时间复杂度为O(1)

4 stack的常见用途

可以用栈来模拟递归。

猜你喜欢

转载自blog.csdn.net/qq_40123329/article/details/86559244