1. 程式人生 > >STL原始碼分析之slist有序容器 下

STL原始碼分析之slist有序容器 下

前言

上節我們對slist的基本構成, 構造析構做了分析, 本節 我們就來分析關於slist的基本元素操作.

slist分析

基本屬性資訊

slist是隻有正向迭代, 所以只能直接獲取頭部的資料.

template <class T, class Alloc = alloc>
class slist
{
	...
private:
  slist& operator= (const slist& L);
public:
	// 獲取頭部元素的地址
  iterator begin() { return iterator((list_node*
)head.next); } const_iterator begin() const { return const_iterator((list_node*)head.next);} // 尾部的地址就是設定的0 iterator end() { return iterator(0); } const_iterator end() const { return const_iterator(0); } // slist的長度是通過連結串列一個個訪問計算出來的, 時間複雜度為O(n) size_type size() const { return __slist_size(head.
next); } size_type max_size() const { return size_type(-1); } // 最大容納的節點數 bool empty() const { return head.next == 0; } // 頭部是否為空來判斷連結串列為空 // 獲取前一個節點, slist只能正向迭代, 所以就只能從頭到尾的進行查詢 // 這裡呼叫的是__slist_previous函式實現, 這個在上一節已經分析過了 iterator previous(const_iterator pos) { return iterator((list_node*
) __slist_previous(&head, pos.node)); } const_iterator previous(const_iterator pos) const { return const_iterator((list_node*) __slist_previous(&head, pos.node)); } ... };

swap. slist進行交換並不是交換所以的元素, 實際只是交換了head的指向

template <class T, class Alloc = alloc>
class slist
{
	...
public:
  void swap(slist& L)
  {
    list_node_base* tmp = head.next;
    head.next = L.head.next;
    L.head.next = tmp;
  }
  ...
};
template <class T, class Alloc>
inline void swap(slist<T, Alloc>& x, slist<T, Alloc>& y) {
  x.swap(y);
}

pop和push

slist的push和pop都只能在頭部進行操作, 是頭插法.

template <class T, class Alloc = alloc>
class slist
{
	...
public:
    // 獲取頭部元素的資料
  reference front() { return ((list_node*) head.next)->data; }
  const_reference front() const { return ((list_node*) head.next)->data; }
    // 在頭部進行插入操作
  void push_front(const value_type& x)   {
    __slist_make_link(&head, create_node(x));
  }
    // 刪除第一個元素
  void pop_front() {
    list_node* node = (list_node*) head.next;
    head.next = node->next;
    destroy_node(node);	// 析構並釋放掉
  }
    ...
};

運算子過載

過載=, 私有

template <class T, class Alloc = alloc>
class slist
{
	...
private:
    // = 運算子定義是私有的
  slist& operator= (const slist& L);
    ...
};

// 這定義的是私有, 無法直接呼叫=
template <class T, class Alloc>
slist<T, Alloc>& slist<T,Alloc>::operator=(const slist<T, Alloc>& L)
{
    // 不是同一連結串列
  if (&L != this) {
    list_node_base* p1 = &head;
    list_node* n1 = (list_node*) head.next;
    const list_node* n2 = (const list_node*) L.head.next;
    while (n1 && n2) {
        // 深拷貝, 直到一個連結串列結束
      n1->data = n2->data;
      p1 = n1;
      n1 = (list_node*) n1->next;
      n2 = (const list_node*) n2->next;
    }
      // 原連結串列還有剩,  就刪除原連結串列多餘的資料
    if (n2 == 0)
      erase_after(p1, 0);	
      // 否則就將n2的剩餘元素初始化
    else
      _insert_after_range(p1, const_iterator((list_node*)n2), const_iterator(0));
  }
  return *this;
} 

過載==, 共有

template <class T, class Alloc = alloc>
class slist
{
	...
public:
    // 友元
  friend bool operator== __STL_NULL_TMPL_ARGS(const slist<T, Alloc>& L1,
                                              const slist<T, Alloc>& L2);
    ...
};
template <class T, class Alloc>
bool operator==(const slist<T, Alloc>& L1, const slist<T, Alloc>& L2)
{
  typedef typename slist<T,Alloc>::list_node list_node;
  list_node* n1 = (list_node*) L1.head.next;
  list_node* n2 = (list_node*) L2.head.next;
    // 一個個元素進行比較
  while (n1 && n2 && n1->data == n2->data) {
    n1 = (list_node*) n1->next;
    n2 = (list_node*) n2->next;
  }
  return n1 == 0 && n2 == 0;
}

過載<, 共有

