STL容器介绍


title: STL容器
tags: vector,map,set
grammar_cjkRuby: true

数组

一维数组

  1. 定义
  • 静态 int array[100] = {1,2}
  • 动态 int* array = new int[100]; delete []array;  //分配了长度为100的数组array
    int* array = new int100; //为长度为100的数组array初始化前两个元素
    1.

二维数组

  1. 定义
    静态 int array10;  定义了数组,并未初始化
    静态 int array10 = { {1,1} , {2,2} };  数组初始化了array0及array1
    指针方式一 : 动态 int (array)[n] = new intm;
    delete []array; //n必须为常量
    指针方式二 :动态 int
    array = new int[m]; 
    for(i:m) array[i] = new int[n];
    for(i:m) delete []array[i];
    delete []array; 多次析构
    指针方式三 :动态 int* array = new intm;
    delete []array; 数组按行存储

  2. 访问
    指针形式:如二维数组valuei的访问:

    *(value[i] + j) 或

    (*(value + i))[j]

多维数组

int* array = new intm[4]; 只有第一维可以是变量,其他维数必须是常量,否则会报错

delete []array; 必须进行内存释放,否则内存将泄漏

数组作为参数

  1. 一维数组传递:
    void func(int* array);
    void func(int array[]);
  2. 二维数组传递:
    void func(int** array);
    void func(int (*array)[n]);  
    1

https://blog.csdn.net/gqb_driver/article/details/8886687

字符数组

char array[10] = “cnblogs”;

char array[] = “cnblogs”;

char *array=“cnblogs”;

