vector

vector

在这里vector<unkonw>,unknow我们使用int作为一个举例说明,当然这个unknow可以是其它任何类型,unkonw可以是一个string,一个vector<int>,甚至可以是vector<vector<set<<int>>>这种多层嵌套类型.

比如:
容器多层嵌套使用示例 其使用方式极似于c语言中指针与数组名的多层嵌套使用.使用一个容器应该最先熟悉其增删查改操作,我们还是以增删查改来分类vector<>的常用方法吧.

构造

construct

vector<int> v0;//直接构造一个空vector<int>
 vector<int> v1(7,8);//构造一个vector<int>填充7个8,注意这个8是可以缺省的,它会调用unknow(这里是int)的默认构造函数来填值
 vector<int> v2(v1);//使用已存在的unknow完全相同的vector<unknow>来构造
 vector<int> v3(v2.begin(),v2.end());//使用unknow相同的一段迭代器区间来构造

关于使用迭代器区间构造,其实这里有一个小注意点,这个迭代器区间不是必须是同类容器的迭代器区间,由于其迭代器区间构造实现方法的原因,只要满足元素type完全一样,且满足重载了操作符++,以及*(“解引用”)的容器的迭代器都可以作为vector的构造传参.如下示例:
迭代器构造示例所以,我们是完全可以用set<>或者数组来构造一个vector<>的.

插入元素(增)

push_back()

insert()

//push_back()
  v1.push_back(3);//尾插一个3

  //insert()
  v1.insert(v1.begin()+2,4);//在迭代器位置,插入一个4
  v1.insert(v1.begin(),7,9);//在迭代器位置插入7个9
  v1.insert(v1.end(),v0.begin(),v0.end());//在迭代器位置插入一个迭代器区间的元素.
  

对于插入,我们可以进行更多的尝试,比如用vector<>的后半段元素,插入到vector<>的前半段去?
迭代器的失效问题 我们可以看到我们本来是想把8,9,10三个数插入到begin()的位置去,可是结果似乎并不正确,我们可以通过这个实验现象,来去反推vector的插入逻辑,在什么样的底层插入逻辑下,才能出现这样的情况呢?
答案是,它会先计算迭代器区间元素所需要的空间,然后再在容器的前端一次挪出所需的空间,然后再将迭代器区间中所指向的元素复制到前端空间中,问题就出在这里,迭代器它可并非是和元素绑定,而是和空间绑定的,当vector前端清理出空间时,我们要插入的8,9,10其实早就流到后面去了,你的迭代器区间的内存里放的已经是5,6,7了.此时,再对这个区间解引用插入才会出现我们上图的状况.
这也算是一种另类的迭代器的失效,所以我们用容器后半段去插入到前半段显然是不行的,但是完全可以用容器的前半段迭代器区间去插入到后半段,因为后半段的元素挪移完全不会影响前半段空间与元素的存储关系.如下图:
迭代器的失效问题
此外,能引发大面积迭代器失效的情况是,在你已经定义了不少迭代器的情况下,此时插入元素,如果恰好引发了扩容,我们都知道,顺序表扩容时,原有的整块空间都将被放弃,所有元素会被迁移到一块新的更大的空间安置,所以你原有的迭代器此时将全部指向错误的,不被允许的内存空间.

删除元素(删)

 //pop_back()
  v0.pop_back();//弹出末位元素

  //erase()
  v0.erase(v0.end()-2);//删除一个迭代器位置的元素
  v0.erase(v0.begin()+3,v0.end());//删除一个迭代器区间的元素

从这里我们就可以很明显的感觉到,对于容器元素的操作方法vector<>相比较string少了相当多,这是因为,string的诞生实际早于STL,为了向前兼容,才造成string不仅有原生带来的众多操作方法,还有STL为了与其他容器相统一加入的迭代器操作方法,使得string的操作方法多到眼花缭乱,但是,用迭代器一定没错,你所想要达到的所有目的,迭代器都可以做,当然或许一些非迭代器方法更加便捷,只是记忆如此多的string方法稍有麻烦.
而且STL也是希望你使用迭代器的,因为这是容器与algorithm以及其他部件联动使用的唯一桥梁.
此外我们注意到push_back(),与pop_back()这两者,按照惯性思维,你pusn_back()和pop_back()都有了,来个push_front()和pop_front()不过分吧,但是实际上,vector还真没有,这并非是push_front()和pop_front()难以实现,而是STL单纯的不想给,因为不管是push_front()还是pop_front(),你在头上不管是插入还是删除一个元素,整个vector的元素都得来次大搬家,如此沉重的代价显然是STL难以容忍的,所以干脆不提供.
如果你一定要在头上插入或者删除只能使用insert(),或者erase().
而且对于底层是顺序表似的结构,你操纵的元素位置越是靠前,时间资源损耗就越大.

查找元素(查)

algorithm::find()

vector也没有提供任何查找元素的方法,你要查找元素非得借用algorithm的find()不可

vector<int>::iterator it=find(v0.begin(),v0.end()-1,3);//利用algorithm的find()查找元素3

重构元素(改)

assign()

v0.assign(v1.begin(),v1.end());//使用一个迭代器区间的元素对v0进行整体重构
 v0.assign(7,8);//使用7个8来重构整个容器的元素

关于vector的排序问题

我们知道vector本身并不是一个有序的容器,有序的容器比如set和map,因为他俩底层本身就是红黑树,所以他俩天然的就带有自动排序的能力,中序遍历出来,就是一个有序队列,故此set和map的class不提供sort也很正常,但是algorithm的sort,map和set也是无法使用的,这主要是因为algorithm的sort()本身使用的是快排的算法,所以这个sort()不支持非随机迭代器(看看这个容器的iterator的class里有没有重载+,有就是随机迭代器,没有就不是),其实底层是连续空间内存的容器,迭代器一般都是随机的.红黑树作为一个链表,map和set当然不想去重载+操作符,因为这仍然是一个效率问题,如果你想要实现链表的随机迭代器,就比如下面这段伪代码

//现在我们想要it+7的迭代器
set::iterator it;
set::iterator  cur=set.begin();
while(cur!=it)
{
 cur=cur->next;
 if(cur==set.end())
 break;
}
if(cur==it)
{
int count=7;
set::iterator to_new=it;
for(count;count>0;count--)
{
 to_new=to_new->next;
}
return to_new;
}
else
return set.end();

可以看到,我们不能根据内存地址去直接把地址值进行加减就完事了,对于链表来说,你节点之间的内存物理距离是完全不等长的,你必须从begin()开始一个节点一个节点找下去,这种动不动就是O(N)的操作方式,也就没有实现.
而对于sort()的使用,前两个参数是一个迭代器区间没什么好说的,只是要牢记,凡是迭代器区间,必定是左闭右开.而对于第三个参数,则是自定义排序规则,你的规则应当是一个bool函数或者重载了()
的对象,重载的()返回值也必须是bool,因为sort会根据返回true或false来决定vector或者其他容器中任意两元素的先后位置.
使用范例如下:

class A
{
   public:
   bool operator()(int x,int y)
   {
     return y>x;
   }
};
bool com(int x,int y)
{
   return x>y;
}

int main()
{
  vector<int> v0{1,12,63,24,55,16,77,28,39};

  sort(v0.begin(),v0.end(),com);//传函数指针方式(函数名就是函数指针)由小到大排序
  out_vector(v0);
 

  A A_com;//先实例化一个对象
  sort(v0.begin(),v0.end(),A_com);//传对象方式,由大到小排序
  out_vector(v0);
  return 0;
}

现象如图:
sort使用方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值