STL源碼剖析 slist單向鏈表概述

概述

  • SGI STL的list是一個雙向鏈表,單向鏈表是slist,其不在標準規格之內
  • 單向和雙向鏈表的區別在于,單向鏈表的迭代器是單向的 Forward Iterator,雙向鏈表的迭代器屬于雙向的Bidirectional Iterator。因此很多功能都被受限
  • 但是單向鏈表的耗用的空間更小,某些操作更快
  • 注意事項:插入操作會將新的元素插入到指定的位置之前,而不是之后的位置。但是單向鏈表無法回頭確定前一個位置,因此需要從頭部開始重新找起,即 除了在單向鏈表的起始處附近其余的地方進行insert 或者 erase都是不合適的操作。這也是單向和雙向鏈表之間最大的差異,因此 單向鏈表提供了 insert_after() 和 erase_after() 供靈活使用
  • 基于上述的考慮,slist 也不提供push_back() 只提供push_front(),因此一定要注意單向鏈表元素的順序和 元素的插入進來的順序 相反

slist的節點

  • 單向鏈表的迭代器比雙向鏈表更為復雜一些,運用了繼承關系,因此在型別轉換層面上有著復雜的表現

#include <iostream>//單向鏈表的節點的基本結構
struct __slist_node_base{__slist_node_base* next;
};//單向鏈表的節點結構
template <class T>
struct __slist_node : public __slist_node_base{T data;
};//全局函數 已知某一個節點,插入新的節點于其后
inline __slist_node_base* __slist_make_link(__slist_node_base* prev_node,__slist_node_base* new_node){//令new_node節點的下一個節點為prev節點的下一節點new_node->next = prev_node->next->next;prev_node->next = new_node; //令prev節點的next指針指向new節點return new_node;
}//全局函數 單向鏈表的大小(元素的個數)
inline std::size_t __slist_size(__slist_node_base* node){std::size_t result = 0;for( ; node != 0;node = node->next){++result; //一個一個累計}return result;
}

slist的迭代器

  • ?實際構造的時候 注意迭代器和節點之間的關系
//單向鏈表的迭代器的基本結構
struct __slist_iterator_base{typedef std::size_t size_type;typedef std::ptrdiff_t difference_type;typedef std::forward_iterator_tag iterator_category; //注意 單向__slist_node_base* node; //指向節點的基本結構 next指針__slist_iterator_base(__slist_node_base* x): node(x){}void incr(){node = node->next; } //前進一個節點bool operator==(const __slist_iterator_base& x)const{return node == x.node;}bool operator!=(const __slist_iterator_base& x)const{return node != x.node;}
};//單向鏈表的迭代器結構
template <class T,class Ref,class Ptr>
struct __slist_iterator : public __slist_iterator_base{typedef __slist_iterator<T,T&,T*>  iterator;typedef __slist_iterator<T,const T&,const T*>const_iterator;typedef __slist_iterator<T,T&,T*>  self;typedef T value_type;typedef Ptr pointer;typedef Ref reference;typedef __slist_node<T> list_node;//調用slist<T>::end()會造成__slist_iterator(0) 于是調用如下函數__slist_iterator(list_node* x) : __slist_iterator_base(x){}__slist_iterator(): __slist_iterator_base(0){}__slist_iterator(const iterator& x): __slist_iterator_base(x.node){}reference operator*() const {return ((list_node*)node)->data;}reference operator->() const {return &(operator*());}self& operator++(){incr(); //前進一個節點return *this;}self& operator++(int){self tmp = *this;incr(); //前進一個節點return tmp;}//沒有實現operator-- 因為這是一個單向鏈表,使用的是forward iterator
};
  • 注意比較兩個slist迭代器是否等同時,比如常規循環下需要判斷 迭代器是否等同于 slist.end()?
  • 但是__slist_iterator并沒有對operator==進行重載,所以會調用 __slist_iterator_base::operator== ,__slist_iterator_base::operator==內部通過判定?__slist_iterator_base* node來判定兩個迭代器是否等同

slist的數據結構

#include <iostream>//單向鏈表的節點的基本結構
struct __slist_node_base{__slist_node_base* next;
};//單向鏈表的節點結構
template <class T>
struct __slist_node : public __slist_node_base{T data;
};//全局函數 已知某一個節點,插入新的節點于其后
inline __slist_node_base* __slist_make_link(__slist_node_base* prev_node,__slist_node_base* new_node){//令new_node節點的下一個節點為prev節點的下一節點new_node->next = prev_node->next->next;prev_node->next = new_node; //令prev節點的next指針指向new節點return new_node;
}//全局函數 單向鏈表的大小(元素的個數)
inline std::size_t __slist_size(__slist_node_base* node){std::size_t result = 0;for( ; node != 0;node = node->next){++result; //一個一個累計}return result;
}//單向鏈表的迭代器的基本結構
struct __slist_iterator_base{typedef std::size_t size_type;typedef std::ptrdiff_t difference_type;typedef std::forward_iterator_tag iterator_category; //注意 單向__slist_node_base* node; //指向節點的基本結構 next指針__slist_iterator_base(__slist_node_base* x): node(x){}void incr(){node = node->next; } //前進一個節點bool operator==(const __slist_iterator_base& x)const{return node == x.node;}bool operator!=(const __slist_iterator_base& x)const{return node != x.node;}
};//單向鏈表的迭代器結構
template <class T,class Ref,class Ptr>
struct __slist_iterator : public __slist_iterator_base{typedef __slist_iterator<T,T&,T*>  iterator;typedef __slist_iterator<T,const T&,const T*>const_iterator;typedef __slist_iterator<T,T&,T*>  self;typedef T value_type;typedef Ptr pointer;typedef Ref reference;typedef __slist_node<T> list_node;//調用slist<T>::end()會造成__slist_iterator(0) 于是調用如下函數__slist_iterator(list_node* x) : __slist_iterator_base(x){}__slist_iterator(): __slist_iterator_base(0){}__slist_iterator(const iterator& x): __slist_iterator_base(x.node){}reference operator*() const {return ((list_node*)node)->data;}reference operator->() const {return &(operator*());}self& operator++(){incr(); //前進一個節點return *this;}self& operator++(int){self tmp = *this;incr(); //前進一個節點return tmp;}//沒有實現operator-- 因為這是一個單向鏈表,使用的是forward iterator
};template<class T,class Alloc>
class simple_alloc{
public:static T* allocate(std::size_t n){return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));}static T* allocate(void){return (T*)Alloc::allocate(sizeof (T));}static void deallocate(T* p,size_t n){if (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));}
};#ifdef __STL_USE_EXCEPTIONS
#define __STL_TRY   try
#define __STL_UNWIND(action)   catch(...) { action; throw; }
#else
#define __STL_TRY
#define __STL_UNWIND(action)
#endifnamespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value);  //沒看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T           value_type;typedef T*          pointer;typedef const T*    const_pointer;typedef T&          reference;typedef const T&    const_reference;typedef std::size_t size_type;typedef ptrdiff_t   difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}};
}template <class T,class Alloc>
class slist{
public:typedef T value_type;typedef value_type* pointer;typedef const value_type* const_pointer;typedef value_type& reference;typedef const value_type& const_reference;typedef std::size_t size_type;typedef std::ptrdiff_t difference_type;typedef __slist_iterator<T,T&,T*> iterator;typedef __slist_iterator<T,const T&,const T*>const_iterator;
private:typedef __slist_node<T> list_node;typedef __slist_node_base list_node_base;typedef __slist_iterator_base iterator_base;typedef simple_alloc<list_node,Alloc>list_node_allocator;static list_node* create_node(const value_type& x){list_node* node = list_node_allocator::allocate(); //配置空間__STL_TRY{Chy::allocator<T>::construct(&node->data,x);node->next = 0;};__STL_UNWIND(list_node_allocator::deallocate(node);) //釋放空間}static void destroy_node(list_node* node){Chy::allocator<T>::destroy(&node->data);//將元素析構list_node_allocator::deallocate(node);  //釋放空間}
private:list_node_base head; //頭部 注意head不是指針,是事物
public:slist(){head.next = 0;}~slist(){clear();}
public:void clear(){erase_after(&head,0);}//全局函數 傳遞鏈表的頭部和尾部inline list_node_base* erase_after(list_node_base* before_first,list_node_base* last_node){list_node * cur = (list_node*)(before_first->next);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:iterator begin(){return iterator((list_node*)head.next);}iterator end(){return iterator(0);}size_type size() const{return __slist_size(head.next);}bool empty() const {return head.next == 0;}//兩個slist之間互換,只需要交換head頭結點即可void swap(slist& L){list_node_base* tmp = head.next;head.next = L.head.next;L.head.next = tmp;}//獲取頭部元素reference front(){return ((list_node*)head.next)->data;}//從頭部插入元素 新的元素成為slist的第一個元素void push_front(const value_type& x){__slist_make_link(&head, create_node(x));}//注意 沒有push_back()//從頭部取出元素,將其刪除之 修改headvoid pop_front(){list_node * node = (list_node*)head.next;head.next = node->next;destroy_node(node);}};

元素操作

參考鏈接

  • STL源碼剖析(十二)序列式容器之slist_JT同學的博客-CSDN博客

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446253.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446253.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446253.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

output怎么用_用樹莓派實現室內溫度監控

樹莓派加上溫度傳感器實現室內溫度監控。可用于家庭&#xff0c;轎車&#xff0c;工業&#xff0c;農業 等許多方面。可做溫度預警&#xff0c;自動降溫等操作。各位小伙伴可自行腦補發揮。1.硬件準備a.樹莓派&#xff08;Raspberry Pi&#xff09;一個b.DS18B20溫度傳感器一個…

java 家庭收支賬戶

代碼1 package lesson.project.p1_familyAccount;import java.util.Scanner;public class FamilyAccount {public static int money 10000;public static String all "";public static void main(String[] args) {Scanner scanner new Scanner(System.in);while …

【Android 13】使用Android Studio調試系統應用之Settings移植(四):40+個依賴子模塊之ActionBarShadow

文章目錄 一、篇頭二、系列文章2.1 Android 13 系列文章2.2 Android 9 系列文章2.3 Android 11 系列文章三、子模塊AS移植3.1 AS創建目標3.2 創建ActionBarShadow(1)使用VS Code打開org_settings/SettingsLib目錄(2)ActionBarShadow的Manifest.xml(3)ActionBarShadow的An…

STL源碼剖析 關聯式容器

STL關聯式容器以set(集合) 和 map(映射表)兩大類&#xff0c;以及對應的衍生體構成,比如mulyiset(多鍵集合) multimap(多鍵映射表) ,容器的底層均基于紅黑樹RB-Tree也是一個獨立的容器&#xff0c;但是不對外開放此外還提供了標準之外的關聯式容器 hash table散列表&#xff0c…

python求小于n的所有素數_用python求出2000000內所有素數的和?不知怎么寫?

展開全部 import itertools import time N 2000000 L range(N) def findnxt(s): flag 0 for n in itertools.ifilter(None, L[s1:]): return n t0 time.time() n 2 X int(N ** .5) while n < X: for i, x in enumerate(L[::n][1:]): if i0: continue L[x] 0 n findn…

STL源碼剖析 關聯式容器 紅黑樹

概念 紅黑樹不僅僅是一個二叉樹&#xff0c;必須滿足如下條件1&#xff0c;每個節點不是紅色就是黑色 (深色底紋為黑色&#xff0c;淺色底紋為紅色)2&#xff0c;根節點是黑色的3&#xff0c;如果節點為紅&#xff0c;其子節點必須為黑色的4&#xff0c;任一節點至NULL(樹的尾…

java藍橋杯 試題-基礎練習-數列排序

試題-基礎練習-數列排序 題目 問題描述   給定一個長度為n的數列&#xff0c;將這個數列按從小到大的順序排列。1<n<200 輸入格式   第一行為一個整數n。   第二行包含n個整數&#xff0c;為待排序的數&#xff0c;每個整數的絕對值小于10000。 輸出格式   輸出…

python生成的詞云沒有圖案_還在為專欄封面發愁?我用Python寫了個詞云生成器!...

媽媽再也不用擔心我寫專欄找不到合適的封面了&#xff01;B站專欄的封面至少是我一直頭疼的問題&#xff0c;每次寫完文章卻找不到合適的圖片作為封面。 詞云是一個很不錯的選擇&#xff0c;既美觀&#xff0c;又提綱挈領。網上也有詞云生成的工具&#xff0c;但大多收費/只能試…

java 1000以內的完數

題目 代碼 package lesson.l6_review;public class PrefectNumber {public static void main(String[] args) {for (int i 1; i <1000 ; i) {int num0;for (int j 1; j <i-1 ; j) {if (i%j0){numj;}}if (inum){System.out.print(i"\t");}}} }

STL源碼剖析 set集合

set的特性是 所有的元素會按照鍵值自動排序set 的鍵值等同于實值set不允許涵蓋兩個相同的鍵值不可以通過迭代器修改set的元素數值&#xff0c;這會破壞元素的排列順序。因此set<T>::iterator 被定義為底層RB-tree的const_iterator,杜絕寫入。也就是set的iterators是一種c…

python turtle畫圣誕樹動圖_圣誕節!教你用Python畫棵圣誕樹

作者 | 糖甜甜甜&#xff0c;985高校經管研二&#xff0c;擅長用 Python、R、tableau 等工具結合統計學和機器學習模型做數據分析。 如何用Python畫一個圣誕樹呢&#xff1f; 最簡單&#xff1a; 1height 5 2 3stars 1 4for i inrange(height): 5print(( * (height - i)) (** …

java藍橋杯 試題-基礎練習-十六進制轉八進制

試題-基礎練習-十六進制轉八進制 題目 試題 基礎練習 十六進制轉八進制 資源限制 時間限制&#xff1a;1.0s 內存限制&#xff1a;512.0MB 問題描述   給定n個十六進制正整數&#xff0c;輸出它們對應的八進制數。 輸入格式   輸入的第一行為一個正整數n &#xff08;1…

STL源碼剖析 map

所有元素會根據元素的鍵值自動被排序 元素的類型是pair&#xff0c;同時擁有鍵值和實值&#xff1b;map不允許兩個元素出現相同的鍵值pair 代碼 template <class T1,class T2> struct pair{typedef T1 first_type;typedef T2 second_type;T1 first; //publicT2 secon…

java api接口怎么寫_Java 如何設計 API 接口,實現統一格式返回?

來源&#xff1a;老顧聊技術前言接口交互返回格式控制層Controller美觀美化優雅優化實現方案前言在移動互聯網&#xff0c;分布式、微服務盛行的今天&#xff0c;現在項目絕大部分都采用的微服務框架&#xff0c;前后端分離方式&#xff0c;(題外話&#xff1a;前后端的工作職責…

java 輸出學生成績和成績等級

題目 從鍵盤讀入學生成績&#xff0c;找出最高分&#xff0c;并輸出學生成績等級。?成績>最高分-10 等級為’A’?成績>最高分-20 等級為’B’?成績>最高分-30 等級為’C’?其余 等級為’D’提示&#xff1a;先讀入學生人數&#xff0c;根據人數創建int數組&#…

STL源碼剖析 multiset 和 multimap

multiset和set完全相同&#xff0c;唯一的差別在于允許鍵值的重復&#xff0c;因此底層操作使用的是紅黑樹的insert_equal() 而不是insert_unique()multimap和map完全相同&#xff0c;唯一的差別在于允許鍵值的重復&#xff0c;因此底層操作使用的是紅黑樹的insert_equal() 而不…

java 二維數組

聲明和初始化 靜態初始化 // 靜態初始化&#xff1a; // 一維數組int[] arr1_1 {1, 2, 4};System.out.println(Arrays.toString(arr1_1)); // 二維數組int[][] arr1_2 {{1, 2}, {4, 5}, {9, 10}};for (int[] i :arr1_2) {System.out.print(Arrays.toS…

STL源碼剖析 hashtable

二叉搜索樹具有對數平均時間的表現&#xff0c;但是這個需要滿足的假設前提是輸入的數據需要具備隨機性hashtable 散列表這種結構在插入、刪除、搜尋等操作層面上也具有常數平均時間的表現。而且不需要依賴元素的隨機性&#xff0c;這種表現是以統計為基礎的 hashtable的概述 …

append在python里是什么意思_“一棵綠蘿七個鬼”是什么意思?臥室里到底能不能養綠蘿!...

很多人都喜歡在家里養盆綠蘿&#xff0c;一是能凈化室內空氣&#xff0c;讓家里綠意濃濃&#xff0c;更有生機一些&#xff1b;二是綠蘿好養&#xff0c;水培土培都行&#xff0c;養著也省心。在養花界有一句俗語&#xff1a;“一棵綠蘿七個鬼”&#xff0c;這句話是什么意思呢…

java 二分查找

注意 二分查找要求原數組為有序序列&#xff0c;從小到大 遞歸解法 public class problem9 {public static void main(String[] args) {int[] arr {1,2,3,4,6,7};int left 0;int right arr.length - 1;int value 2;System.out.println(Arrays.toString(arr));int index …