【STL】list的簡單剖析以及各種函式的實現
STL中的list是比較常用的容器,對於任何位置的元素插入或元素移除,list永遠是常數。
list中的迭代器在插入和刪除後,仍然有效,但是耦合操作splice操作可能使迭代器失效,而vector就不成立了。
list節點
template <class T>
struct __list_node {
typedef void* void_pointer;
void_pointer prev; // 型別為void*。其實可設為 __list_node<T>*
void_pointer next;
T data;
};
顯然這是一個雙向連結串列,但其實是一個環狀的雙向連結串列
只需要一個指標,就可以完整的表現整個連結串列。
所以新增一個node節點,放置在尾端,符合前閉後開的區間,
這樣是迭代器begin和end比較好完成。
4個構造
(1)explicit list ( const Allocator& = Allocator() );
(2)explicit list ( size_type n, const T& value = T(), const Allocator& = Allocator() );
(3)template < class InputIterator >
list ( InputIterator first, InputIterator last, const Allocator& = Allocator() );
(4)list ( const list<T,Allocator>& x );
(1)是預設的建構函式,構造一個空的list物件,with no content and a size of zero。
空的list,前驅指向後繼,後繼指向前驅
(2)構造有n個T物件的list。
(3)迭代器的建構函式,區間[first,last),左閉右開
注迭代器建構函式的引數還可以用陣列,可測試用例
(4)拷貝建構函式,
測試程式碼
// constructors used in the same order as described above:
list<int> first; // empty list of ints
list<int> second (4,100); // four ints with value 100
list<int> third (second.begin(),second.end()); // iterating through second
list<int> fourth (third); // a copy of third
// the iterator constructor can also be used to construct from arrays:
int myints[] = {16,2,77,29};
list<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
cout << "The contents of fifth are: ";
for (list<int>::iterator it = fifth.begin(); it != fifth.end(); it++)
cout << *it << " ";
cout << endl;
迭代器操作函式:begin,end,rbegin,rend
begin和end
看上面list的節點的設計,知道有個頭結點node放置在list的尾部,起始位置前驅指向node,list的尾指向node,node的前驅指向尾,後繼指向起始節點。
所以begin和end函式中設計、前閉後開
iterator begin() {return (link_node*)((*node).next)}
iterator end() {return node;}
rbegin和rend
rbegin的位置相當於end的位置,
rend的位置相當於begin的位置。
Capacity操作函式
empty函式
看node節點的後繼是否指向自己
bool empty()const { return node->next == node;}
size函式
返回lis的元素的個數
size_t size()const
{
size_t result = 0;
distance(begin(),end(),result);//全域性函式,計算元素個數
return result;
}
max_size和resize函式
max_size返回list中最大能包含元素個數。
reszie函式改變list中size的大小。
如果引數sz大於max_size,那麼用c來構造;
如果引數sz小於max_size,那麼進行切割。
void resize ( size_type sz, T c = T() );
測試
list<int> mylist;
unsigned int i;
// set some initial content:
for (i=1;i<10;i++) mylist.push_back(i);
mylist.resize(5); //1 2 3 4 5
mylist.resize(8,100);// 1 2 3 4 5 100 100 100
mylist.resize(12); // 再加4個0
//mylist contains: 1 2 3 4 5 100 100 100 0 0 0 0
cout << "mylist contains:";
for (list<int>::iterator it=mylist.begin();it!=mylist.end();++it)
cout << " " << *it;
cout << endl;
Element access: front函式和back函式
利用那個node節點
reference front() {return *begin();}
reference back() {return *(--end());}
Modifiers:函式
先介紹insert函式和erase函式,這兩個函式比較重要。
insert函式
insert函式其實是在pos位置之前插入元素的,返回的是新插入元素的 位置(看函式的實現),但是pos位置仍然為之前節點的位置(看測試程式碼)
iterator insert ( iterator position, const T& x );
void insert ( iterator position, size_type n, const T& x );
template <class InputIterator>
void insert ( iterator position, InputIterator first, InputIterator last );
iterator insert ( iterator position, const T& x )
{
link_node* tmp = create_node(x);//建立新的節點。
//調整指標,使tmp插入進去
tmp->next = position.next;
tmp->prev = position.node->prev;
(link_node*(position.node->prev))->next = tmp;
position.node->prev = tmp;
return tmp;
}
測試
list<int> mylist;
list<int>::iterator it;
// set some initial values:
for (int i=1; i<=5; i++) mylist.push_back(i); // 1 2 3 4 5
it = mylist.begin();
++it; // it points now to number 2
mylist.insert (it,10); // 1 10 2 3 4 5
//注意看這
// "it" still points to number 2
mylist.insert (it,2,20); // 1 10 20 20 2 3 4 5
--it; // it points now to the second 20
vector<int> myvector (2,30);
mylist.insert (it,myvector.begin(),myvector.end());
// 1 10 20 30 30 20 2 3 4 5
erase函式
返回pos位置的下一個節點,pos變成next_node;即刪除後兩個表示的一樣節點
iterator erase ( iterator position );
iterator erase ( iterator first, iterator last );
//移除pos位置的節點
iterator erase ( iterator pos)
{
link_node *next_node = (link_node*)pos.node->next;
link_node *prev_node = (link_node*)pos.node->prev;
prev_node->next = next_node;
next_node_>prev = prev_node;
destroy_node(pos.node);
return iterator(next_node);
}
測試
int main ()
{
unsigned int i;
list<unsigned int> mylist;
list<unsigned int>::iterator it1,it2;
// set some values:
for (i=1; i<10; i++) mylist.push_back(i*10);
// 10 20 30 40 50 60 70 80 90
it1 = it2 = mylist.begin(); // ^^
advance (it2,6); // ^ ^
++it1; // ^ ^
it1 = mylist.erase (it1); // 10 30 40 50 60 70 80 90
// ^ ^
it2 = mylist.erase (it2); // 10 30 40 50 60 80 90
// ^ ^
++it1; // ^ ^
--it2; // ^ ^
mylist.erase (it1,it2); // 10 30 60 80 90
// ^
cout << "mylist contains:";
for (it1=mylist.begin(); it1!=mylist.end(); ++it1)
cout << " " << *it1;
cout << endl;
//mylist contains: 10 30 60 80 90
return 0;
}
push_back、push_front函式
呼叫insert函式
void push_front ( const T& x ){ insert(begin(),x);}
void push_back(const T& x){ insert(end(),x);}
pop_back、pop_front函式
呼叫erase函式
void pop_front () { erase(begin());}
void pop_back()
{
iterator tmp = end();
erase(--tmp);
}
assign函式
重新寫list,Assign new content to container,就像建構函式一樣。
template <class InputIterator>
void assign ( InputIterator first, InputIterator last );
void assign ( size_type n, const T& u );
list<int> first;
list<int> second;
first.assign (7,100); // 7 ints with value 100
second.assign (first.begin(),first.end()); // a copy of first
int myints[]={1776,7,4};
first.assign (myints,myints+3); // assigning from array
swap函式
Swap content,交換兩個list
void swap ( list<T,Allocator>& lst );
list<int> first (3,100); // three ints with a value of 100
list<int> second (5,200); // five ints with a value of 200
list<int>::iterator it;
first.swap(second);
//first contains: 200 200 200 200 200
//second contains: 100 100 100
clear函式
清除整個連結串列
void clear()
{
link_node *cur =(link_node*)node->next;//起始位置
while(cur != node)
{
link_node *tmp = cur;
cur = cur->next;
destroy_node(tmp);
}
//恢復node原始狀態,
node->next = node;
node->prev = node;
}
Operations函式
void reverse ( );
//Reverse the order of elements
remove
Remove elements with specific value
//刪除所有的value值
void remove ( const T& value )
{
iterator first = begin();
iterator last = end();
while(first != last)
{
iterator next = first;
++next;
if(*first == value)
erase(first);
first = next;
}
}
unique函式
//移除數值相同的連續元素,只有相同連續的元素,才會被移除剩一個
void unique()
{
iterator first = begin();
iterator last = end();
if(first == last)
return ;//空連結串列,返回
iterator next = first;
while(++next != last)
{
if(*first == *next)
erase(next);
else
first = next;//調整指標,向後移動
next = first;//修正區段,如果刪除了,next指向下一個區域
}
}
splice,sort,merge、reverse
這四個函式都需要呼叫,list內部提供的一個所謂的遷移操作(transfer):將某連續範圍的元素遷移到某個特定位置之前,就是一個指標的移動。
transfer函式
可以是同一個list,也可以是兩條list
//將[first,last)內的所有元素都移到pos之前
//注:區間是左閉右開的,將first到last前一個插入pos之前
void transfer(iterator pos,iterator first,iterator last)
{
//last等於pos,就不用移動
if(pos != last)
{
//1-4從左向右斷開
//(1)將last的前一個節點後繼指向pos位置
(*((link_node*)(*last.node).prev)).next = pos.node;
//(2)將firt到last摘出去
(*((link_node*)(*fisrt.node).prev)).next = last.node;
//(3)將摘出去的連線到pos之前
(*((link_node*)(*pos.node).prev)).next = frst.nod;
//從右向左連結
//(4)標記pos之前的節點,因為(5)斷開後找不到這個位置
link_node* tmp = link_node*((*pos.node).prev);
//(5)pos前驅指向last之前的節點
(*pos.node).prev = (*last.node).prev;
//(6)完全摘出去,last前驅指向fist之前的節點
(*last.node).prev = (*first.node).prev;
//(7)將frst節點連線到pos之前的tmp節點
(*first.node).prev = tmp;
}
}
reverse
將list中的原序列元素反序,呼叫transfer,
void reverse()
{
//連結串列為空或者只有一個元素,什麼不做
//這樣判斷,速度較快
if(node->next == node || (node->next)->next == node)
return;
iterator first = begin();
++first;//此時first指向第二個元素
//
while(first != end())
{
iterator old = first;
++first;
//將後面的元素全部插入第一個元素之前。
transfer(begin(),old,first);
}
}
splice函式
list提供的公共介面來將某些連續方位的元素從一個list移動到另一個list,封裝了transfer。
另一個list中的元素已經被移走,
void splice ( iterator position, list<T,Allocator>& x );//移動整個x
void splice ( iterator position, list<T,Allocator>& x, iterator i );//只移動一個元素
void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last );
測試
list<int> mylist1, mylist2;
list<int>::iterator it;
// set some initial values:
for (int i=1; i<=4; i++)
mylist1.push_back(i); // mylist1: 1 2 3 4
for (int i=1; i<=3; i++)
mylist2.push_back(i*10); // mylist2: 10 20 30
it = mylist1.begin();
++it; // points to 2
mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" still points to 2 (the 5th element)
mylist2.splice (mylist2.begin(),mylist1, it);
// mylist1: 1 10 20 30 3 4
// mylist2: 2
// "it" is now invalid.
it = mylist1.begin();
advance(it,3); // "it" points now to 30
mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());
// mylist1: 30 3 4 1 10 20
merge函式
注:必須兩個list必須都已經遞增排序,合併完後,引數x中沒有元素
void merge ( list<T,Allocator>& x )
template <class Compare>
void merge ( list<T,Allocator>& x, Compare comp );
void merge ( list<T,Allocator>& x )
{
iterator first1 = begin();
iterator last1 = end();
iteartor first2 = x.begin();
iterator last2 = x.end();
//注:兩個list必須遞增排序
while(first1 != last1 && first2 != last2)
{
if(*first2 < *first1)
{
iterator next = first2;
transfer(first1,fisrt2,++next);
first2 = next;
}
else
++first1;
}
if(first2 != last2)
transfer(last1,first2,first2);
}
sort函式
list不能使用STL中的演算法sort,必須使用自己的sort函式,
因為STL中的sort函式只接收RamdonAccessIterator
void sort ( );
template <class Compare>
void sort ( Compare comp );
void sort()
{
//連結串列為空或者只有一個元素,什麼不做
//這樣判斷,速度較快
if(node->next == node || (node->next)->next == node)
return;
//一些新的lists,作為中結資料儲存區
list<T> carry;
list<T> counter[64];
int fill = 0;
//待排序的list不為空
while(!empty())
{
//將待排序的list的首元素移動到carry中
carry.splice(carry.begin(),*this,begin());
//將carry中的元素放在桶counter[fill]中
int i = 0;
while(i < fill && !counter[i].empty())
{
//合併到桶中,遞增有序
counter[i].merge(carry);
carry.swap(counter[i++]);
}
//i ==fill跳出迴圈,說明當前桶放滿,放在下個桶中
//或者當前桶沒放滿,放在當前桶中
carry.swap(counter[i]);
//當前桶放滿,放下個桶
if(i == fill)
++fill;
}
for(int i = 1; i < fill; ++i)
counter[i].merge(counter[i-1]);
swap(counter[fill-1]);
}
這段程式碼長度不長,但是比較難理解。
fill–當前可以處理的元素個數為2^fill個
counter[fill]–可以容納2^(fill+1)個元素
__counter[0]裡存放2(0+1)次方個元素 1
__counter[1]裡存放2(1+1)次方個元素 4
__counter[2]裡存放2(2+1)次方個元素 8
carry–一個臨時中轉站,在處理的元素個數不足2^fill個時,在counteri之前轉移元素
具體是顯示步驟是:
1、每次讀一個數據到carry中,並將carry的資料轉移到counter[0]中
1)當counter[0]中的資料個數少於2時,持續轉移資料到counter[0]中
2)當counter[0]的資料個數等於2時,將counter[0]中的資料轉移到counter[1]…從counter[i]轉移到counter[i+1],直到counter[fill]中資料個數達到2^(fill+1)個。
2、 ++fill,重複步驟1.
注:歸併排序,先將前兩個歸併,然後再將後兩個歸併,再歸併程4個元素;然後再兩個兩個歸併,歸併成4個,兩個4個歸併成8個;歸併成16個。32個。。。。。
測試
list<double> first, second;
first.push_back (3.1);
first.push_back (2.2);
first.push_back (2.9);
second.push_back (3.7);
second.push_back (7.1);
second.push_back (1.4);
first.sort();
second.sort();
first.merge(second);
second.push_back (2.1);
first.merge(second,mycomparison);