template <class T, class Alloc>
inline bool operator<(const slist<T, Alloc>& L1, const slist<T, Alloc>& L2)
{	// 直接進行比較
  return lexicographical_compare(L1.begin(), L1.end(), L2.begin(), L2.end());
}

插入操作

插入操作的核心是在上節分析的__slist_make_link函式, 將元素插入指定位置後.

insert_after : 將元素插入指定位置後

template <class T, class Alloc = alloc>
class slist
{
	...
private: // 私有, 由內部函式呼叫
  list_node* _insert_after(list_node_base* pos, const value_type& x) {
    return (list_node*) (__slist_make_link(pos, create_node(x)));
  }
public:
  iterator insert_after(iterator pos, const value_type& x) {
    return iterator(_insert_after(pos.node, x));	// 呼叫 _insert_after
  }

  iterator insert_after(iterator pos) {
    return insert_after(pos, value_type());	// 呼叫 _insert_after
  }

  void insert_after(iterator pos, size_type n, const value_type& x) {
    _insert_after_fill(pos.node, n, x);	// 呼叫 _insert_after_fill
  }
  template <class InIter>
  void insert_after(iterator pos, InIter first, InIter last) {
    _insert_after_range(pos.node, first, last);	// 呼叫 _insert_after_fill
  }
    ...
};

insert : 將元素插入指定位置之前

template <class T, class Alloc = alloc>
class slist
{
	...
public:
    // 元素插入在指定位置之前
    // 先呼叫__slist_previous獲取指定位置之前的位置, 在執行_insert_after
  iterator insert(iterator pos, const value_type& x) {
    return iterator(_insert_after(__slist_previous(&head, pos.node), x));
  }
  iterator insert(iterator pos) {
    return iterator(_insert_after(__slist_previous(&head, pos.node),
                                  value_type()));
  }
    // 無返回值, 且元素插入在指定位置之前
  void insert(iterator pos, size_type n, const value_type& x) {
    _insert_after_fill(__slist_previous(&head, pos.node), n, x);
  } 
  template <class InIter>
  void insert(iterator pos, InIter first, InIter last) {
    _insert_after_range(__slist_previous(&head, pos.node), first, last);
  }
    ...
};

刪除操作

arase_after刪除指定位置的元素 .

template <class T, class Alloc = alloc>
class slist
{
	...
private: // 私有, 由內部函式呼叫
    // 刪除一個元素
  list_node_base* erase_after(list_node_base* pos) {
    list_node* next = (list_node*) (pos->next);
    list_node_base* next_next = next->next;
    pos->next = next_next;
    destroy_node(next);
    return next_next;
  }
   
  list_node_base* erase_after(list_node_base* before_first,
                              list_node_base* last_node) {
    list_node* cur = (list_node*) (before_first->next);
      // 將[first, last) 範圍的元素進行刪除
    while (cur != last_node) {
      list_node* tmp = cur;
      cur = (list_node*) cur->next;
      destroy_node(tmp);
    }
    before_first->next = last_node;
    return last_node;
  }
    
public:
    // 呼叫erase_after函式
  iterator erase_after(iterator pos) {
    return iterator((list_node*)erase_after(pos.node));
  }
  iterator erase_after(iterator before_first, iterator last) {
    return iterator((list_node*)erase_after(before_first.node, last.node));
  }
    ...
};

erase刪除指定位置之前的元素

template <class T, class Alloc = alloc>
class slist
{
	...
public:
    // 刪除指定位置之前的元素
  iterator erase(iterator pos) {
    return (list_node*) erase_after(__slist_previous(&head, pos.node));
  }
  iterator erase(iterator first, iterator last) {
    return (list_node*) erase_after(__slist_previous(&head, first.node),
                                    last.node);
  }
    ...
};

resize重新調整slist的連結串列.

template <class T, class Alloc>
void slist<T, Alloc>::resize(size_type len, const T& x)
{
  list_node_base* cur = &head;
    // 定位出指定大小的後連結串列的位置
  while (cur->next != 0 && len > 0) {
    --len;
    cur = cur->next;
  }
    // 連結串列過長將cur之後的全部刪除
  if (cur->next) 
    erase_after(cur, 0);
    // 否則連結串列過短, 重新插入
  else
    _insert_after_fill(cur, len, x);
}

template <class T, class Alloc = alloc>
class slist
{
	...
public:
  void resize(size_type new_size, const T& x);
  void resize(size_type new_size) { resize(new_size, T()); }
    ...
};

remove刪除連結串列中所有指定的元素

