C++ STL 总结(八)

前言

  • 参考黑马 c++ 笔记
  • 案例代码都亲自测试过

Q&A

  • 记录 使用过程中常犯错误:
  1. 容器中 size() 函数:unsigned int size();用作计算时:(int)v.size() - 1;最好转成有符号型,防止size = 0时,计算结果为正无穷(坑)。

一、STL介绍

1.1 STL 诞生

  • 长久以来,软件界一直希望建立一种可重复利用的东西
  • C++的 面向对象泛型编程思想,目的就是复用性的提升
  • 大多情况下,数据结构和算法都未能有一套标准导致被迫从事大量重复工作
  • 为了建立 数据结构和算法的一套标准,诞生了STL

1.2 STL 基本概念

  • STL(Standard Template Library,标准模板库)
  • STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
  • 容器算法之间通过迭代器进行无缝连接。
  • STL几乎所有的代码都采用了模板类或者模板函数

1.3 STL 六大组件

  STL大体分为六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  1. 容器:各种数据结构,如vector、list、deque、set、map等用来存放数据
  2. 算法:各种常用的算法,如sort、 find、 copy、 for_ each 等
  3. 迭代器:扮演了容器与算法之间的 胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略。
  5. 适配器::一种用来修饰容器或者仿函数或迭代器接口的东西。
  6. 空间配置器:负责空间的配置与管理。

1.4 STL 容器、算法、迭代器

1.4.1 容器:置物之所也

STL容器就是将运用最广泛的一些数据结构实现出来

常用的数据结构:数组、链表、树、栈、队列、集合、映射表 等

容器分为序列式容器和关联式容器两种:

  1. 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
  2. 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

1.4.2 算法:解决某个具体问题

有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)

算法分为:质变算法 和 非质变算法:

  1. 质变算法:是指运算过程中 会更改区间内的元素的内容。例如拷贝、替换、删除 等

  2. 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值 等

1.4.3 迭代器:容器和算法之间粘合剂

提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

每个容器都有自己专属的迭代器 eg: vector::iterator it = v.begin()

迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针。

种类功能支持运算
输入迭代器对数据的只读访问只读,支持++、==、! =
输出迭代器对数据的只写访问只写,支持++
前向迭代器读写操作,并能向前推进迭代器读写,支持++、==、! =
双向迭代器读写操作,并能向前和向后操作读写,支持++、–
随机访问迭代器读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器读写,支持++、–、[n]、 -n、<、<=、>、>=

常用的容器中迭代器种类为 双向迭代器 和 随机访问迭代器

1.4.3 容器算法迭代器初识

代码1:利用迭代器遍历容器的三种方法

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;

// for_each 的回调函数
// int 类型与vector一样
void myprint(int val)
{
	cout << val << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v;
	v.push_back(12);
	v.push_back(13);
	v.push_back(14);
	v.push_back(15);

	// 方法一
	vector<int>::iterator it_begin = v.begin();	// 指向第一个位置
	vector<int>::iterator it_end = v.end();	// 指向最后一个元素的下一个元素 -> 取其值无意义
	while(it_begin != it_end)
	{
		cout << *it_begin << endl;
		it_begin++;
	}

	// 方法二
	for(vector<int>::iterator it=v.begin();it!=v.end();it++)
	{
		cout << *it << endl;
	}

	// 方法三:依赖<algorithm>标准算法头文件
	for_each(v.begin(),v.end(),myprint);

	system("pause");
	return 0;
}

代码2:迭代器访问数组成员

vector<person>::iterator it = v.begin();
  • it:以指向person对象的指针形式 进行访问 eg:it->name;
  • *it:以 person对象形式 进行访问 eg:it.name;
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
#include <ostream>
using namespace std;

// 自定义数据类型
class person
{
public:
	person(string name="null",int age=18):name_(name),age_(age){}
	string name_;
	int age_;
};

void test()
{
	// 存放对象本身
	vector<person> v;
	person p1("xiaohong",22);
	person p2("xiaohua",23);
	person p3("xiaolan",24);
	person p4("xiaoqi",22);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	for(vector<person>::iterator it = v.begin();it!=v.end();it++)
	{
		cout << (*it).name_ <<" + "<< it->age_ << endl;
	}
}

