【stl容器--实践操作】

前言

打怪升级:第15天
在这里插入图片描述

一、string

string类对string变量、字符串(char*)以及单个字符(char)一般都有对应的函数。
string本质上就是字符串,内部原理是char*(了解过C语言的朋友应该可以更好地理解下面的内容)

(一)初始化 、 赋值 、 拼接

c++重载的函数是非常多的,我们需要做的是先来见识一番,之后根据自己的习惯和需求掌握其中几种即可。
在这里插入图片描述

//  string四种初始化方式
string s1;    //  无参构造
string s2("hello string");  //  使用字符串初始化
string s3(s2);       //  使用同为string的s2初始化(拷贝构造)
string s4(10, 'w');  //  使用字符初始化
// 补充: string s5(s2.substr(0,10));   拷贝构造:s2从下标为0的位置开始往后10个字符

示例:
在这里插入图片描述

// string赋值方式  --  对 operator= 的重载
char* str="hello string";
string s6;
s6=str;
s6=s2;
s6='w';

// 使用函数赋值  assign()
s6.assign(str);   //  使用字符串赋值
s6.assign(str, n1);  //  从下标为n1处开始
s6.assign(str, n1, n2); //  从下标为n1处开始,前n2个字符
s6.assign(s2);
//s6.assign(s2, n1);  这个函数在vs中可以使用,但是在Dev上识别不出来,应该是vs对assign函数做了扩展。
//s6.assign(s2, n1, n2);
s6.assign(10, 'w');

在这里插入图片描述


(二)单个字符的访问和修改

[]、at()

string s1("hello");
for(int i = 0; i < s1.size(); i++)
{
	cout << s1.at(i) << ' ';  // 等同于 s1[i] 
}
cout << endl;

(三)插入和删除

insert 、erase

在这里插入图片描述

	string s1("hello");
	s1.insert(s1.size(), " 靓仔");
	cout << s1 << endl;
	s1.erase(5, s1.size() - 5);   //  如果第二个数据大于后面剩余的字符个数,那就只会把后面的字符全部删掉
	cout << s1 << endl;

运行实例:
在这里插入图片描述


(四)查找和替换

find、rfind、replace

find函数在找不到时会返回 string::npos
在这里插入图片描述

string s7;
int pos = s7.find("bit", n1, n2);  // 在s7中,从下标为n1的位置开始,前n2个字符中是否存在字符串"bit"
									//	找到了返回下标,找不到就返回EOF
s7.rfind();      //   从右往左查找,找最后一次出现的位置
s7="abcdefghigklmn";
s7.replace(2, 0, "0000");     //  替换下标为2的位置0个字符为字符串"0000",等价于:插入     

运行实例:
在这里插入图片描述


(五)比较

compare

等于:返回 0
大于: 返回 1
小于:返回 -1

void test05()
{
	string s1("hello");
	string s2("hello");
	cout << s1.compare(s2) << endl;
}

(六)获取子串

substr

在这里插入图片描述

	string s1 = "panda@csdn.com";
	int pos = s1.find('@');
	string s2 = s1.substr(0, pos);
	cout << "用户名:" << s2 << endl;

运行实例:在这里插入图片描述


二、vector

不定长的单头数组
在这里插入图片描述
在这里插入图片描述

(一)初始化和赋值 --(4+3)

=、assign

	//  初始化    --  4种
	vector<int>v1;  //  无参构造

	vector<int>v2(v1);   //  拷贝构造

	vector<int>v3(v1.begin(), v1.end());  // 通过区间初始化

	vector<int>v4(10, 8);   //  使用n个数字初始化

	//   赋值   --  3种
	cout << "赋值" << endl;
	vector<int>v5;
	v5 = v1;       //  operator=            

	vector<int>v6;
	v6.assign(v1.begin(), v1.end());  //  assign()函数,区间赋值

	vector<int>v8;
	v8.assign(10, 8);    //  使用n个数字赋值

运行实例:
在这里插入图片描述


(二)访问数据 – (4)

[]、at()、front、back

