这里,我将分别描述Vector和List的数据结构特点,以及一些函数的操作特点,然后再总结一下二者之间的区别
Vector的数据结构特点
Vector的数据安排以及操作方式,与array非常相似,二者之间唯一的差别在于空间的运用的灵活性,array是静态空间,一旦配置了就不能改变,要重新换一个大一点的区间,而Vector则在配置空间时预留一定的空闲空间,实现动态扩容。
并且这里使用size来表示已使用空间的大小;capacity表示总容量大小(end_of_storage-start),也就是说,这里再在vector中会留有空间来实现动态扩充;
Vector维护的是一个连续的线性空间,所以不论元素类型如何,Vector的迭代器都可以实现比如operator++,operator--,operator+=,operator+,operator- 等操作,从而Vector支持随机存取,因此提供的迭代器是Random Access Iterators
这里Vector用了三个迭代器指针:
iterator start; //表示目前使用空间的头
iterator finish; //表示目前使用空间的尾
iterator end_of_storage; //表示目前可用空间的尾
接下来就是一些函数比如erase,insert,push_back这些操作的特点:
我们知道这里在Vector中是有预留空间的,当capacity>size时,那么push_back操作就是常数时间,
{
finish=value;
++finish;
}
如果capacity==size,那么就需要重新申请一个连续的内存,大小为capacity=2*size,然后将原来的数据复制到这里来,释放原来的空间,很明显,这里就不再是常数的时间了,而是线性时间;
上面介绍的和insert操作要求是一样的,就不在赘述;
然后是erase操作,当删除一个区间的数据时,因为这个是连续存储,所以就需要将区间后面的数据复制到前面来,和删除区间的数据接上,变成连续的,你会发现,那么原来后面的位置数据还是在啊,没事,因为我改变了finish的指针地址,所以后面的数据就是无用的,push时刷新即可。
注:
你会发现,当原来尺寸不够时,重新换连续内存的时候,那么原来的迭代器就失效了,因此这里要注意,还有erase后,原来的迭代器所指的当前位置,就是那个区间的下一个了;
List的数据结构及特点
list设计中,是有两个部分,因此需要分开设计。
typedef ListNode* ListNodeP;
template<class T>
class ListNode
{
public:
ListNode();
~ListNode();
private:
T data;
ListNodeP Next;
ListNodeP Prev;
};
template<class T>
class List:public ListNode
{
public:
List();
~List();
};
一个是list节点的设置,因为list这里是双向链表,那么节点就必须得有next和prev,以及节点的运算符++等等;然后就是继承节点类的上层,即list类,其中定义了一些函数,用list作为数据结构,来实现一些算法,包括push,pop,erase,insert,sort等等;
List的迭代器:
List不再能够像Vector一样以普通指针作为迭代器,因为其节点不保证在存储空间中连续存在,这个很重要,也就是说节点的所分配的内存并不是连续的;
由于STL List是一个双向链表,从而迭代器必须具备前移和后移的能力;从而List提供的是一个Bidirectional Iterators;
由于List里面每个节点都是一块地址,从而删除一个节点,那么还在的节点,其迭代器并不会失效,就连erase也只是删除的那个节点的迭代器失效;
注:
这里SGI List不仅是一个双向链表,而且还是一个环状链表,在这里,它使用一个节点node,使得它链接了begin和end;
template<class T,class Alloc=alloc>
class List:public ListNode
{
public:
List();
~List();
private:
ListNodeP node; //只要一个指针,便可表示整个环状双向链表
};
iterator begin() {return node->next;}
iterator end() {return node;}
从这个迭代器可以看出,这里的node是多出来,用来链接最开始的和结尾的;
其他的没啥可说的,但是List的sort需要说一下,因为list并不是随机迭代器,而STL的sort(),只接受RamdonAccessIterator ;
因此这里采用了归并排序,说实话,刚开始,我看到代码是懵的,这tm写的是啥呀,但是看了别人博客中关于这个算法的动态图的展示,我才知道它想干啥,我只想说,牛逼!
这里,就不展示书上写的了,我把leetcode中写的链表的排序,贴出来;
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* split(ListNode* node,int step)
{
for(int i=1;node&&i<step;++i){ //只将当前节点与后面分离
node=node->next;
}
if(!node) return NULL;
ListNode *tail=node->next;
node->next=NULL;
return tail;//总时返回下一个节点,但是将上一段隔离
}
ListNode* merge(ListNode* left,ListNode* right,ListNode* head)
{
ListNode* cur=head;
while(left&&right)
{
if(left->val>right->val)
{
cur->next=right;
cur=cur->next;
right=right->next;
}
else
{
cur->next=left;
cur=cur->next;
left=left->next;
}
}
cur->next=((left)?left:right);
while(cur->next) cur=cur->next;
return cur;
}
ListNode* sortList(ListNode* head) {
if(!head||!head->next) return head;
int sz=0;
ListNode *dummy=new ListNode(0);
dummy->next = head; //制作一个上节点,将上节点的next作为首节点,传入
ListNode*cur=head;
while(cur)
{
cur=cur->next;
++sz;
}
ListNode* left;
ListNode* right;
ListNode* tail;
for(int step = 1; step < sz; step <<= 1){
cur = dummy->next;
tail = dummy;
while(cur){
left=cur;
right=split(left,step);
cur=split(right,step);
tail=merge(left,right,tail);
}
}
return dummy->next;
}
};
Vector和List在删除的时候是有区别的,因为vector是连续的空间,从而在插入或删除的时候,很可能原来的空间就失效了,因此迭代器就失效了,需要一块新的空间,而List在插入的时候或删除的时候,只有当前节点会失效,他们每个节点都是独立的,只不过用数据结构连接起来。