template <class T, class Alloc>
void slist<T,Alloc>::remove(const T& val)
{
  list_node_base* cur = &head;
  while (cur && cur->next) {
      // 刪除指定元素
    if (((list_node*) cur->next)->data == val)
      erase_after(cur);
    else
      cur = cur->next;
  }
}
template <class T, class Alloc> 
template <class Predicate> void slist<T,Alloc>::remove_if(Predicate pred)
{
  list_node_base* cur = &head;
  while (cur->next) {
      // 自定義條件判斷
    if (pred(((list_node*) cur->next)->data))
      erase_after(cur);
    else
      cur = cur->next;
  }
}

unique刪除連續的相同資料, 只留第一個

template <class T, class Alloc> 
void slist<T,Alloc>::unique()
{
  list_node_base* cur = head.next;
  if (cur) {
    while (cur->next) {
        // 判斷下一個是否相同
      if (((list_node*)cur)->data == ((list_node*)(cur->next))->data)
        erase_after(cur);
      else
        cur = cur->next;
    }
  }
}

merge將兩個連結串列進行合併並排序(前提是兩個連結串列已經排序好了)

template <class T, class Alloc>
void slist<T,Alloc>::merge(slist<T,Alloc>& L)
{
  list_node_base* n1 = &head;
  while (n1->next && L.head.next) {
      // 安從大到小排序
    if (((list_node*) L.head.next)->data < ((list_node*) n1->next)->data) 
      __slist_splice_after(n1, &L.head, L.head.next);
    n1 = n1->next;
  }
  if (L.head.next) {
    n1->next = L.head.next;
    L.head.next = 0;
  }
}

sort

slist排序跟list的排序操作是一樣的, 實現也是類似的. 這裡便使用list的sort步驟來分析

這個sort的分析 :

  • 這裡將每個重要的引數列出來解釋其含義
    1. fill : 當前可以處理的元素個數為2^fill個
    2. counter[fill] : 可以容納2^(fill+1)個元素
    3. carry : 一個臨時中轉站, 每次將一元素插入到counter[i]連結串列中.

在處理的元素個數不足2^fill個時,在counter[i](0<i<fill)之前轉移元素

具體是顯示步驟是:

  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