vector<int>v1;
	InitVector(v1);
	// operator[]
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << ' ';
	}
	cout << endl;

	// at()
	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1.at(i) << ' ';
	}
	cout << endl;

	// front(), back()
	cout << v1.front() << ' ' << v1.back() << endl;

	//补充: begin(), end()               iterator:迭代器
	for(vector<int>::iterator it = v1.begin(); it < v1.end(); it++)
	{
		cout << *it << ' ';
	}
	cout << endl;

运行实例:在这里插入图片描述


(三)插入、删除和清空

insert、erase、clear

vector<int>v1;
	InitVector(v1);
	PrintVector(v1);
	v1.pop_back();     //  尾删
	PrintVector(v1);

	v1.insert(v1.begin() + 2, 10);  //  在下标为2的位置插入一个 10
	PrintVector(v1); 
	v1.insert(v1.begin(), 2, 100);  //  在最前面插入两个 100
	PrintVector(v1);
	v1.erase(v1.end() - 1);  //  删除最后一个位置的数据
	PrintVector(v1);
	v1.erase(v1.begin(), v1.begin() + 2); // 删除 [0,2)之间的数据
	PrintVector(v1);
	v1.clear();				 // 清空
	PrintVector(v1);

运行实例:在这里插入图片描述


(四)大小和容量

size、capacity、resize

	vector<int>v1;
	InitVector(v1);
	v1.resize(12);    //  改变容器大小,空位默认用 0 填充
	bool temp = v1.empty(); //  判空
	cout << "size->" << v1.size() << endl;
	cout << "capacity->" << v1.capacity() << endl;
	v1.push_back(12);
	PrintVector(v1);

运行实例:
在这里插入图片描述


(五)交换

swap

	vector<int>v1;
	InitVector(v1);
	PrintVector(v1);
	vector<int>v2(6, 'w');
	PrintVector(v2);
	swap(v1, v2);
	PrintVector(v1);
	PrintVector(v2);

	// 收缩内存  --  借助匿名容器
	v1.assign(100000, 1);
	cout << "交换前:size->" << v1.size() << " capacity->" << v1.capacity() << endl;
	v1.resize(10);
	cout << "resize:size->" << v1.size() << " capacity->" << v1.capacity() << endl;
	vector<int>(v1).swap(v1);    //   匿名容器执行完当前语句后被系统回收
	cout << "交换后:size->" << v1.size() << " capacity->" << v1.capacity() << endl;

运行实例:
在这里插入图片描述


(六)预开辟空间

reserve

	cout << "系统自动更新capacity" << endl;
	vector<int>v1;
	int* pi = NULL;
	int num = 0;
	for (int i = 0; i < 100000; i++)
	{
		v1.push_back(i);
		if (pi != &v1[0])
		{
			pi = &v1[0];
			num++;
		}
	}
	cout << "重新开辟内存次数:" << num << endl;

	cout << "提前开辟好空间" << endl;
	vector<int>v2;
	v2.reserve(100000);  //  reserve是预开辟空间,但是实际大小还是0
	int* ppi = NULL;
	num = 0;
	for (int i = 0; i < 100000; i++)
	{
		v2.push_back(i);
		if (ppi != &v2[0])
		{
			ppi = &v2[0];
			num++;
		}
	}
	cout << "重新开辟内存次数:" << num << endl;

运行实例:
在这里插入图片描述
注意:如果发生了重新开辟内存,那么之前声明的迭代器就需要进行更新才能使用。


三、deque

双端数组(双端队列) – 操作选项同上面的vector
在这里插入图片描述


四、stack

:stack是一种非常简单的数据结构,只能从栈顶进出数据
它和vector并不一样,
vector是一个数组,虽然是单端数组,但是数组有的操作vector也有,比如遍历、排序、POS位置的插入和删除等
而stack则有更多的限制,只能访问栈顶的数据,如果想要访问后面的数据就需要将栈顶数据出栈,那么栈顶数据就丢失了,
由于栈的限制很多,所以它操作起来非常简单。(因为它能做的操作少啊)
在这里插入图片描述
在这里插入图片描述

class Person
{
public:
	Person(string name, int age)
	{
		this->_name = name;
		this->_age = age;
	}

	string _name;
	int _age;
};