void test1()
{
	// 存放对象指针
	vector<person*> v;
	person p1("xiaohong",22);
	person p2("xiaohua",23);
	person p3("xiaolan",24);
	person p4("xiaoqi",22);
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	
	for(vector<person*>::iterator it = v.begin();it!=v.end();it++)
	{
		cout << (*it)->name_ <<" - "<< (*it)->age_ << endl;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{	
	test();
	test1();
	system("pause");
	return 0;
}

代码3:容器嵌套容器 - 相当于多维数组

void test2()
{
	vector<vector<int>> big;

	vector<int> small;
	vector<int> small2;
	vector<int> small3;
	vector<int> small4;

	for(int i = 0;i < 4;i++)
	{
		small.push_back(i + 1);
		small2.push_back(i + 2);
		small3.push_back(i + 3);
		small4.push_back(i + 4);
	}

	big.push_back(small);
	big.push_back(small2);
	big.push_back(small3);
	big.push_back(small4);

	for(vector<vector<int>>::iterator it=big.begin();it!=big.end();it++)
	{
		for(vector<int>::iterator it2=(*it).begin();it2!=(*it).end();it2++)
		{
			cout << *it2 << " ";
		}
		cout << endl;
	}

}

二、常用容器

2.1 string 容器

2.1.1 本质

  string是C++风格的字符串,而string本质上是一个类

2.1.2 特点

  • string 类内部封装了很多成员方法 例如:查找find,拷贝copy,删除delete,替换replace,插入insert

  • string 管理 char* 所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

【Tip:string 和 char* 区别】

  • char* 是一个指针
  • string 是一 个类,类内部封装了char*,管理这个字符串,是一个char* 型 的容器。

2.1.3 代码

转成 C 字符串 (char*)

const char* c_str ( ) const;

转成 c 格式的字符串,以 \0 结尾,由 char* 指针指向。

int main ()
{
  char * cstr, *p;
  
  string str ("Please split this phrase into tokens");
  cstr = new char [str.size()+1];	//'\0'
  strcpy (cstr, str.c_str());

  // cstr now contains a c-string copy of str

  p=strtok (cstr," ");
  while (p!=NULL)
  {
    cout << p << endl;;
    p=strtok(NULL," ");
  }

  delete[] cstr;  
  return 0;
}

string 迭代器
  • string::iterator it1 = str.begin();
  • string::reverse_iterator it2 = str.rbegin();
构造函数原型
  1. string(); //创建一个空的字符串 例如: string str;
  2. string(const char* s); //使用字符串 s 初始化
  3. string(const string& str); //使用一个string对象初始化另一个string对象
  4. string(int n, char C); //使用n个字符c初始化
int _tmain(int argc, _TCHAR* argv[])
{	
	string s1;
	string s2 = "hello";
	string s3 = s2;
	string s4(9,'0');
	cout << s1 << endl << s2 << endl << s3 << endl << s4 << endl;
	system("pause");
	return 0;
}
赋值操作函数
  1. string& operator=(const char* s);
    //char* 类型字符串赋值给当前的字符串
  2. string& operator=(const string &s);
    //把字符串s赋给当前的字符串
  3. string& operator=(char C);
    //字符赋值给当前的字符串
  4. string& assign(const char *s);
    //把字符串s赋给当前的字符串.
  5. string& assign(const char *s,int n);
    //把字符串 s 的前 n 个字符赋给当前的字符串
  6. string& assign(const string &s);
    //把字符串 s 赋给当前字符串
  7. string& assign(int n, char C);
    //用 n 个字符 c 赋给当前字符串
int _tmain(int argc, _TCHAR* argv[])
{	
	string s1,s2;
	
	s1 = "hello";
	s2 = s1;
	s1 = 'D';
	s1.assign("Hello",3);
	s1.assign(s2);
	s2.assign(8,'A');
	
	cout << s1 << endl << s2 << endl;
	system("pause");
	return 0;
}
字符串拼接函数
  1. string& operator+= (const char* str) ;
    // 重载+=操作符
  2. string& operator+= (const char c) ;
    // 重载+=操作符
  3. string& operator+= (const string& str) ;
    // 重载+=操作符
  4. string& append (const char *s) ;
    // 把字符串s连接到当前字符串结尾
  5. string& append (const char *s, int n) ;
    // 把字符串s的前n个字符连接到当前字符串结尾
  6. string& append (const string &s) ;
    // 同operator+=(const string& str)
  7. string& append(const string &s, int pos, int n);
    // 字符串s中从pos开始的n个字符连接到字符串结尾
int _tmain(int argc, _TCHAR* argv[])
{	
	string s1,s2;
	s1 += "hello";
	s1 += 'D';
	s2 += s1;
	s1.append("word!");
	s1.append("who are you!",7);
	s2.append(s1);
	s1.append(s2,3,5);

	cout << s1 << endl << s2 << endl;
	system("pause");
	return 0;
}
字符串 查找和替换 函数
  1. int find(const string& str, int pos = 0) const;
    //查找str第一次出现位置,从pos开始查找
  2. int find(const char* s, int pos = 0) const;
    //查找s第一次出现位置,从pos开始查找
  3. int find(const char* s,int pos, int n) const;
    //从pos位置查找s的前n个字符第一次位置
  4. int find(const char C, int pos = 0) const;
    //查找字符c第一次出现位置
  5. int rfind(const string& str, int pos = npos) const;
    //查找str最后一次位置,从pos开始查找
  6. int rfind(const char* s,int pos = npos) const;
    //查找s最后一次出现位置,从pos开始查找
  7. int rfind(const char* s,int pos, int n) const;
    //从pos查找s的前n个字符最后一次位置
  8. int rfind(const char C,int pos = 0) const;
    //查找字符c最后一次出现位置
  9. string& replace(int pos, int n, const string& str);
    //替换从pos开始n个字符为字符串str
  10. string& replace(int pos, int n,const char* s);
    //替换从pos开始的n个字符为字符串s
int _tmain(int argc, _TCHAR* argv[])
{	
	string s1,s2;
	int num;
	s1 = "What's your Name? Are you OK?";
	s2 = "you";
	num = s1.find(s2,2);	 // num = s1.find(s2);
	num = s1.find("you",2);	 // num = s1.find("you");
	num = s1.find("your",2,3);
	num = s1.find('s',2);    // num = s1.find('s');
	num = s1.rfind("you",2); // num = s1.rfind("you");
	num = s1.rfind(s2,2);	 // num = s1.rfind(s2);
	num = s1.rfind("your",2,3);
	num = s1.rfind('s',2);	 // num = s1.rfind('s');
	string s3 = "her";
	s3 = s1.replace(7,4,s3);
	s3 = s1.replace(7,4,"her");
	
	cout << s1 << endl << num << endl;
	system("pause");
	return 0;
}

总结:

  • find 查找是从左往后,rfind 从右往左
  • find 找到字符串后返回查找的第一个字符位置, 找不到返回 -1
  • replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串
字符串 比较 函数

比较方式:

  • 字符串比较是 按字符的ASCIl码 进行对比
  1. int compare(const string &s) const;
    // 与字符串s比较

  2. int compare(const char *s) const;|
    // 与字符串s比较

= 返回 0
> 返回 1
< 返回 -1

int _tmain(int argc, _TCHAR* argv[])
{	
	string s1 = "hello",s2 = "Hello";
	string s3 = s1;
	int num;
	num = s1.compare(s2);
	//num = s1.compare(s3);

	cout << num << endl;
	system("pause");
	return 0;
}

总结:字符串对比主要是用于 比较两个字符串是否相等,判断谁大谁小的意义并不是很大。

字符串 存取 函数

string 中 单个字符存取 方式有两种

  1. char& operator[](int n);
    //通过 [ ] 方式取字符
  2. char&pat(int n);
    //通过 at 方法获取字符
int _tmain(int argc, _TCHAR* argv[])
{	
	string s1 = "hello";

	char c1 = s1.at(1);
	for(int i = 0;i < s1.size();i++)
		cout << s1[i] << " ";
	s1[0] = 'H';
	s1.at(1) = 'E';

	cout << s1 << endl;
	system("pause");
	return 0;
}
字符串 插入、删除 函数
  1. string& insert(int pos, const char* s);
    //插入字符串
  2. string& insert(int pos, const string& str);
    //插入字符串
  3. string& insert(int pos, int n, char c);
    //在指定位置插入 n 个字符 c
  4. string& erase(int pos, int n = npos);
    /删除从 pos 开始的 n 个字符
int _tmain(int argc, _TCHAR* argv[])
{	
	string s1 = "Hello";
	string s2 = "?? Tom: ";

	s1.insert(5,",word!");
	s1.insert(0,s2);
	s1.erase(0,3);

	cout << s1 << endl;
	system("pause");
	return 0;
}

总结:插入和删除的起始下标都是从 0 开始

字符串 子串 函数

原型:

std:string substr(const size_t_Off = 0U, const size_t_Count = 4294967295U) const

简写:

string substr(int pos =0, int n = npos) const;

// 返回由 pos 开始的 n 个字符组成的字符串

int _tmain(int argc, _TCHAR* argv[])
{	
	string s1 = "wujieem@qq.com";
	string s2 = s1.substr(0,s1.find('@'));
	string s3 = s1.substr(1);		// 第二个参数可省略

	cout << s2 << endl;
	system("pause");
	return 0;
}

2.2 vector 容器

2.2.1 本质

  vector数据结构和数组非常相似,也称为 单端数组

vector 与普通数组区别:

  • 不同之处在于 数组是静态空间,而 vector 可以动态扩展

动态扩展:

  • 并不是在原空间之后续接新空间,而是 找更大的内存空间,然后将原数据拷贝新空间,释放原空间

2.2.2 特点

在这里插入图片描述
vector 容器的迭代器是支持 随机访问的迭代器

2.2.3 代码

构造函数原型
  1. vector<T> v;
    //采用模板实现类实现,默认构造函数
  2. vector(v. begin(), v.end());
    //将 v [begin(), end()) (前闭后开) 区间中的元素拷贝给本身。
  3. vector(n, elem);
    //构造函数将n个elem拷贝给本身。
  4. vector(const vector &vec);
    //拷贝构造函数。新的 vector容量不一定相同
void printVector(vector<int> &v)
{
	for(vector<int>::iterator it = v.begin();it != v.end();it++)
	{
		cout << *it;
	}
	cout << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	for(int i=0;i<10;i++)
		v1.push_back(i);
	printVector(v1);

	// 迭代器初始化数组
	vector<int> v2(v1.begin(),v1.end()-1);
	printVector(v2);

	// 用 m 个 相同元素初始化数组
	vector<int> v3(5,189);
	printVector(v3);
	vector<vector<bool>> visited(3, vector<bool>(5, 0));

	vector<int> v4(v1);
	printVector(v4);

	system("pause");
	return 0;
}
赋值操作函数
  1. vector& operator=(const vector &vec);
    //重载等号操作符
  2. assign(beg, end);
    //将[beg, end)区间中的数据拷贝赋值给本身。
  3. assign(n, elem);
    //将n个elem拷贝赋值给本身。
void printVector(vector<int> &v)
{
	for(vector<int>::iterator it = v.begin();it != v.end();it++)
	{
		cout << *it;
	}
	cout << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	for(int i=0;i<10;i++)
		v1.push_back(i);
	//printVector(v1);

	vector<int> v2;
	v2 = v1;
	printVector(v2);

	v2.assign(v1.begin(),v1.end()-2);
	printVector(v2);

	v2.assign(3,996);
	printVector(v2);

	system("pause");
	return 0;
}
反向迭代器

v.rbegin() v.rend() 可以反向遍历数组。

#include <iostream>
#include <vector>
using namespace std;

void main()
{
	vector<int> res;
	res.push_back(10);
	res.push_back(20);
	res.push_back(30);
	res.push_back(40);

	// 1. 反向迭代器遍历
	for(vector<int>::reverse_iterator it = res.rbegin();
									  it != res.rend();it++)
		cout << *it << endl;

	// 2. 反向迭代器赋值
	vector<int> res2(res.rbegin(), res.rend());
}
emplace 系列函数(emplace_back)
#include<iostream>
#include<vector>
#include<string>
using namespace std;

class Person{
private:
	string _name; int _age;
public:
	Person(string name,int age)
	{
		this->_age = age;
		this->_name = name;
	}
};
void main()
{
	vector<Person>	arr;
	// 直接传入参数 - 没有临时对象构造
	arr.emplace_back("wujiw",23);
	// 先构造一个临时对象,再将该对象`拷贝`到数组
	arr.push_back(Person("wujiw", 23));
}
容量、大小 操作函数
  1. empty();
    //判断容器是否为空
  2. capacity();
    //容器的容量
  3. unsigned int size();
    //返回容器中元素的个数
  4. resizp(int num);
    //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
    //如果容器变短,则末尾超出容器长度的元素被删除。
  5. resize(int num, elem);
    //重新指定容器的长度为num;若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除。
int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	for(int i=0;i<10;i++)
		v1.push_back(i);

	cout << v1.empty() << endl;	// 0
	cout << v1.capacity() << endl;	// 13
	cout << v1.size() << endl;	// 10

	v1.resize(15,8);	// 默认用 0 填充
	printVector(v1);
	v1.resize(5);
	printVector(v1);

	system("pause");
	return 0;
}
插入、删除 操作函数
  1. push_ back(ele);
    //尾部插入元素ele
  2. pop_ back();
    /删除最后一个元素
  3. insert(const_iterator pos, ele);
    //迭代器指向位置pos插入元素ele
  4. insert(const_iterator pos, int count ,ele);
    //迭代器指向位置pos插入count个元素ele
  5. erase(const_iterator pos);
    //删除迭代器指向的元素
  6. erase(const_iterator start, const_iterator end);
    //删除迭代器从start到end之间的元素
  7. clear();
    //删除容器中所有元素
int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	//for(int i=0;i<10;i++)
	//v1.push_back(i);
	v1.push_back(1);
	v1.push_back(2);
	v1.pop_back();

	v1.insert(v1.begin(),3);
	printVector(v1);

	v1.insert(v1.end(),3,4);
	printVector(v1);

	v1.erase(v1.begin());
	printVector(v1);

	v1.erase(v1.end()-1,v1.end());
	printVector(v1);

	v1.clear();
	printVector(v1);

	system("pause");
	return 0;
}
数据存取 操作函数
  1. at(int idx);
    //返回索引 idx 所指的数据

  2. operator[ ];
    //返回索引 idx 所指的数据

  3. front();
    //返回容器中第一个数据元素

  4. back();
    //返回容器中最后一个数据元素