template <class T, class Alloc>
void slist<T,Alloc>::sort()
{
  if (head.next && head.next->next) {
    slist carry;
    slist counter[64];
    int fill = 0;
    while (!empty()) {
      __slist_splice_after(&carry.head, &head, head.next);
      int i = 0;
      while (i < fill && !counter[i].empty()) {
        counter[i].merge(carry
            
           

相關推薦

STL原始碼分析slist有序容器

前言 上節我們對slist的基本構成, 構造析構做了分析, 本節 我們就來分析關於slist的基本元素操作. slist分析 基本屬性資訊 slist是隻有正向迭代, 所以只能直接獲取頭部的資料. template <class T, class Alloc =

STL原始碼分析slist有序容器

前言 不同於list是Forward Iterator型別的雙向連結串列, slist是單向連結串列, 也是Bidirectional Iterator型別. slist主要耗費的空間小, 操作一些特定的操作更加的快, 同樣類似與slist, 在執行插入和刪除操作迭代器都不會像vec

STL原始碼分析list有序容器

前言 前兩節對list的push, pop, insert等操作做了分析, 本節準備探討list怎麼實現sort功能. list是一個迴圈雙向連結串列, 不是一個連續地址空間, 所以sort功能需要特殊的演算法單獨實現, 而不能用演算法中的sort. 當然還可以將list的元素插

STL原始碼分析deque有序容器

前言 前一節我們分析了deque的基本使用, 本節我們來分析一下deque的對map的操作, 即插入, 刪除等. 但是本節只分析push, pop和刪除操作, 而insert操作有點複雜還是放到下節來分析. push, pop 因為deque的是能夠雙向操作, 所以其push

STL原始碼分析deque有序容器

前言 deque的功能很強大, 其複雜度也比list, vector複雜很多. deque是一個random_access_iterator_tag型別. 前面分析過vector是儲存在連續的線性空間, 頭插入和刪除其代價都很大, 當陣列滿了還要重新尋找更大的空間; deque也是一

STL原始碼分析hashtable關聯容器

前言 前面我們分析過RB-tree關聯容器, RB-tree在插入(可重複和不可重複), 刪除等操作時間複雜度都是O(nlngn), 以及滿足5個規則, 以及以他為底層的配接器; 本節就來分析hashtable另個關聯容器, 他在插入, 刪除等操作都可以做到O(1)的時間複雜度.

STL原始碼分析RB-tree關聯容器

前言 上節我們分析了關於RB-tree的迭代器實現, 其中最重要的功能都會在rb-tree結構中呼叫. 本節我們就來分析RB-tree結構. 再來複習一下紅黑樹的規則: 每個節點的顏色是黑色或者紅色 根節點必須是黑色的 每個葉節點(NULL)必須是黑色的

STL原始碼分析deque

前言 前面兩節對deque基本所有的操作都分析了, 本節就分析deque的insert操作的實現. insert的過載函式有很多, 所以沒有在上節一起分析, 本節也只是對部分過載函式進行分析, 剩下的列出原始碼就行了. 原始碼分析 insert實現 這裡先將insert的

STL原始碼分析RB-tree關聯容器

前言 本節將分析STL中難度很高的RB-tree, 如果對紅黑樹有所認識的那麼分析起來的難度也就不是很大, 對紅黑樹沒有太多瞭解的直接來分析的難度就非常的大了, 可以對紅黑樹有個瞭解紅黑樹之原理和演算法詳細介紹. 紅黑樹是很類似與AVL-tree的, 但是因為AVL-tree在插入,

STL原始碼分析__type_traits型別

前言 上一篇探討的是traits是為了將迭代器沒能完善的原生指標, traits用特化和偏特化程式設計來完善. 這一篇準備探討__type_traits, 為了將我們在空間配置器裡面的提過的__true_type和false_type進行解答. 而type_traits型別對我們ST

STL原始碼分析traits萃取劑

前言 前面我們分析了迭代器的五類, 而迭代器所指向物件的型別被稱為value type. 傳入引數的型別可以通過編譯器自行推斷出來, 但是如果是函式的返回值的話, 就無法通過value type讓編譯器自行推斷出來了. 而traits就解決了函式返回值型別. 同樣原生指標不能內嵌型別

STL原始碼分析迭代器

前言 迭代器是將演算法和容器兩個獨立的泛型進行調和的一個介面. 使我們不需要關係中間的轉化是怎麼樣的就都能直接使用迭代器進行資料訪問. 而迭代器最重要的就是對operator *和operator->進行過載, 使它表現的像一個指標. 型別 迭代器根據移動特性和實施操作

STL原始碼分析記憶體池

前言 上一節只分析了第二級配置器是由多個連結串列來存放相同記憶體大小, 當沒有空間的時候就向記憶體池索取就行了, 卻沒有具體分析記憶體池是怎麼儲存空間的, 是不是記憶體池真的有用不完的記憶體, 本節我們就具體來分析一下 記憶體池 static data template的初始

STL原始碼分析第二級配置器

前言 第一級是直接呼叫malloc分配空間, 呼叫free釋放空間, 第二級三就是建立一個記憶體池, 小於128位元組的申請都直接在記憶體池申請, 不直接呼叫malloc和free. 本節分析第二級空間配置器, STL將第二級配置器設定為預設的配置器, 所以只要一次申請的空間不超過1

STL原始碼分析第一級配置器

前言 上一節我們分析了空間配置器對new的配置, 而STL將空間配置器分為了兩級, 第一級是直接呼叫malloc分配空間, 呼叫free釋放空間, 第二級三就是建立一個記憶體池, 小於128位元組的申請都直接在記憶體池申請, 不直接呼叫malloc和free. 本節我們就先分析第

STL原始碼分析空間配置器

前言 SGI STL將new的申請空間和呼叫建構函式的兩個功能分開實現, 如果對new不太清楚的, 可以先去看看這一篇new實現再來看配置器也不遲. 本節是STL分析的第一篇, 主要分析STL各個部分都會出現的alloc實現, 雖然每個部分都只會預設呼叫它, 不瞭解它也可以看懂分析,

STL原始碼分析multimap配接器

前言 前面我們分析了map, 知道map是不允許插入相同的鍵值的, 也不會儲存第二次的資料, 而本節分析的multimap與map不同, 它允許多個重複的鍵值插入. mutimap操作 int main() { multimap<string, int> mul

STL原始碼分析multiset配接器

前言 前面也分析過set, 並且set不能插入相同的鍵, 本節分析的multiset與set不同之處就是他允許插入相同的鍵. multiset操作 int main() { multiset<string> multi; // 允許重複插入鍵 mult

STL原始碼分析map配接器

前言 上一節分析了pair結構, 正是為map分析做鋪墊, map本身實現也不難, 其資料儲存是pair, 儲存結構是RB-tree, 即map也並不能說是關聯容器, 而應該是配接器. map操作 map的insert必須是以pair為儲存結構, 當然也可以直接使用make_

STL原始碼分析pair結構體

前言 前面在分析set, RB-tree都有在insert實現中出現pair, 下節分析map的時候更會經常出現pair, 所以打算在之前先對pair有個認識. pair是一個有兩個變數的結構體, 即誰都可以直接呼叫它的變數, 畢竟struct預設許可權都是public, 將兩個