char filename[100];

  1. 字符数组的copy
    strcpy(filename,“test”) ;
    相当于 sprinf(filenmae,"%s",“test”);
  2. 字符数组的连接
    strcat(filename, “//singleTestresult.txt”);

数组与指针关系

数组不等于指针

^[数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;

数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;

指向数组的指针则是另外一种变量类型,(在win32平台下,长度为4),仅仅意味着数组存放地址。]

内存泄露

我们定义了一个指针,然后给它赋予了一个地址值,然后又不再使用,但是没有delete,那么当给指针赋予其他的地址值时,原来的内存将无法释放,这就叫做内存泄露。


STL 容器

STL中的容器主要是两种:序列式容器和关联式容器。

序列式容器:vector list string等等

关联式容器:set map等

顺序容器

1.顺序容器的特点

  • vector,string,array 支持快速访问,但在中间插入元素费时。
  • list,forward_list 在任何位置添加或删除都很快速,但不支持随机访问。
  1. 顺序容器

  2. vector——可以作为string,cha,int,double等基本类型和自定义类的容器

  3. vec.size() //返回容器的大小

  4. vec.push_back() //在容器尾部插入元素

迭代器——iterator

vector::iterator v = vec.begin();

容器的容器

如vector<vector> lines={{1,2}};

Vector中使用结构体Struct

http://www.cnblogs.com/qyaizs/articles/2039101.html

重载结构体‘<’

#include <iostream>
#include <vector>
using namespace std ;
struct Rect
{
int Dir;
int Ini;
int Inj;
};
int main()
{
Rect rect;
vector<Rect>vec;

for (int i=0;i<10;i++)
{
rect.Dir=i;
rect.Ini=i+2;
rect.Inj=i*i;
vec.push_back(rect);
}
for (int i=0;i<vec.size();i++)
{
cout<<vec[i].Dir<<"\t"<<vec[i].Ini<<"\t"<<vec[i].Inj<<endl;
}

system("pause");
}

vector中插入pair

  • 声明 vector<pair<int,int> >VP;

  • 插入 VP.push_back(make_pair<int,int>(10,50));

  • 访问 vector<pair<int,int> > ::iterator iter;

    #include
    #include
    using namespace std;

    int main(){
    //声明
    vector<pair<int,int> >VP;

       //插入
       VP.push_back(make_pair<int,int>(10,50));
       VP.push_back(make_pair<int,int>(10,20));
       VP.push_back(make_pair<int,int>(20,40));
       VP.push_back(make_pair<int,int>(40,80));
       VP.push_back(make_pair<int,int>(30,90));
    
       vector<pair<int,int> > ::iterator iter; //访问vector
       iter=VP.begin();
       iter++;
       iter++;
      VP.erase(iter);  //删除元素(20,40)
    
     for(iter=VP.begin();iter!=VP.end();iter++)
      {
          cout<<iter->first<<"/t"<<iter->second<<endl;
      }
      return 0;
    

    }


C++ STL 之 vector 的 capacity 和 size 属性区别

size 是当前 vector 容器真实占用的大小,也就是容器当前拥有多少个容器。

capacity 是指在发生 realloc 前能允许的最大元素数,即预分配的内存空间。

当然,这两个属性分别对应两个方法:resize() 和 reserve()。

使用 resize() 容器内的对象内存空间是真正存在的。

使用 reserve() 仅仅只是修改了 capacity 的值,容器内的对象并没有真实的内存空间(空间是"野"的)。

此时切记使用 [] 操作符访问容器内的对象,很可能出现数组越界的问题。

下面用例子进行说明:

#include <iostream>
#include <vector>

using std::vector;
int main(void)
{
    vector<int> v;
    std::cout<<"v.size() == " << v.size() << " v.capacity() = " << v.capacity() << std::endl;
    v.reserve(10);
    std::cout<<"v.size() == " << v.size() << " v.capacity() = " << v.capacity() << std::endl;
    v.resize(10);
    v.push_back(0);
    std::cout<<"v.size() == " << v.size() << " v.capacity() = " << v.capacity() << std::endl;

    return 0;
}

运行结果为:(win 10 + VS2010)

注: 对于 reserve(10) 后接着直接使用 [] 访问越界报错(内存是野的),大家可以加一行代码试一下,我这里没有贴出来。

这里直接用[]访问,vector 退化为数组,不会进行越界的判断。此时推荐使用 at(),会先进行越界检查。

相关引申:

针对 capacity 这个属性,STL 中的其他容器,如 list map set deque,由于这些容器的内存是散列分布的,因此不会发生类似 realloc() 的调用情况,因此我们可以认为 capacity 属性针对这些容器是没有意义的,因此设计时这些容器没有该属性。

在 STL 中,拥有 capacity 属性的容器只有 vector 和 string。


数组与vector的互转

https://blog.csdn.net/Sagittarius_Warrior/article/details/54089242

一、数组转vector

float arrHeight[] = { 1.68,1.72,1.83,2.05,2.35,1.78,2.1,1.96 };
 vector<float> vecHeight(arrHeight, arrHeight+sizeof(arrHeight)/sizeof(float));

vector转数组

由于vector内部的数据是存放在连续的存储空间,vector转数组事实上只需要获取vector中第一个数据的地址和数据的长度即可。如果仅仅是传参,无需任何操作,直接传地址即可,如果要进行数据复制,可以借用内存拷贝函数“memcpy”。例如:

    float *buffer = new float[vecHeight.size()];
    if (!vecHeight.empty())
    {
        memcpy(buffer, &vecHeight[0], vecHeight.size()*sizeof(float));
    }

循环删除容器中元素

https://blog.csdn.net/Mrzhoug/article/details/51635361

STL中任何容器都可以使用迭代器进行元素的遍历,当需要在遍历中删除某些元素时,容器中元素的布局(位置或者排序)会随之改变,当前迭代器所指示的元素也会发生变化,这时继续递增或者递减迭代器进行后续元素的遍历时就要特别小心。

https://blog.csdn.net/magictong/article/details/4074381

1、 自己迭代实现删除:

vector<int>::iterator itVec = vectInt.begin();
// 删除所有值为1022的元素
for ( ; itVec != vectInt.end(); )
{
 // 删除
 if (*itVec == 1022) 
  itVec = vectInt.erase(itVect);
 else
  ++itVec;
}

list循环删除

list<int>::iterator itList = listInt.begin();
// 删除所有值为1022的元素
for ( ; itList != listInt.end(); )
{
 // 删除
 if (*itList == 1022) 
  listInt.erase(itList++);
 else
  ++itList ;
}

3、关联式容器循环删除

for(iter = map.begin(); iter != map.end()?

{

if (shouldDelete(*iter))

map.erase(iter++);

else

++iter;

}

使用stl算法库中的remove remove_if来进行删除:

调用STL中算法 remove或者remove_if的陷阱,并没有真正的删除(你可以此时调用size方法来验证这一点),然后应该使用erase删除残余数据,remove或者remove_if的实现原理是用后面 未被删除的元素 来覆盖前面 应该被删除的元素。返回值ForwardIterator指向经移除后的最后元素的下一位置。如vector{0,1,2,3,3,4},执行remove(),希望移除所有值为3的元素,结果为{0,1,2,4,3,4},返回值ForwardIterator指向第5个元素。即:

0 1 2 3 3 4 移除前

0 1 2 4 3 4 移除后

^[谨慎对待操作STL容器的方法begin end size的优化,常见是将他们提取到循环的外部,如果你执行删除操作,可能导致错误,因为在进行删除操作的时候,容器的end,size很有可能随时变化。list容器的end一直不会变化,但是放在for里面,编译器也会优化。除非你能保证for循环中不会改变容器的大小。

]

pair

pair类型的操作

pair<T1, T2>p1; //创建一个空的pair对象,它的两个元素类型分别为T1、T2,采用值初始化

pair<T1, T2>p1(v1, v2); //创建一个pair对象,first成员被初始化为v1,second成员被初始化被v2

make_pair(v1,v2); //v1,v2创建一个新的pair对象,其元素类型分别是v1,v2的类型

p1 < p2; //两个pair对象之间的小于运算,其定义遵循字典次序

p1 == p2; //如果两个pair对象的first和second成员依次相等,则两个对象相等

p.first; //p中的first的公有数据成员

p.second; //p中的second的公有数据成员

1)pair的创建和初始化,与顺序容器类似,如下:

	pair<string, string> anon;//调用默认构造函数来初始化
	pair<string, int> word_count;
	pair<string, vector<int>> line;
 
	pair<string, string> author("jim", "weshon"); //定义时提供初始化
	typedef pair<string, string> Author;
	Author product("marcel", "Product");

2)pair对象的操作,支持== 、< ,first、second成员的访问,如下

	string firstbook;
	if (author.first == "jim" && author.second == "weshon")
	{
		firstbook = "stephon hero"; //
	}