int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	for(int i=0;i<10;i++)
		v1.push_back(i);

	v1[3] = 8;
	v1.at(4) = 9;
	cout << v1.at(3) << endl;
	cout << v1[4] << endl;
	
	cout << v1.front() << endl;
	cout << v1.back() << endl;

	system("pause");
	return 0;
}
互换容器 操作函数

swap(dec); // 将 vec 与 本身的元素互换

int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	for(int i=0;i<10;i++)
		v1.push_back(i);
	printVector(v1);

	vector<int> v2;
	for(int i=0;i<10;i++)
		v2.push_back(i+10);
	printVector(v2);

	v1.swap(v2);
	printVector(v1);
	printVector(v2);

	system("pause");
	return 0;
}

用 “ swap + 匿名对象 ” 收缩内存:

int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	for(int i=0;i<1000;i++)
		v1.push_back(i);
	cout << v1.capacity() << " " << v1.size() <<endl; // 1066 100
	
	v1.resize(3);
	cout << v1.capacity() << " " << v1.size() <<endl; // 1066 3

	// 收缩内存
	vector<int>(v1).swap(v1);	// 此处 匿名对象容量、大小为 3 , 3
	cout << v1.capacity() << " " << v1.size() <<endl; // 3 3

	system("pause");
	return 0;
}

