一. 链表
1.1 单向链表
1.1.1 传统链表
模型:
#include<iostream>
using namespace std;
//链表节点
struct LinkNode{
int data; //指向任何类型数据
LinkNode* next;
};
//链表结构体
struct LinkList{
LinkNode* head;
int size;
};
//初始化链表
LinkList* init(){
LinkList* list = new LinkList;
list -> size = 0;
list -> head = NULL;
return list;
}
//末尾插入
template<typename T>
void push_back(LinkList* list, T value){
LinkNode* node = new LinkNode;
node -> data = value;
node -> next = NULL;
if(list->head == NULL){
list->head = node;
list->size = 1;
}
else{
LinkNode* pt = list->head;
while(pt->next!=NULL)
pt = pt->next;
pt->next = node;
list->size++;
}
}
//指定位置插入
template<typename T>
void insert(LinkList* list, int pos, T value){
if(pos == list->size - 1)
push_back(list, value);
else if(pos==0){
LinkNode* node = new LinkNode;
node -> data = value;
node -> next = list->head;
list->head = node;
list->size++;
}
else{
int num = 0;
LinkNode* pt = list->head;
while(num<pos){
pt = pt -> next;
num++;
}
LinkNode* ps = pt -> next;
LinkNode* node = new LinkNode;
node -> data = value;
node -> next = ps;
pt->next = node;
list->size++;
}
}
//遍历链表的值
void print(LinkList* list){
if(list->head==NULL)
cout<<"NULL";
else{
LinkNode* pt = list->head;
while(pt->next!=NULL){
cout<<pt->data<<" ";
pt = pt->next;
}
cout<<pt->data;
}
}
int main(){
LinkList* list = init();
for(int i=0;i<5;i++)
push_back(list, i);
print(list);
insert(list, 2, 255);
cout<<endl;
print(list);
return 0;
}
结果:
1.1.2 企业链表
模型:
对比传统链表的优势: 数据类型可以任意更改,只需重构一下输出函数即可
#include <iostream>
#include<string>
using namespace std;
//企业链表
struct LinkNode {
LinkNode* next;
};
struct LinkList {
LinkNode head;
int size;
};
//初始化链表
LinkList* init() {
LinkList* list = new LinkList;
list->head.next = NULL;
list->size = 0;
return list;
}
//插入
void insert(LinkList* list, int pos, LinkNode* data) {
LinkNode* pre = &list->head;
for (int i = 0; i < pos; i++) {
pre = pre->next;
}
data->next = pre->next;
pre->next = data;
list->size++;
}
struct student {
LinkNode node;
string name;
int age;
};
void sss(LinkNode* node) {
student* s = (student*)node;
cout << s->name << " " << s->age << endl;
}
//打印
void print(LinkList* list) {
LinkNode* p = list->head.next;
if (list->size==0)
return;
while (p != NULL) {
student* s = (student*)p;
cout << s->name << " " << s->age << endl;
p = p->next;
}
}
int main() {
student s1, s2, s3, s4;
s1.age = 18; s2.age = 19; s3.age = 20; s4.age = 21;
s1.name = "AAA";
s2.name = "BBB";
s3.name = "CCC";
s4.name = "DDD";
LinkList* list = init();
insert(list, 0, (LinkNode*)&s1);
insert(list, 1, (LinkNode*)&s2);
insert(list, 2, (LinkNode*)&s3);
insert(list, 3, (LinkNode*)&s4);
print(list);
return 0;
}
结果:
1.2 list容器
简介: list是C++的标准容器之一,是一个双向链表
头文件: #include <list>
构造函数:
list<int> c0;
:创建一个空链表c0list<int> c1(3);
:创建一个含三个默认值(都为0)的链表c1list<int> c2(5,2);
:创建一个含五个值的链表c2,其中每个值都为二list<int> c3(c1);
:创建一个链表c3,并将c1拷贝给c3list<int> c4(c1.begin(), c1.end());
:使用迭代器将某个链表的一部分拷贝给c4
函数:
-
c.begin()
:返回指向链表第一个元素的迭代器 -
c.end()
:返回指向链表最后一个元素之后的迭代器 -
c.rbegin()
:返回逆向链表的第一个元素,即c链表的最后一个元素的迭代器 -
c.rend()
:返回逆向链表的最后一个元素的下一个位置,即c链表的第一个数据再往前的位置的迭代器 -
c.assign(n, num)
:将n个num拷贝赋值给链表c -
c.assign(c1.begin(), c1.end())
:将c1的部分拷贝给c,这里是将c1完全拷贝给c -
int a[3] = {1,2,3}; c.assign(a, a+3);
:将数组a的值赋给链表c -
c.swap(c1)
:交换c和c1里的元素,前提是两个链表的元素类型相同 -
c.front()
:返回链表c的第一个元素 -
c.back()
:返回链表c的最后一个元素 -
c.empty()
:判断链表是否为空,若为空返回true -
c.size()
:返回链表c中实际元素的个数 -
c.resize(num)
:重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除 -
c.resize(num, elem)
:重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除 -
c.max_size()
:返回链表c可能容纳的最大元素数量 -
c.clear()
:清除链表c中的所有元素 -
c.insert(pos,num)
: 在pos位置插入元素num。c.insert(pos,n,num)
:在pos位置插入n个元素num。c.insert(pos,c1.beg(),c1.end())
:在pos位置插入c1链表里区间为[beg,end)的元素。pos 、beg 、end 都是迭代器类型
-
c.push_back(num)
:在末尾增加一个元素num -
c.pop_back()
:删除末尾的元素 -
c.push_front(num)
:在开始位置增加一个元素num -
c.pop_front()
: 删除第一个元素 -
c.emplace_front(num)
:在容器头部插入一个元素num, 该函数和 push_front() 的功能相同,但效率更高 -
c.emplace_back(num)
:在容器尾部直接插入一个元素num, 该函数和 push_back() 的功能相同,但效率更高 -
emplace()
:在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高 -
c.erase(pos)
:删除pos位置的元素(pos为迭代器),返回下一个数据的位置 -
c.erase(beg, end)
:删除[beg, end)区间内的元素,返回下一个数据的位置 -
c.remove(elem)
:删除容器中所有与elem值匹配的元素 -
c.reverse()
:反转链表 -
c.unique()
:删除容器中相邻的重复元素,只保留一个 -
c.merge(c2)
:合并2个有序的链表到c中并使之有序,释放c2 -
c.splice(c.begin(), c2)
:将c2插入到c1的begin位置,释放c2 -
c1.splice(c1.beg,c2,c2.beg,c2.end)
:c2的[beg,end)位置的元素连接到c1的beg位置并且释放c2的[beg,end)位置的元素 -
c.sort()
:将链表排序,默认升序 -
c.sort(compare)
:自定义回调函数实现自定义排序例:
bool compare(int a, int b){ return a > b; } c.sort(compare); //降序排列
二、栈
头文件 :#include <stack>
和其它容器相比,stack是一类存储机制简单、提供成员函数较少的容器:
成员函数 | 作用 |
---|---|
empty() | 当栈为空时返回true,否则返回false |
size() | 返回stack中元素个数 |
top() | 返回栈顶元素的引用,如果栈为空,程序会报错 |
push(val) | 将val元素压入栈顶 |
pop() | 删除栈顶元素 |
top() | 返回栈顶元素 |
c.swap(c1) | 交换两个栈的元素 |
三、map容器
头文件 :#include <map>
map容器类似python的字典类型,是使用键值对来存储数据。在map中,所有元素都会根据键值自动排序。底层是用二叉树实现,不允许容器中出现重复的键值。
构造 :
map<key, value> v;
(例:map<string, int> v
,其中键的类型为string,值的类型为int)
插入 :
map里的每个元素都是关联的键值对,在c++里属于pair类型
pair<string ,int> p("Tom", 20);
pair<string, int> p = make_pair("Tom", 20);
访问key值:
cout<<p.first;
//结果为Tom
cout<<p.second;
//结果为20
使用p.first
和p.second
来访问每一项的键和值
所以,map容器在插入时:
v.insert(pair<string,int>("Tom", 20));
v.insert(make_pair("Tom", 20));
注意:使用insert插入时,如果新插入的key值在map中已经存在,则不会执行
数组插入:
v["Tom"] = 20;
和insert不同的是,如果新插入的key值在map中已经存在,则更新value值
不建议使用数组形式去插入,建议用于查找和更新 ,这是由于当键不存在时,会自动为该键赋0值并插入。
删除 :
v.erase(v.begin());
删除指定位置元素v.erase(v.begin(), v.end());
删除指定区间元素v.erase(key);
删除指定key值元素
清空 :
v.clear();
查找 :
v.find(key);
若找到返回key值处的迭代器,否则返回迭代器v.end()
统计 :
v.count(key)
统计键为key的元素个数,在map里要么为0,要么为1
大小和交换 :
v.size();
返回容器中元素数目v.empty();
若容器为空返回truev.swap(v1);
交换v和v1的元素
排序 :
按键值从大到小排序:
class compare
{
public:
bool operator()(int v1,int v2)
{
return v1>v2;
}
}
map<int,int,compare> v;
四、vector容器
构造函数 :
-
vector<int> v;
无参构造 -
vector<string> v(10, "hello")
创建含10个元素的动态数组v,其中每个元素都为"hello" -
vector<int> v(10,20);
创建含10个元素的动态数组v,其中每个元素都为20 -
vector<int> v(100);
初始化一个100个元素的动态数组,每个元素默认为0 -
vector<string> v(20);
初始化一个20个元素的动态数组,每个元素默认为空字符串 -
区间构造:
vector<int> v2(v1.begin(), v1.end());
拷贝构造:
vector<int> v2(v1);
赋值 :
v2 = v1;
v2.assign(v1.begin(), v1.end());
v2.assign(3, 100)
// v2 = {100, 100, 100}
添加删除元素 :
-
v.push_back(elem);
向v的末尾插入elem -
v.pop_back();
删除末尾元素 -
v.insert(pos, elem);
向pos处插入elem, pos为迭代器v.insert(pos, 2, 20);
向pos处插入2个20, pos为迭代器v.insert(pos, v1.begin(), v1.end());
在pos处插入另一个数组的[begin,end)区间的数据 -
v.erase(pos);
删除pos处元素,pos为迭代器 -
v.erase(beg, end);
删除[beg, end)区间元素 -
v.clear();
清空所有元素
遍历 :
v.at(int pos);
返回pos处元素的引用,这里的pos为下标,而不是迭代器v.front();
返回首元素的引用v.back();
返回尾元素的引用v.begin();
第一个元素的迭代器v.end();
指向最后一个元素的下一个位置v.rbegin();
迭代器,指向最后一个元素v.rend();
迭代器,指向第一个元素之前的位置
其它 :
-
v.size();
返回v中元素个数 -
v.capacity();
返回当前容器的容量值 -
v.swap(v1);
交换两个容器的内容 -
v.empty();
若容器为空返回true -
v.max_size();
返回容器能容纳的最大数量 -
v.reserve(n);
给v预分配存储空间为n,即capacity的值 ,但是没有给这段内存进行初始化。v.reserve(n, val);
给v预分配存储空间为n, 并将值初始化为val需要注意的是:reserve 函数分配出来的内存空间,只是表示vector可以利用这部分内存,但vector不能有效地访问这些内存空间,访问的时候就会出现越界现象,导致程序崩溃。
-
v.resize(n[, elem]);
重新指定容器的长度为n,若变长,则以默认值填充(若指定了参数elem
,则以elem
填充),若变短则容器末尾元素会被删除。调用这个函数之后,就可以引用容器内的对象了。
五、string容器
初始化 :
-
string s1(s2);
-
string s1 = s2;
-
string s1("value");
-
string s1(num, 'c');
//num个c组成的字符串 -
char* str = "value"; string s1(str);
//string(const char *s),使用字符串常量来初始化 -
string s1(str, index);
//将字符串str内始于位置index的部分作为s1的初值 -
string s1(str, index, len);
//将字符串str内始于index且长度为len的部分作为字符串的初值 -
string s1(s2.begin(), s2.end());
-
string s(str, chars_len) ;
// 将str字符串前chars_len个字符作为字符串s的初值 -
s.~string();
//销毁所有字符,释放内存
查找与添加 :
int find(const string & str,int pos =0) const
//查找str第一次出现位置,从pos开始查找
int find(const char* s,int pos = 0) const
//查找s第一次出现的位置,从pos开始查找
int find(const char* s,int pos = 0,int n)
//从pos位置查找s的前n个字符第一次出现的位置
int find(const char c,int pos = 0) const
//查找字符c第一次出现的位置
int rfind(const string & str,int pos =0) const
//查找str最后一次出现位置,从pos开始查找
int rfind(const char* s,int pos = 0) const
//查找s最后一次出现的位置,从pos开始查找
int rfind(const char* s,int pos = 0,int n)
//从pos位置查找s的前n个字符最后一次出现的位置
int rfind(const char c,int pos = 0) const
//查找字符c最后一次出现的位置
string & replace(int pos,int n,const string& str)
//替换从pos开始n个字符为字符串str
string & replace(int pos,int n,const char * s)
//替换从pos开始的n个字符为字符串s
string & append(const char *s)
//把字符串s连接到当前字符串的结尾
string & append(const char *s,int n)
//把字符串的前n个字符连接到当前字符串结尾
string & append(const string & s)
//同operator +=()
string & append(const string &s,int pos,int n)
//把字符串s从pos开始的n个字符连接到当前字符串结尾
string & append(int n,char c)
//在当前字符串结尾添加n个字符c
s.push_back(str); s.pop_back();
插入与截取 :
string & insert(int pos,const char *s)
//插入字符串
string & insert(int pos,const string & str)
//插入字符串
string & insert(int pos,int n,char c)
//在指定位置插入n个字符c
str = s.substr(int pos, int n);
//返回s中从pos开始的n个字符组成的字符串
删除 :
string & erase(int pos,int n = npos)
//删除从pos开始的n个字符
g & s)//同operator +=()
string & append(const string &s,int pos,int n)//把字符串s从pos开始的n个字符连接到当前字符串结尾
string & append(int n,char c)` //在当前字符串结尾添加n个字符c
s.push_back(str); s.pop_back();
插入与截取 :
string & insert(int pos,const char *s)
//插入字符串
string & insert(int pos,const string & str)
//插入字符串
string & insert(int pos,int n,char c)
//在指定位置插入n个字符c
str = s.substr(int pos, int n);
//返回s中从pos开始的n个字符组成的字符串
删除 :
string & erase(int pos,int n = npos)
//删除从pos开始的n个字符