3)生成新的pair对象,如下:

	pair<string, string> next_author;
	string first, second;
	while (cin >> first >> second)
	{
		next_author = make_pair(first, second);
		//pair<string, string> next_author(first, second); //与上面创建类似

map

map和multimap的作用,这两种数据类型在存储数据时,会根据pair<>的first成员进行排序,不同的是前者将不会插入对first成员重复的结构,后者可以

map和set不允许为同一个键添加第二个元素,但是还有支持一个键对应多个实例的multimap和multiset类型:

【multimap】支持同一个键多次出现的map类型

【multiset】支持同一个键多次出现的set类型

map的构造函数:

map<k,v>; //创建一个名为m的空map对象,其键和值的类型分别为k和v

map<k,v>m(m2); //创建m2的副本m,m与m2必须具有相同的键类型和值类型

map<k,v>m(b,e); //创建map类型的对象m,存储迭代器b和e标记的范围内的所有元素的副本,元素的类型必须能转换为pair<const k, v>

map中键类型的约束,键必须支持“<”操作符,键不允许修改

map<k,v>::key_type //索引的键的类型

map<k,v>::mapped_type //键所关联的值的类型

map<k,v>::value_type //pair类型,first成员具有map<const k, v>::key_type类型,second成员具有map<const k, v>::mapped_type类型

map<string, int> word_count;
	map<string, list<size_t>> line_count;
	map<vector<int>::iterator, int> vcmap;
	//map<list<int>::iterator, int> lsmap; //error, list can't support < operator

map插入数据

第一种:用insert函数插入pair数据

Map<int, string> mapStudent;

mapStudent.insert(pair<int, string>(1, “student_one”));

my_Map.insert(make_pair(“d”,4));

2

第二种:用insert函数插入value_type数据

typedef map<int,string>:: valType

Map<int, string> mapStudent;

mapStudent.insert(map<int, string>::value_type (1, “student_one”));

第三种:用数组方式插入数据

Map<int, string> mapStudent;

mapStudent[1] = “student_one”;

mapStudent[2] = “student_two”

第四种: 利用迭代器插入

m.insert(beg,end); //beg和end标记元素范围的迭代器,元素类型必须为m.value_type,返回值为void

m.insert(iter,e); //e是一个用在m上的value_type类型的值,不在插入以iter为起点搜索新元素的位置;返回值为一个迭代器指向m中具有给定元素键的元素

string word = "";
	while (cin >> word)
	{
		if (word == "@")
			break; 
			
		//word_count.insert(map<string, int>::value_type("Anna", 1));
		//word_count.insert(make_pair("Jim", 1));
		//typedef map<string, int>::value_type valType;
		//word_count.insert(valType("COM", 1));
		pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, 1));
		if (!ret.second)
		{
			++ret.first->second;
		}
		//++word_count[word];
	}