//  stack
void test03()
{
	stack<Person>s;
	Person p1("宁姚", 32);
	Person p2("陈平安", 33);
	Person p3("天真", 5000);
	s.push(p1);              // 入栈
	s.push(p2);
	s.push(p3);
	cout << "size->" << s.size() << endl;   //  获取大小
	while (!s.empty())  //  判空
	{
		cout << "name->" << s.top()._name << "\tage->" << s.top()._age << endl;   //  获取栈顶数据
		s.pop();       //  出栈
	}
	cout << "size->" << s.size() << endl;
}

举个栗子:
在这里插入图片描述


五、queue

队列:queue和stack一样也是很简单的容器,它的要求是只能在队头出数据,只能在队尾入数据
在这里插入图片描述在这里插入图片描述


六、list

链表:list (底层为双向循环链表)
在这里插入图片描述

构造、赋值、交换、大小和容量、插入、删除和清空、访问数据、

vector有的list基本上都有,并且用法完全相同(但是底层原理是不同的)。
list更多的操作:

  1. 删除:remove
  2. 翻转:reverse
  3. 排序:sort
    在这里插入图片描述

bool Mycompare(int a, int b)
{
	// 升序
	//return a < b;

  //  降序
	return a > b;
}

void test04()
{
	list<int>l1;
	for (int i = 10; i > 5; i--)
	{
		l1.push_back(i);
		l1.push_front(1);
	}
	cout << "改变前:";
	for_each(l1.begin(), l1.end(), Print<int>);  // Print()是自己写的函数模板
	cout << endl;

	cout << "翻转后:";
	l1.reverse();     //   翻转
	for_each(l1.begin(), l1.end(), Print<int>);
	cout << endl;

	// 所有不支持随机访问的容器,不可以使用标准算法,既sort(),因此这里容器自己提供了 list.sort()算法
	// 对于自定义类型必须指定排序函数,不然编译器不知道如何排序
	cout << "升序列:";
	l1.sort();        //  排序  --  升序
	for_each(l1.begin(), l1.end(), Print<int>);
	cout << endl;

	cout << "降序列:";
	l1.sort(MyCompare);  //  排序  --  降序:自己提供一个排序函数
	for_each(l1.begin(), l1.end(), Print<int>);
	cout << endl;

	cout << "移除 1:";
	l1.remove(1);     //  移除
	for_each(l1.begin(), l1.end(), Print<int>);
	cout << endl;
}

运行实例:
在这里插入图片描述

list没有的操作:

  1. 对数据的访问:at()、[ ]
    因为list底层是链表,无法像数组一样通过下标进行访问。
list<int>::iterator it = li.begin(); // iterator 是迭代器,这里it可以当做一个指针使用,但是它并不是指针
it = it + 1; //  error ,链表不支持随机访问
it++;   //  right
it--;   //  right, 双向链表可以找到前后节点
//  这里说的是设立的思想,不过至于为什么支持 ++ 不支持 it + 1,那是因为大佬们在设计的时候只重载了 ++,而没有重载 + 号,
//  这是为了防止使用者 有了 +1, 就想用 +2 、 +300等的随机访问操作,这在链表中是无法使用的。

七、set、multiset

