目录
一.双向链表:
1.简介:
链表即将一串数字连接在一起,
形成一个闭环
,如下图所示:
为了使链表形成一个闭环,就需要将一个元素的左右元素都定义清楚,所以每个元素都会
包括两个值
:(1) 该元素左边的值;
(2) 该元素右边的值;
2.实现:
(1)构建节点结构体:
需要将一个节点的左边元素与右边元素封装在一起。
struct pot
{
int left;
int right;
};
此处定义了一个pot的结构体,里面包含它左侧的值与右侧的值;
(2)初始化:
如果不进行初始化,就会成为以下示意图:
这种情况最后遍历查找时就会发生无限循环,没有停止条件的情况。
所以我们引入一个“ 0 ”元素:
这样 0 就是整个链表的起止点了,具体代码:
struct pot p[105];
//先开15个结构体
p[0].left=0;
//等于自身
p[0].right=0;
//等于自身
相当于0自己连着自己;
(3)添加元素:
添加元素分为添加到某元素左边与某元素右边,此处仅以将第 j元素加到第 i 元素右侧为例。
首先如第二个图 :将 p[ i ] 的右值定义为p[ j ]右值;
然后如第三个图: 将 p[ i ] 的右值定义为 j ;
接着如第四个图: 将p[ i ]右值转移到 j ;
最后如第五个图: 建立p[ j ]与右值的关系;
完整代码如下:
p[j].right=p[i].right;
p[j].left=i;
p[i].right=j;
p[p[j].right].left=j;
持续输入数值后,就可以构成一个双向线表了。
(4)去除元素:
原理与加入相同,只用倒着看上方示例图。
直接上代码,依然以将 i 右侧的 j 除去:
q[i].right=q[j].right;
q[q[j].right].left=i;
//将j的左右两元素连接
3.优缺点:
(1)优点:
线性
表的顺序存储结构,在存、读数据时,不管是哪个位置,时间复杂度都是0(1);而插入或删除时,时间复杂度都是0(n)。这就说明,它比较适合元素个数不太变化,而更多是存取数据的应用。
(2)缺点:
A.如果我们要插入或者删除的元素是在第一个位置,那么无疑的,我们需要移动大量的元素来完成这样的操作,而且限于线性表长度必须小于数组长度,如果我们需要插入大量数据,那么很难保证空间是否充足,而如果我们要删除大量数据的时候,无疑又会造成空间的浪费
B.顺序存储空间容易满,出现上溢,程序访问容易出问题,顺序存储结构下,存储空间不便扩充;
C.顺序存储空间的分配问题,分多了浪费,分少了空间不足上溢。
4.例题:
看一道十分典型的线性表,这道题也是最基础的类型。
这道题就只用区分一下向左插入还是向右插入:
#include<iostream>
using namespace std;
struct per{
int left;
int right;
int here; //用于记录是否输出
} p[100005];
int main(){
for(int i=0;i<100005;i++){
p[i].here=1;
}
int m,n;
int place,v;
p[0].left=0;
p[0].right=0;
//初始化0
p[1].left=0;
p[1].right=p[0].right;
p[0].right=1;
p[p[1].right].left=1;
//先将1号放在0右边
cin>>m;
for(int i=2;i<=m;i++){
cin>>v;
cin>>place;
if(place==1){
p[i].left=v;
p[i].right=p[v].right;
p[v].right=i;
p[p[i].right].left=i;
}
//向右插入
else{
p[i].left=p[v].left;
p[i].right=v;
p[v].left=i;
p[p[i].left].right=i;
}
//向左插入
}
cin>>n;
for(int j=0;j<n;j++){
int x;
cin>>x;
p[x].here=0;
}
//标记是否输出
for(int i=p[0].right;i!=0;i=p[i].right){
if(p[i].here==1&&p[i].right!=0) cout<<i<<" ";
else if(p[i].here==1&&p[i].right==0) cout<<i;
}
//从0开始向右输出
return 0;
}
这道题除去某元素时注意一下技巧,只用here标记一下是否在队列中。
二.map函数:
1.map的简介:
map的
实质其实是映射
,也就像数组,在定义
数组
时(如int array[100]),其实是定义了一个从int型到int型的映射比如array[0]=25、array[4]=36就分别是将0映射到25、将4映射到36。 而一个double型数组则是将int型映射到double型,如db[0]=3.14,double[1]=0.01;
在数据储存上,map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器),就比如map和vector , pair , map,set等镶嵌。
但是如果是字符串到整型的映射,必须是string而不是char数组
2.map的基本用法:
(1)map的定义:
map的尖括号中包含两个元素,第一个是key值,另一个是value值。通过两者之间关系可以后续进行查找与赋值。
(2)map的赋值:
假设定义一个map<int , string> p ;
赋值主要有两种方式:使用insert直接赋key与value值 通过key赋value值
a.使用insert赋值:
// (1).运 用 pair 函 数:
p.insert(pair<int,string>(1,"h1"));
// (2).再 次 运 用 map:
p.insert(map<int,string>::value_type(2,"h2")); /*注意加上::value_type*/
// (3).使用make_pair:
p.insert(make_pair(3,"h3"));
前两种运用了pair, map ,第三种就比较简洁。
b.通过key赋value值:
p[0]="h0";
这种方法比较常见。
(3)判断是否插入成功:
由于所学尚少,这个操作我很少用,先上码在解释。
pair<map<int,string>::iterator,bool> insert_pair;
insert_pair=person.insert(map<int,string>::value_type(3,"lewis3"));
cout<<"是否插入成功:"<<boolalpha<<insert_pair.second<<endl;
//boolalpha可以将bool类型的1和0转化为true与false
首先,用pair定义一个变量。(pair可以考虑为一个封装着两个变量的结构体)
然后, 让该变量等于插入数值操作。(正常插入操作)
最后,输出pair中的bool变量。(小知识点:boolalpha可以将bool型的0,1转化为false,true)
(4)erase操作:
擦除操作主要分为3种:
a.设置迭代器,将迭代器擦去;(命名迭代器方法为:map<类型1,类型2>::iterator)
迭代器定义可能比较陌生,记住格式即可。
b.
直接擦key值
;
c.
设置迭代器,设置范围
,一起擦掉;
具体实现参考以下示例:
// 擦 除 步 骤
#include <iostream>
#include <map>
using namespace std;
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
//插 入 元 素:
mymap['a']=10;
mymap['b']=20;
mymap['c']=30;
mymap['d']=40;
mymap['e']=50;
mymap['f']=60;
it=mymap.find('b');
mymap.erase (it); // 擦掉迭代器那一位
mymap.erase ('c'); // 通过key值擦去
it=mymap.find ('e');
mymap.erase ( it, mymap.end() ); // 擦掉连续几个
// show content:
for (it=mymap.begin(); it!=mymap.end(); ++it)
cout << it->first << " => " << it->second << '\n';
return 0;
}
结果输出:
a => 10
d => 40
(5)find操作:
简单说就是,如果key存在,map.find(key) ==> value,即找到key所对应的value值。
反之,
map.find(key)=map.end();
看以下代码演例:
map<char,int> my;
my['a']=50;
my['b']=100;
my['c']=150;
my['d']=200;
cout << "elements in mymap:" << '\n';
cout << "a => " << mymap.find('a')->second << '\n';
cout << "c => " << mymap.find('c')->second << '\n';
cout << "d => " << mymap.find('d')->second << '\n';
结果:
a => 50
c => 150
d => 200
(6)clear操作:
将map清空,size化为0;
eg:设map变量为p:
p.clear();
(7)size操作:
得到map中元素个数;
(8)读取单个value:
相当于输入的反过程,直接输出map[key]输出value;
eg:
map<int,string> person;
person[0]="lewis0";
cout<<person[0];
输出:lewis0
(9)遍历读取map中元素:
遍历读取时主要有两种操作:定义迭代器 使用auto
不用恐惧太麻烦,遍历主要记模板,使用的频率不如单个读取高,注意auto自动匹配数据类型,十分好用,也可以用在一般循环。
排序按照字典序输出。
输一个完整代码:
#include<iostream>
#include<map>
using namespace std;
int main()
{
map<int,string> person;
//一.赋 值 操 作:
// 1.直 接 赋 值:
person[0]="lewis0"; /*每个赋值都要注意带上标号*/
person[0]="lewis10";
//如 果 直 接 改 变 value,那 person[0] 的 值 就 会 随 之 改 变
// 2.使 用 insert 赋 值:
// (1).运 用 pair 函 数:
person.insert(pair<int,string>(1,"lewis1"));
// (2).再 次 运 用 map:
person.insert(map<int,string>::value_type(2,"lewis2")); /*注意加上::value_type*/
//二.读 取 操 作:
// 1.一 般 法 读 取:
map<int,string>::iterator it; /*注意加上::iterator*/
for(it=person.begin();it!=person.end();it++)
{
cout<<it->first<<":"<<it->second<<endl;
}
// 2.使 用 auto 自 动 定 义 类 型:
for(auto it=person.begin();it!=person.end();it++) /*auto有自动定义数据类型的功能,不必再定义it */
{
cout<<it->first<<":"<<it->second<<endl;
}
// 3.使 用 auto 直 接 遍 历:
for(auto it:person)
{
cout<<it.first<<":"<<it.second<<endl;
}
//三.判 断 是 否 数 据 插 入 成 功:
pair<map<int,string>::iterator,bool> insert_pair;
insert_pair=person.insert(map<int,string>::value_type(3,"lewis3"));
cout<<"是否插入成功:"<<boolalpha<<insert_pair.second<<endl; //boolalpha可以将bool类型的1和0转化为true与false
//四.获取map中的元素:
map<int,string>::iterator it1;
it1=person.find(1);
if(it1!=person.end())
{
cout<<it1->second<<endl;
}
else
{
cout<<"not find"<<endl;
}
//五.删除的方法:
// 1.通过设置it:
map<int,string>::iterator it2;
it2 =person.find(1);
person.erase(it2);
// 2.通过直接改变key值:
person.erase(2);
for(auto it:person)
{
cout<<"-------------------------"<<endl;
cout<<it.first<<":"<<it.second<<endl;
}
//六.对字符进行操作:
map<string,string>person2;
person2["0"]="10";
person2["1"]="11";
person2["2"]="12"; ///只需要将数字改为字符
map<string,string>::iterator it3;
it3=person2.find("1");
person2.erase(it3);
for(auto it:person2)
{
cout<<it.first<<":"<<it.second<<endl;
}
cout<<endl<<endl;
cout<<person2["0"];
}
(10)map与其他STL嵌套:
首先嵌套vector:
一般vector作为value值,对于一个key对应多个value值。相当于二维数组a[m][n],key即为m,vector即为n。
见代码:
map<int,vector<int> >Q;
for(int i=5;i<=10;i++){
Q[1000].push_back(i);
}//向1000对应的vector里插入5 6 7 8 9 10
Q[1000].erase(Q[1000].begin(),Q[1000].begin()+3);//删除1000对应的vector里前三个元素
for(int i=0;i<Q[1000].size();i++){
cout<<Q[1000][i]<<" ";
}
return 0;
}
输出:8 9 10
其次,嵌套map,pair,具体应用参考例题。
3.例题:
#include<cstdio>
#include<iostream>
#include<map>//引用STL里的map库
using namespace std;
map<int,map<int,int> > a;//建立二维映射
int main(){
int n,q,x,y,k,z;
cin>>n>>q;//输入寄包柜个数及询问次数
for(int i=1;i<=q;++i){//循环q次
cin>>k>>x>>y;
if(k==1){//存放操作
cin>>z;
a[x][y]=z;//建立一次映射
}
else{//查询操作
cout<<a[x][y]<<endl;//直接输出所映射的值
}
}
return 0;
}
这种方法是两个map固定2个key值,以确定物品位置。
#include <bits/stdc++.h>
using namespace std;
map <int, int> mat[10005]; //二维map
int main() {
int n, q;
scanf("%d%d", &n, &q);//输入
while (q--) {
int x, i, j, k;
scanf("%d", &x);
if (x == 1) {
scanf("%d%d%d", &i, &j, &k);
mat[i][j] = k; //直接映射
} else {
scanf("%d%d", &i, &j);
printf("%d\n", mat[i][j]); //直接查找对应元素,输出
}
}
return 0;
}
此方法是直接定义二维数组,定义map时比较出奇。
#include<cstdio>
#include<map>
using namespace std;
int n,q,p,k;
map<long long,int>b;
long long i,j;
int main()
{
scanf("%d%d",&n,&q);
while(q--)
{
scanf("%d%d%d",&p,&i,&j);
if(p==1)
{
scanf("%d",&k);
b[i*100000+j]=k;
}
else printf("%d\n",b[i*100000+j]);
}
return 0;
}
这种方法就很巧妙了,直接将二维数组化为一维数组,错开了二维数组两位重复的情况。
三.vector函数:
1.vector简介
:
他像是一个动态变化的数组,可以根据自己输入改变容器大小,以大大减少空间。尖括号中时数据类型。具体初始vector如下:
vector<int> a; //无参数 - 构造一个空的vector,
vector<int> a(10); //定义了10个整型元素的向量(尖括号中为元素类型名,它可以是任何合法的数据类型),但没有给出初值,其值是不确定的。
vector<int> a(10,1); //定义了10个整型元素的向量,且给出每个元素的初值为1
vector<int> a(b); //用b向量来创建a向量,整体复制性赋值, 拷贝构造
vector<int> a(b.begin(),b.begin+3); //定义了a值为b中第0个到第2个(共3个)元素
int b[7]={1,2,3,4,5,9,8};
vector<int> a(b,b+6); //从数组中获得初值,b[0]~b[5]
2.vector用法:
假设vector<int>a;
(1)开头,结尾表示
a.front();
//返回第一个元素
a.back();
//返回最后一个元素
(2)清空操作
将vector中元素清空,a.size()=0
a.clear();
//将vector中元素清空,a.size()=0
(3)检验是否为空
空返回true,假返回false
a.empty();
//判断是否空
(4) 删去最后一个元素
即删去最后加入的元素
a.pop_back();
//即删去最后加入的元素
(5)删除操作
与map的erase相似,删除区间元素
a.erase(a.begin()+1, a.begin()+3);
//删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)
删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起
(包括它)
一直到a.begin()+3
(不包括它)
(6)中间插入操作
使用insert操作:
a.加一个元素
a.insert(a.begin()+1,5);
//在第0位后1位上加入5
b.加一串元素
a.insert(a.begin()+1, 3,5);
//在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
(7)尾部插入操作
a.push_back(k);
//在a的最后一个向量后插入一个元素,其值为k
(8)元素个数
a.size();
//数字个数
(9)最大容量
a.capacity()
//总容量大小
(10)调节元素个数
a.不赋值改变个数,如果需要补位,则出随机值;
如果位数过多,删除多余项
a.resize(10);
//多删少补
b.将补位元素赋值:
a.resize(10,2)
//如果数位不足,补2
(11)调节容量大小
a.reserve(100)
//将容量调节至100
线性表,map,vector对于初学者来说比较抽象,需要熟练掌握基本操作,才能感受到他的便捷。
以上为本次主要内容,如有不当,欢迎评论区讨论!