Từ tổng quan, bạn có thể nghĩ deque
như mộtdouble-ended queue
Các dữ liệu deque
được lưu trữ bởi các vectơ kích thước cố định, đó là
được trỏ bởi một map
(cũng là một đoạn của vectơ, nhưng kích thước của nó có thể thay đổi)
Mã phần chính của deque iterator
như sau:
/*
buff_size is the length of the chunk
*/
template <class T, size_t buff_size>
struct __deque_iterator{
typedef __deque_iterator<T, buff_size> iterator;
typedef T** map_pointer;
// pointer to the chunk
T* cur;
T* first; // the begin of the chunk
T* last; // the end of the chunk
//because the pointer may skip to other chunk
//so this pointer to the map
map_pointer node; // pointer to the map
}
Mã phần chính của deque
như sau:
/*
buff_size is the length of the chunk
*/
template<typename T, size_t buff_size = 0>
class deque{
public:
typedef T value_type;
typedef T& reference;
typedef T* pointer;
typedef __deque_iterator<T, buff_size> iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
typedef pointer* map_pointer;
// allocate memory for the chunk
typedef allocator<value_type> dataAllocator;
// allocate memory for map
typedef allocator<pointer> mapAllocator;
private:
//data members
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
}
Dưới đây tôi sẽ cung cấp cho bạn mã cốt lõi của deque
, chủ yếu là về ba phần:
vòng lặp
Cách xây dựng một deque
1. trình vòng lặp ( __deque_iterator
)
Vấn đề chính của iterator là, khi ++, - iterator, nó có thể bỏ qua phần khác (nếu nó trỏ đến cạnh của chunk). Ví dụ, có ba khối dữ liệu: chunk 1
, chunk 2
, chunk 3
.
Các pointer1
con trỏ đến điểm bắt đầu chunk 2
, khi toán tử --pointer
nó sẽ trỏ đến cuối chunk 1
, để đến pointer2
.
Dưới đây tôi sẽ cung cấp cho các chức năng chính của __deque_iterator
:
Đầu tiên, bỏ qua bất kỳ đoạn nào:
void set_node(map_pointer new_node){
node = new_node;
first = *new_node;
last = first + chunk_size();
}
Lưu ý rằng, chunk_size()
hàm tính kích thước khối, bạn có thể nghĩ nó trả về 8 để đơn giản hóa ở đây.
operator*
lấy dữ liệu trong khối
reference operator*()const{
return *cur;
}
operator++, --
// hình thức tiền tố của sự gia tăng
self& operator++(){
++cur;
if (cur == last){ //if it reach the end of the chunk
set_node(node + 1);//skip to the next chunk
cur = first;
}
return *this;
}
// postfix forms of increment
self operator++(int){
self tmp = *this;
++*this;//invoke prefix ++
return tmp;
}
self& operator--(){
if(cur == first){ // if it pointer to the begin of the chunk
set_node(node - 1);//skip to the prev chunk
cur = last;
}
--cur;
return *this;
}
self operator--(int){
self tmp = *this;
--*this;
return tmp;
}
iterator bỏ qua n bước / truy cập ngẫu nhiên
self& operator+=(difference_type n){ // n can be postive or negative
difference_type offset = n + (cur - first);
if(offset >=0 && offset < difference_type(buffer_size())){
// in the same chunk
cur += n;
}else{//not in the same chunk
difference_type node_offset;
if (offset > 0){
node_offset = offset / difference_type(chunk_size());
}else{
node_offset = -((-offset - 1) / difference_type(chunk_size())) - 1 ;
}
// skip to the new chunk
set_node(node + node_offset);
// set new cur
cur = first + (offset - node_offset * chunk_size());
}
return *this;
}
// skip n steps
self operator+(difference_type n)const{
self tmp = *this;
return tmp+= n; //reuse operator +=
}
self& operator-=(difference_type n){
return *this += -n; //reuse operator +=
}
self operator-(difference_type n)const{
self tmp = *this;
return tmp -= n; //reuse operator +=
}
// random access (iterator can skip n steps)
// invoke operator + ,operator *
reference operator[](difference_type n)const{
return *(*this + n);
}
2. Cách xây dựng một deque
chức năng chung của deque
iterator begin(){return start;}
iterator end(){return finish;}
reference front(){
//invoke __deque_iterator operator*
// return start's member *cur
return *start;
}
reference back(){
// cna't use *finish
iterator tmp = finish;
--tmp;
return *tmp; //return finish's *cur
}
reference operator[](size_type n){
//random access, use __deque_iterator operator[]
return start[n];
}
template<typename T, size_t buff_size>
deque<T, buff_size>::deque(size_t n, const value_type& value){
fill_initialize(n, value);
}
template<typename T, size_t buff_size>
void deque<T, buff_size>::fill_initialize(size_t n, const value_type& value){
// allocate memory for map and chunk
// initialize pointer
create_map_and_nodes(n);
// initialize value for the chunks
for (map_pointer cur = start.node; cur < finish.node; ++cur) {
initialized_fill_n(*cur, chunk_size(), value);
}
// the end chunk may have space node, which don't need have initialize value
initialized_fill_n(finish.first, finish.cur - finish.first, value);
}
template<typename T, size_t buff_size>
void deque<T, buff_size>::create_map_and_nodes(size_t num_elements){
// the needed map node = (elements nums / chunk length) + 1
size_type num_nodes = num_elements / chunk_size() + 1;
// map node num。min num is 8 ,max num is "needed size + 2"
map_size = std::max(8, num_nodes + 2);
// allocate map array
map = mapAllocator::allocate(map_size);
// tmp_start,tmp_finish poniters to the center range of map
map_pointer tmp_start = map + (map_size - num_nodes) / 2;
map_pointer tmp_finish = tmp_start + num_nodes - 1;
// allocate memory for the chunk pointered by map node
for (map_pointer cur = tmp_start; cur <= tmp_finish; ++cur) {
*cur = dataAllocator::allocate(chunk_size());
}
// set start and end iterator
start.set_node(tmp_start);
start.cur = start.first;
finish.set_node(tmp_finish);
finish.cur = finish.first + num_elements % chunk_size();
}
Giả sử i_deque
có 20 phần tử int 0~19
có kích thước khối là 8 và bây giờ đẩy 3 phần tử (0, 1, 2) thành i_deque
:
i_deque.push_back(0);
i_deque.push_back(1);
i_deque.push_back(2);
Đó là cấu trúc bên trong như dưới đây:
Sau đó đẩy lại, nó sẽ gọi phân bổ đoạn mới:
push_back(3)
Nếu chúng ta push_front
, nó sẽ phân bổ đoạn mới trước khi thắngstart
Lưu ý khi push_back
phần tử vào deque, nếu tất cả các bản đồ và khối được điền, nó sẽ gây ra phân bổ bản đồ mới và điều chỉnh các đoạn. Nhưng đoạn mã trên có thể đủ để bạn hiểu deque
.