STL學習筆記--4、序列式容器之list
阿新 • • 發佈:2019-02-04
1、概述
list:地址不連續的空間。每次插入或刪除一個元素,就配置或釋放一個元素空間。對於任意位置的元素插入或刪除,list永遠是常數時間。
2、list節點
list本身和list節點是不同的;
template <class T>
struct __list_node {
typedef void* void_pointer;
//void_pointer實際為__list_node<T>*
void_pointer next;
void_pointer prev;
T data;
};
雙向連結串列。
3、list迭代器
list迭代器必須有能力指向list的節點;
遞增:指向下一個節點
遞減:指向上一個節點
取值:取得是節點的資料值
成員取用:取得是節點的成員
STL的list是雙向連結串列;迭代器提供的為Bidirectional Iterators。
插入insert和接合splice操作不會使原有list的迭代器失效。
刪除只會使指向刪除元素的迭代器失效。
template<class T, class Ref, class Ptr>
struct __list_iterator {
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, Ref, Ptr> self;
//迭代器提供的是BidirectionaIterator
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
//link_type為node的指標
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
link_type node;
//建構函式
__list_iterator(link_type x) : node(x) {}
__list_iterator() {}
__list_iterator(const iterator& x) : node(x.node) {}
bool operator==(const self& x) const { return node == x.node; }
bool operator!=(const self& x) const { return node != x.node; }
//取值取得是節點的資料值
reference operator*() const { return (*node).data; }
//成員取用取得是節點的成員
pointer operator->() const { return &(operator*()); }
//前置++,指向下一個節點
self& operator++() {
node = (link_type)((*node).next);
return *this;
}
//後置++
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}
self& operator--() {
node = (link_type)((*node).prev);
return *this;
}
self operator--(int) {
self tmp = *this;
--*this;
return tmp;
}
};
4、list的資料結構
SGI的list不僅是雙向連結串列,更是一個環形雙向連結串列。
讓指標node指向尾端的空白節點,則符合前閉後開的區間要求。
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node;
protected:
link_type node;//指向尾段空白節點
//...
public:
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }
bool empty() const { return node->next == node; }
size_type size() const {
size_type result = 0;
//distance迭代器的成員函式
distance(begin(), end(), result);
return result;
}
reference front() { return *begin(); }
reference back() { return *(--end()); }
};
5、list的構造和記憶體管理
1)、應用:
#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
list<int> ilist;
cout << "size=" << ilist.size() << endl; // size=0
ilist.push_back(0);
ilist.push_back(1);
ilist.push_back(2);
ilist.push_back(3);
ilist.push_back(4);
cout << "size=" << ilist.size() << endl; // size=5
list<int>::iterator ite;//宣告迭代器
for(ite = ilist.begin(); ite != ilist.end(); ++ite)
cout << *ite << ' '; // 0 1 2 3 4
cout << endl;
//插入和接合操作不會造成原迭代器失效
ite = find(ilist.begin(), ilist.end(), 3);
if (ite != ilist.end())
ilist.insert(ite, 99);//在以前3的位置處插入一個數99,
//插入完成後,新節點位於ite所指節點前方
cout << "size=" << ilist.size() << endl; // size=6
cout << *ite << endl; // 3 !!!!
for(ite = ilist.begin(); ite != ilist.end(); ++ite)
cout << *ite << ' '; // 0 1 2 99 3 4
cout << endl;
//刪除操作時,指向被刪除元素的那個迭代器失效,其他不受影響
ite = find(ilist.begin(), ilist.end(), 1);
if (ite != ilist.end())
cout << *(ilist.erase(ite)) << endl;// 2 !!!
cout<<*ite<<endl;// 1 !!!
cout<<*(ite++)<<endl;// 1 !!!
for(ite = ilist.begin(); ite != ilist.end(); ++ite)
cout << *ite << ' '; // 0 2 99 3 4
cout << endl;
return 0;
}
2)、實現:
配置了list_node_allocator,方便以節點大小為單位來分配記憶體。
/*
//get_node()
//put_node()
//create_node()
//destroy_node()
//empty_initialize()
*/
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node;
typedef simple_alloc<list_node, Alloc> list_node_allocator;
protected:
//配置一個節點
link_type get_node() { return list_node_allocator::allocate(); }
//釋放一個節點
void put_node(link_type p) { list_node_allocator::deallocate(p); }
//產生一個節點,配置並構造,帶有元素值x
link_type create_node(const T& x) {
link_type p = get_node();
//空間配置的元素建構函式
construct(&p->data, x);
return p;
}
//銷燬一個節點,析構並釋放。
void destroy_node(link_type p) {
//空間配置的元素解構函式
destroy(&p->data);
put_node(p);
}
public:
//產生一個空連結串列
list(){empty_initialize();}
protected:
//只有一個node節點,且node的頭尾都指向自己,元素值為空。
void empty_initialize() {
node = get_node();
node->next = node;
node->prev = node;
}
};
push_back(x)呼叫insert來實現。
void push_back(const T& x){insert(end(),x);}
insert()函式完成之後,新節點將位於哨兵迭代器(標識插入點)所直接點的前方。
//在position所指向的位置處插入一個節點,內容為x
iterator insert(iterator position, const T& x) {
//為插入內容構造節點
link_type tmp = create_node(x);
//修改迭代器的指向
//4步策略
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
return tmp;
}
6、元素操作
1)、實現:
/*
//push_front
//push_back
//erase
//pop_front
//pop_back
//clear
//remove(const T& value)將數值為value的所有元素移除
//unique()移除所有數值相同並且連續的元素。只有連續且相同的元素才會被移除的只剩一個
*/
void push_front(const T& x) { insert(begin(), x); }
void push_back(const T& x) { insert(end(), x); }
//刪除position迭代器指向的位置處的節點
//返回為next_node
iterator erase(iterator position) {
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
//調整迭代器的指向
prev_node->next = next_node;
next_node->prev = prev_node;
//清除節點
destroy_node(position.node);
return iterator(next_node);
}
void pop_front() { erase(begin()); }
//移除尾節點時,end()指向的是尾後節點
void pop_back() {
iterator tmp = end();
erase(--tmp);
}
//清除所有節點
template <class T, class Alloc>
void list<T, Alloc>::clear()
{
link_type cur = (link_type) node->next;
//從頭節點開始刪除
while (cur != node) {
link_type tmp = cur;
cur = (link_type) cur->next;
destroy_node(tmp);
}
//最後一個節點頭尾指向自身
node->next = node;
node->prev = node;
}
//將數值為value的所有元素移除
template <class T, class Alloc>
void list<T, Alloc>::remove(const T& value) {
iterator first = begin();
iterator last = end();
//遍歷節點
while (first != last) {
iterator next = first;
++next;
if (*first == value) erase(first);
first = next;
}
}
//移除所有數值相同並且連續的元素。只有連續且相同的元素才會被移除的只剩一個
template <class T, class Alloc>
void list<T, Alloc>::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;
}
}
遷移操作transfer:將某個連續範圍的元素遷移到某個特定的位置之前。
//transfer
//將[first,last)的所有元素放到position之前。
void transfer(iterator position, iterator first, iterator last) {
if (position != last) {
(*(link_type((*last.node).prev))).next = position.node;//1
(*(link_type((*first.node).prev))).next = last.node;//2
(*(link_type((*position.node).prev))).next = first.node;//3
link_type tmp = link_type((*position.node).prev);//4
(*position.node).prev = (*last.node).prev;//5
(*last.node).prev = (*first.node).prev; //6
(*first.node).prev = tmp;//7
}
}
transfer非公開介面。
list中提供了一些公開的介面,底層使用transfer來實現的。
- splice:將某個連續範圍的元素從一個list移動到另一個(或同一個)list的某個定點。
- merge:將一個list合併到另一個list。兩個list都必須先經過遞增排序。
- reverse:將list的內容逆置。
- sort:排序。
2)、應用:
int iv[5] = { 5,6,7,8,9 };
list<int> ilist2(iv, iv+5);
// 目前,ilist 的內容為0 2 99 3 4
ite = find(ilist.begin(), ilist.end(), 99);//在99前面插入
ilist.splice(ite,ilist2); // 0 2 5 6 7 8 9 99 3 4
ilist.reverse(); // 4 3 99 9 8 7 6 5 2 0
ilist.sort(); // 0 2 3 4 5 6 7 8 9 99
總結:
- (1)push_front(const T& value), push_back(const T& value)
- (2)erase(iterator position):移除迭代器position所指節點
- (3)pop_front(), pop_back()
- (4)clear()://清空list節點
- (5)remove(const T& value):將數值為value的所有元素移除
- (6)unique():移除數值相同的連續元素,只剩下一個。注意相同而連續。
- (7)splice:接合操作。將某連續範圍的元素從一個list移動到另一個(或同一個)list的某個定點。
- (8)merge(list &x):將x合併到*this身上。兩個lists內容必須先遞增排序。
- (9)reverse():將list的內容逆置。
- (10)sort():list不能使用STL演算法sort(),必須使用自己的sort()成員函式,因為STL演算法sort()只接受RamdonAccessIterator。list中sort()函式採用quicksort。