【注】匿名对象只在当前行有效;当前行结束就释放。

预留空间 操作函数

reserve(int len);

// 容器预留 len 个元素长度,预留位置不初始化,元素不可访问。

问题:

int _tmain(int argc, _TCHAR* argv[])
{
	vector<int> v1;
	int *p = NULL;
	int num = 0;
	for(int i=0;i<10000;i++)
	{
		v1.push_back(i);
		if(p != &v1[0]){
			p = &v1[0];
			num++;		// 重新分配地址次数
		}
	}
	cout << num << endl;// 不预留空间24次 - 开销很大
	system("pause");
	return 0;
}

解决:

vector<int> v1;
v1.reserve(10000);	// 预留空间
···
cout << num << endl;// 1次

总结: 如果数据量较大,可以一开始利用 reserve 预留空间

2.3 deque 容器

2.3.1 认识

  双端数组,可以对头端进行插入删除操作

deque与vector区别:

  • vector 对于头部的插入删除效率低,数据量越大,效率越低
  • deque 相对而言,对头部的插入删除速度回比 vector
  • vector 访问元素时的速度会比 deque 快。这和两者内部实现有关【vector 是一段连续空间】

2.3.2 特点

deque 内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据。

中控器维护的是每个缓冲区的地址 [ 访问时先找到段缓冲区的地址,再找到具体数据(慢) ],使得使用 deque 时像一片连续的内存空间