集合:set – (底层为红黑树
特点:在加入数据时会自动对数据进行排序,因此也叫关联式容器;
使用时只需包含头文件< set >;
set中不能出现重复的数据(重复数据不会被插入);
multiset可以出现重复数据。

(一)初始化、赋值

=

set的初始化和赋值比较简单,初始化只有默认构造和拷贝构造,赋值只有:=

set<int>s1;    //  默认构造
set<int>s2(s1); // 拷贝构造

     //   赋值
set<int>s3;
s3=s2;

(二)插入、删除

insert、erase、clear

由于set在放入数据时会自动排序,无需设置将数据放到首部、尾部还是中间某个位置,
因此set容器放入数据只需要一个insert即可。

set<int>s4;
s4.insert(10);
s4.insert(20);
s4.insert(50);
s4.insert(40);
s4.insert(30);
for(set<int>::iterator it=s4.begin(); it!=s4.end(); ++it)
	cout<< *it<<' ';
	cout<<endl;

运行实例这里是引用

(三)查找、统计

find、count

在set中由于不能出现重复数据,因此count(key)只有0或1两个值。
在这里插入图片描述

void test06()
{
    set<int>s;
    s.insert(1);
    s.insert(5);
    s.insert(4);
    s.insert(2);
    s.insert(3);
    set<int>::iterator it = s.find(2);
    if (it == s.end())
        cout << "did not find" << endl;
    else
        cout << *it << endl;

    it = s.find(20);
    if (it == s.end())
        cout << "did not find" << endl;
    else
        cout << *it << endl;
   
    cout << "number of 2->" << s.count(2) << endl;
}

运行实例:
在这里插入图片描述


(四)大小、交换

size、swap

(五)排序

set在插入数据时直接对数据进行排序,默认是升序序列,如果我们要改变排序规则就需要在插入数据之前进行,
这里我们可以写一个仿函数(重载小括号),这里需要将仿函数写在类里面,因为set后面的括号里需要的是一个类型。
在这里插入图片描述

class MyCompare
{
public:
	template<typename t>
	bool operator()(t v1, t v2)const  //  vs中这个 const不能省略
	{
		return v1 > v2;
	}
};

int main()
{
	set<int, MyCompare>s1;
	s1.insert(50);
	s1.insert(20);
	s1.insert(40);
	s1.insert(30);
	s1.insert(10);
	for (set<int, MyCompare>::iterator it = s1.begin(); it != s1.end(); it++)
		cout << *it << ' ';
	cout << endl;

	return 0;
}

结果:
在这里插入图片描述

对于自定义类型数据,我们必须设置比较函数

class Person
{
public:
	
	Person(string name, int age)
	{
		this->_name = name;
		this->_age = age;
	}
	
	string _name;
	int _age;
};

class MyCompare
{
public:
		bool operator()(const Person& s1, const Person& s2)const  //  const都不可省
	{
		return s1._name.compare(s2._name) >= 0;
	}
};

int main()
{
	set<Person, MyCompare>s2;
	Person p1("陈平安", 32);
	Person p2("宁姚", 31);
	Person p3("裴钱", 24);
	s2.insert(p1);
	s2.insert(p2);
	s2.insert(p3);
	for (set<Person, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++)
		cout << "name->" << (*it)._name << "age->" << it->_age << endl;
	return 0;
}

示例:
在这里插入图片描述


拓展:

pair

对组:pair,内置函数模板很少,这里我们了解一下它的初始化、赋值以及访问。
在这里插入图片描述

	vector<pair<string, int> >array;
    pair<string, int>Person("崔巉", 240);   //  初始化
    array.push_back(Person);
    Person = make_pair("左右", 230);   // 赋值
    array.push_back(Person);
    Person = make_pair("刘十六", 2000);
    array.push_back(Person);
    Person = make_pair("齐静春", 200);
    array.push_back(Person);
    Person = make_pair("老秀才", 300);
    array.push_back(Person);
    for(int i=0; i<array.size(); ++i)
    cout << array[i].first << ' ' << array[i].second << endl;  // 访问数据

运行实例:
在这里插入图片描述


八、map、multimap

图、查找表:map (底层为红黑树
map中所以元素都是pair;
pair中的第一个元素为key(键值),起到索引作用,第二个元素为value(实值);
map在插入数据时会按照键值进行升序排序,因此map也是关联式容器。
优点:
可以通过键值快速查找value。
map和multimap的区别:
map中不能出现重复的key,而multimap中可以(与set和multiset区别一样);
注意这里只限制key,而map中如果出现重复的value是可以的。

(一)初始化、赋值

初始化:默认构造和拷贝构造
赋值:operator=

void PrintMap(map<const int, int>m)
{
    for (map<int, int>::iterator it = m.begin(); it != m.end(); ++it)
    {
        cout << "key->" << it->first << " value->" << it->second << endl;
    }
    cout << endl;
}

void test04()
{
    // 创建map数组  --  底层为二叉树
    map<const int, int>m1;   //  vs2022中不加const会报错
   // pair<int, int>tmp;
    //tmp = make_pair(4, 40);
    m1.insert(pair<const int, int> (1, 10)); // 插入数据,插入过程中会自动按照key值排序
    m1.insert(pair<const int, int>(4, 40));
    m1.insert(pair<const int, int>(2, 20));
    m1.insert(pair<const int, int>(3, 30));
    PrintMap(m1);

    map<const int, int>m2(m1);  //  拷贝构造
    PrintMap(m2);

    //  赋值
    map<const int, int>m3;
    m3 = m1;
    PrintMap(m3);
}

运行实例:
在这里插入图片描述


(二)插入、删除

insert、erase、clear

在这里插入图片描述

void test05()
{
    // 这里讲解4中插入的方法
    map<const int, int>m1;
    // 第一种
    m1.insert(pair<const int, int>(1, 10));
    // 第二种
    m1.insert(make_pair(3, 30));
    // 第三种
    m1.insert(map<const int, int>::value_type(2, 20));
    // 第四种
    m1[4] = 40;  //  key=4, value=40

    //  第四种一般用来插入数据,因为可以通过键值来访问value
    // 不建议用来访问数据,因为如果访问的键值不存在,编译器会自动创建 value为0的对组
    cout << m1[10] << endl;

    PrintMap(m1);
}

运行实例:
在这里插入图片描述


(三)查找、统计

find、count

同set


(四)大小、交换

size、empty、swap

同set


(五)排序

class MyCompare
{
public:
    bool operator()( int v1,  int v2)const
    {
        return v1 > v2;
    }
};

void test08()
{
    map<const int, int, MyCompare>m1;
    m1.insert(make_pair(1, 10));
    m1.insert(make_pair(2, 20));
    m1.insert(make_pair(3, 30));

    for (map<int, int, MyCompare>::iterator it = m1.begin(); it != m1.end(); ++it)
    {
        cout << "key->" << it->first << " value->" << it->second << endl;
    }
    cout << endl;
}

运行实例:
在这里插入图片描述


总结

  • 查找:find
    只有string类模板的find函数找到时返回值为下标,找不到则返回 string::npos
    其他自带find函数以及标准函数返回值都为迭代器,找不到则返回 end() 。

  • 单个数据访问:at
    我们一般会觉得有了下标访问操作符就足够了,at会有些多余,
    但是对于set、map这些容器是不能通过 [ ] 进行访问的,这时就需要at() 了,由此可知at() 更加通用。

  • 迭代器
    eg: vector v1;
    vector::iterator it = v1.begin();
    这里 it 就是一个迭代器,可以当做指针使用,但是实际上并不是指针,而是类模板,
    迭代器通过重载指针的操作符 -> 、++、–等封装了一个指针;
    是一个“可遍历STL( Standard Template Library)容器内全部或部分元素”的对象。
    因此 it 不能使用 NULL初始化,而且v1.begin() 的返回值也不能赋给整形指针。
    参考文章:迭代器和指针

  • insert、erase后的迭代器失效
    失效原因:

  1. insert扩容后会出现野指针;
  2. erase后指针指向改变(可以接收返回值);
    解决方法:使用insert和erase之后为了防止迭代器失效,我们应该防止再次使用,下次使用时应再次查找。
  • resize 和 reserve
    resize是调整大小,往大了调整后空位默认补0,
    reserve是预开辟空间,但是此时的大小时没有改变的,比如说:
    你现在有100块钱,银行说可以给你贷款50万,那么这50万就是你可以使用的,但是这些钱实际上还是属于银行的,你实际拥有的钱只有100块,你去相亲的时候不能说你现在拥有50万存款,
    这里也是一样的,我们重新创建的变量,此时size=0, 我们reserve(100),这是“贷款”,并不是真的给你100的内存空间,你的实际大小还是0,
    只有使用resize(100),调整了自身的大小之后才能够说我拥有50万的存款。
    因此,在使用copy、merge等需要将数据移动到一个新的容器时,需要确保该容器拥有足够的空间,如果不够,就使用resize调整容器的大小。

  • 大小
    只有顺序表(数组)有容量这一说,链表是想要添加数据就找一块区域即可,因此可以说整个内存都是它的容量。
    所以list、set、map这些只有size()函数,但都没有内置capacity() 函数。

  • 声明:函数原型截图来源于黑马程序员的c++教学视频,本文也是在看过了视频之后进行的自我总结。

评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值