查找并读取元素

m.count(k);返回m中k的出现次数

m.find(k);如果m容器中存在按k索引的元素,则返回指向该元素的迭代器;不存在的话,返回超出末端的迭代器

if (word_count.count("jim"))
	{
		cout << "jim = " << word_count["jim"] << endl;
	}
	
	map_iter = word_count.find("tom");
	if (map_iter != word_count.end())
	{
		word_count["tom"] = 250;
		cout << "tom = " << map_iter->second << endl;
	}

删除元素

m.erase(k); //删除m中关键字为k的元素;返回size_type类型的值,表示删除的元素个数

m.erase§; //删除m中迭代器p指向的元素;p必须存在,且不等于m.end();返回void类型

m.erase(b,e); //删除一段范围内的元素,由迭代器b和e标记;返回为void类型

word_count.erase(“jim”);

map返回值只可能是0或者1,返回0表示不存在

map对象的迭代遍历

map<string, int>::iterator map_iter = word_count.begin(); //iter: pair<const string, int> 上面已有

	map_iter = word_count.begin();
	for ( ; map_iter != word_count.end(); ++map_iter)
	{
		cout << map_iter->first << " " << map_iter->second << endl;

从map中删除元素

m.erase(k),删除 m 中键为 k 的元素。返回 size_type 类型的值,表示删除的元素个数(要么是0,要么是1);

m.erase§ //从 m 中删除迭代器 p 所指向的元素。p 必须指向 m 中确实存在的元素,而且不能等于 m.end()。返回 void

m.erase(b, e),从 m 中删除一段范围内的元素,该范围由迭代器对 b 和 e 标记。b 和 e 必须标记 m 中的一段有效范围:即 b 和 e 都必须指向 m 中的元素或最后一个元素的下一个位置。而且,b 和 e 要么相等(此时删除的范围为空),要么 b 所指向的元素必须出现在 e 所指向的元素之前。返回 void 类型

set

  • set 不支持下标操作符,而且没有定义 mapped_type 类型。
  • 在 set 容器中,value_type 不是 pair 类型,而是与 key_type 相同的类型。
  • 它们指的都是 set 中存储的元素类型。这一差别也体现了 set 存储的元素仅仅是键,而没有所关联的值。
  • 与 map 一样,set 容器存储的键也必须唯一,而且不能修改。

添加元素

set 容器的定义和使用:以一段范围的元素初始化 set 对象,或在 set 对象中插入一组元素时,对于每个键,事实上都只添加了一个元素。

// define a vector with 20 elements, holding two copies of each number from 0 to 9
  vector<int> ivec;
  for (vector<int>::size_type i = 0; i != 10; ++i) {
      ivec.push_back(i);
      ivec.push_back(i); // duplicate copies of each number
  }
  // 对于vector中的重复元素,只插入一个。
  set<int> iset(ivec.begin(), ivec.end());
  cout << ivec.size() << endl;      // prints 20
  cout << iset.size() << endl;      // prints 10</int></int></int>

3

查找元素

set.find()

set.count()

  iset.find(1)     // returns iterator that refers to the element with key == 1
  iset.find(11)    // returns iterator == iset.end()
  iset.count(1)    // returns 1
  iset.count(11)   // returns 0

删除元素

hashmap介绍

https://blog.csdn.net/q_l_s/article/details/52416583

https://bbs.csdn.net/topics/320066836


  1. 数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针,而且在其失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。 ↩︎

  2. m.insert(e); //e是m上的value_type类型,value_type 并非元素的类型,而是描述键及其关联值类型的 pair 类型。如果键不在m中,则插入新元素,在的话,保持m不变;返回值为一个pair类型的对象,包含指向键位e.first的元素的map迭代器以及一个bool类型的对象,表示是否插入了该元素 ↩︎

  3. 往set中添加元素:与 map 容器的操作一样,带有一个键参数的 insert 版本返回 pair 类型对象,包含一个迭代器和一个 bool 值,迭代器指向拥有该键的元素,而 bool 值表明是否添加了元素。使用迭代器对的 insert 版本返回 void 类型。 ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值