在这里插入图片描述

在这里插入图片描述
PS:deque 容器的迭代器也是支持随机访问的。

2.3.3 代码

构造函数操作函数
  1. deque<T> deqT;
    //默认构造形式
  2. deque(beg, end);
    //构造函数将[beg, end)区间中的元素拷贝给本身。
  3. deque(n, lelem);
    //构造函数将n个elem拷贝给本身。
  4. deque(const deque &deq);
    //拷贝构造函数
void printDeque(const deque<int> &v)
{	// const deque 要用 const_iterator 访问
	for(deque<int>::const_iterator it = v.begin();it != v.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	deque<int> d1;
	for(int i=0;i<10;i++)
	{d1.push_back(i);}
	printDeque(d1);

	deque<int> d2(d1.begin(),d1.end()-1);
	printDeque(d2);
	deque<int> d3(4,19);
	printDeque(d3);
	deque<int> d4(d2);
	printDeque(d4);
	
	system("pause");
	return 0;
}

注意: const deque 要用 const_iterator 访问。

const deque &v;
deque::const_iterator it;

赋值操作函数
  1. deque& operator=(const deque &deq);
    // 重载等号操作符
  2. assign(beg,end);
    // 将[beg, end)区间中的数据拷贝赋值给本身。
  3. assign(n, elem);
    // 将n个elem拷贝赋值给本身。
int _tmain(int argc, _TCHAR* argv[])
{
	deque<int> d1;
	for(int i=0;i<10;i++)
	{d1.push_back(i);}
	printDeque(d1);

	deque<int> d2;
	d2 = d1;
	printDeque(d2);

	d2.assign(d1.begin(),d1.end()-2);
	printDeque(d2);

	d2.assign(3,19);
	printDeque(d2);

	system("pause");
	return 0;
}

总结:赋值 和 vector一样

大小操作函数
  1. deque.empty();
    // 判断容器是否为空
  2. deque.size();
    // 返回容器中元素的个数
  3. deque.resize(num);
    // 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
    // 如果容器变短,则末尾超出容器长度的元素被删除。
  4. deque.resize(num, elem);
    // 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
    // 如果容器变短,则末尾超出容器长度的元素被删除。

【区别】没有容量的概念,不够了就去开辟,只要将地址添加到中控器中。

int _tmain(int argc, _TCHAR* argv[])
{
	deque<int> d1;
	for(int i=0;i<10;i++)
	{d1.push_back(i);}
	printDeque(d1);
	
	cout << d1.empty() << endl;
	cout << d1.size() << endl;

	d1.resize(20);
	printDeque(d1);

	d1.resize(5);
	printDeque(d1);
	system("pause");
	return 0;
}
插入、删除 操作函数

两端插入操作:

  1. push_back(elem);
    //在容器尾部添加一个数据
  2. push_front(elem);
    //在容器头部插入一个数据
  3. pop_back();
    //删除容器最后一个数据
  4. pop_front();
    //删除容器第一个数据

指定位置操作:

  1. insert(pos,elem);
    //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
  2. insert(pos,n,elem);
    //在pos位置插入n个elem数据,无返回值。
  3. insert(pos, beg, end);
    //在pos位置插入 [beg,end) 区间的数据,无返回值。
  4. clear(p);
    //清空容器的所有数据
  5. erase(beg, end);
    //删除 [beg,end) 区间的数据,返回下一个数据的位置。
  6. erase(pos);
    //删除pos位置的数据,返回下一个数据的位置。

总结:插入和删除提供的位置是迭代器。

数据存取 操作函数
  1. at(int idx);
    //返回索引 idx 所指的数据
  2. operator[];
    //返回索引 idx 所指的数据
  3. front();
    //返回容器中第一个数据元素
  4. back();
    //返回容器中最后一个数据元素
数据排序 操作函数

sort(iterltor beg, iterator end);

//对 beg 和 end 区间内元素进行排序

int _tmain(int argc, _TCHAR* argv[])
{
	deque<int> d1;
	d1.push_back(2);
	d1.push_back(1);
	d1.push_back(4);
	d1.push_back(3);
	d1.push_back(5);
	printDeque(d1);
	// 排序
	sort(d1.begin(),d1.end());
	printDeque(d1);

	system("pause");
	return 0;
}

对于 支持随机访问的迭代器 的容器,都可以利用sort算法直接对其进行排序。

2.4 案例:评委打分

#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
#include <string>
#include <deque>
#include <ctime>

class student {
public:
	student(string name,int score){
		this->name_ = name;
		this->score_ = score;
	}
	string name_;
	int score_;
};

// 为 vector 创建 student 对象
void createStudent(vector<student> &vs) {
	string num = "ABCDE";
	string name = "Student";
	int score = 0;	//初始分数 0
	for (int i = 0; i < 5; i++) {
		student s1(name+num[i],score);
		vs.push_back(s1);
	}
}
// 打印 student 内容
void printvector(const vector<student> &v)
{
	for (vector<student>::const_iterator it = v.begin(); it != v.end(); it++)
	{
		cout << it->name_ <<" "<< it->score_ <<endl;
	}
}

void setscore(vector<student> &vs) {
	// 为每个学生打分、求平均分
	for (int j = 0; j < vs.size(); j++)
	{
		deque<int> scores;
		int all_num = 0;
		// 10个评委打分
		for (int i = 0; i < 10; i++)
		{
			int score = rand() % 41 + 60; // 60 - 100;
			scores.push_back(score);
		}
		// 从小到大排序
		sort(scores.begin(), scores.end());
		for (deque<int>::iterator it = scores.begin(); it != scores.end(); it++)
		{
			cout << *it << " ";
		}cout  << endl;
		// 去除最高、最低分
		scores.pop_back();
		scores.pop_front();
		// 求和
		for (deque<int>::iterator it = scores.begin(); it != scores.end(); it++)
		{
			all_num += *it;
			//cout <<  score << endl;
		}
		// 设置平均分
		vs[j].score_ = all_num/scores.size();
	}
}
int main()
{
	// 随机数种子
	srand((unsigned int)time(NULL));
	// 创建5个学生对象
	vector<student> vs;
	createStudent(vs);
	setscore(vs);	// 打分
	printvector(vs);

	return 0;
}

2.5 stack 容器

2.5.1 初始

stack 是一种 先进后出(First In Last Out,FILO) 的数据结构,它只有一个出口

在这里插入图片描述

2.5.2 特点

  • 栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为

2.5.3 代码

构造函数
  1. stack<T> stk;
    //stack采用模板类实现,stack对象的默认构造形 式
  2. stack(const stack &stk);
    //拷贝构造函数
赋值操作
  1. stack& operator=(const stack &stk);
    //重载等号操作符
数据存取
  1. push(elem);
    //向栈顶添加元素
  2. pop();
    //从栈顶移除第一个元素
  3. top();
    //返回栈顶元素
大小操作
  1. empty();
    //判断堆栈是否为空
  2. size();
    //返回栈的大小
#include <stack>

void main()
{
	stack<int> s1,s2;
	s1.push(100);
	s1.push(200);
	s1.push(300);
	s1.push(400);
	s2 = s1;
	cout << s1.size() << endl;
	while (!s2.empty()) {
		cout <<  s2.top() << endl;
		s2.pop();
	}
}

2.6 queue 容器

2.6.1 初识

Queue 是一种 先进先出(First In First Out,FIFO) 的数据结构,它有两个出口

在这里插入图片描述

2.6.2 特点

  • 队列容器允许从一端新增元素,从另一端移除元素
  • 队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为

2.6.3 代码

构造函数
  1. queue<T> que;
    //queue采用模板类实现,queue对象的默认构造形式
  2. queue(const queue &que);
    //拷贝构造函数
赋值操作
  1. queue& operator= (const queue &que);
    //重载等号操作符
数据存取:
  1. push(elem);
    //往队尾添加元素
  2. pop();
    //从队头移除第一个元素
  3. back();
    //返回最后一一个元素
  4. front();
    //返回第一个元素
    大小操作:
  5. empty();
    //判断队列是否为空
  6. size();
    //返回队列的大小.
#include <queue>

void main()
{
	queue<int> s1,s2;
	s1.push(100);
	s1.push(200);
	s1.push(300);
	s1.push(400);
	s2 = s1;
	cout << s1.size() << endl;
	while (!s2.empty()) {
		cout <<  s2.front() << " " << s2.back() << endl;
		s2.pop();
	}
}

2.7 list 容器(链表)

2.7.1 初识

  链表(list) 是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

  • 链表的组成:链表由一系列结点组成

  • 结点的组成:一个是存储数据元素的 数据域,另一个是存储下一个结点地址的 指针域

STL 中的链表是一个 双向循环链表

在这里插入图片描述

2.7.2 特点

  由于链表的存储方式并不是连续的内存空间(不能通过++等随机访问),因此链表 list 中的迭代器只支持前移和后移(pre\next 指针访问),属于 双向迭代器

优点:

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作快,修改指针即可,不需要移动大量元素

缺点:

  • 容器遍历速度,没有数组快(指针域,有寻址等过程)

  • 占用空间比数组大

  • 链表灵活,但是空间 (指针域) 和时间 (遍历) 额外耗费较大。

List有一个重要的性质, 插入操作和删除操作都不会造成原有 list 迭代器的失效,这在vector是不成立的。

总结:STL中 List 和 vector 是两个最常被使用的容器,各有优缺点。

2.8 优先队列 priority_queue

  • 优先队列中元素被赋予优先级。优先队列具有 最高级先出(first in, largest out) 的行为特征。

  • 优先队列 具有队列的所有特性,包括队列的 基本操作,只是 在这基础上添加了内部的一个排序,它 本质是一个堆实现的

2.8.1 原型

#include<queue>
priority_queue<Type, Container, Functional>
  1. Type 就是 数据类型

  2. Container 就是 容器类型

    Container 必须是用数组实现的容器,比如 vector,deque 等等,但 不能用 list。STL里面 默认用的是 vector

  3. Functional 就是 比较的方式

2.8.2 使用方法

// 升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
// 降序队列,大顶堆
priority_queue <int,vector<int>,less<int> > q;
  • greaterless 是 std 实现的两个 仿函数

2.8.3 内部方法

top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容

2.8.4 用 pair 做优先队列元素

  • pair 的比较规则:先比较第一个元素第一个相等比较第二个
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

int main()
{
    priority_queue < pair<int, int> > a;
    pair<int, int> b(1, 2);
    pair<int, int> c(1, 3);
    pair<int, int> d(2, 5);
    a.push(d);
    a.push(c);
    a.push(b);
    
    while (!a.empty())
    {
        cout << a.top().first << ' ' << a.top().second << '\n';
        a.pop();
    }
}

ret:

2 5
1 3
1 2

2.8.5 自定义类型做优先队列元素

#include <iostream>
#include <queue>
using namespace std;

// 方法 1
struct tmp1 // 运算符重载 <
{
    int x;
    tmp1(int a) {x = a;}
    bool operator< (const tmp1& a) const
    {
        return x < a.x; //大顶堆
    }
};

//方法2
struct tmp2 //重写仿函数
{
    bool operator() (tmp1 a, tmp1 b)
    {
        return a.x < b.x; //大顶堆
    }
};

int main()
{
    tmp1 a(1);
    tmp1 b(2);
    tmp1 c(3);
    
    priority_queue<tmp1> d;
    d.push(b);
    d.push(c);
    d.push(a);
    
    while (!d.empty())
    {
        cout << d.top().x << '\n';
        d.pop();
    }
    cout << endl;

    priority_queue<tmp1, vector<tmp1>, tmp2> f;
    f.push(b);
    f.push(c);
    f.push(a);
    while (!f.empty())
    {
        cout << f.top().x << '\n';
        f.pop();
    }
}
3
2
1
 
3
2
1

2.9 array

  • array 静态数组大小在定义时固定,位于 栈上
#include<iostream>
#include<array>
//#include<tuple>

using namespace std;

int main()
{
	std::array<double, 3> dbnew = { 10.1,10.2,10.3 };
	for (int i = 0;i < 3;i++)
	{
		cout << dbnew[i] << endl;
	}
}

2.10 tuple

  • tuple 静态数组包含不同的类型类型固定后必须赋值为相应的类型

#include<iostream>
#include<tuple>
#include<string>
using namespace std;
void main()
{
	tuple<string, int, double > mytuple("Guodong",18,99.9);
	cout<<get<0>(mytuple)<<' '<< get<1>(mytuple) <<' '<< get<2>(mytuple);
 
	cin.get();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值