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.mzph.cn/news/446253.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

output怎么用_用树莓派实现室内温度监控

树莓派加上温度传感器实现室内温度监控。可用于家庭&#xff0c;轿车&#xff0c;工业&#xff0c;农业 等许多方面。可做温度预警&#xff0c;自动降温等操作。各位小伙伴可自行脑补发挥。1.硬件准备a.树莓派&#xff08;Raspberry Pi&#xff09;一个b.DS18B20温度传感器一个…

STL源码剖析 关联式容器

STL关联式容器以set(集合) 和 map(映射表)两大类&#xff0c;以及对应的衍生体构成,比如mulyiset(多键集合) multimap(多键映射表) ,容器的底层均基于红黑树RB-Tree也是一个独立的容器&#xff0c;但是不对外开放此外还提供了标准之外的关联式容器 hash table散列表&#xff0c…

STL源码剖析 关联式容器 红黑树

概念 红黑树不仅仅是一个二叉树&#xff0c;必须满足如下条件1&#xff0c;每个节点不是红色就是黑色 (深色底纹为黑色&#xff0c;浅色底纹为红色)2&#xff0c;根节点是黑色的3&#xff0c;如果节点为红&#xff0c;其子节点必须为黑色的4&#xff0c;任一节点至NULL(树的尾…

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源码剖析 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 二维数组

声明和初始化 静态初始化 // 静态初始化&#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 …

java三个柱子汉诺塔问题

题目 移动盘子&#xff0c;每一次只能移动一个&#xff0c;小盘子在大盘子上。 打印1 from A to B过程 注意 1&#xff09;盘子编号的变化和辅助柱子的变化 2&#xff09;当盘子编号为1时&#xff0c;结束递归&#xff0c;此时移动结束 代码 package p2;/*** Illustratio…

java杨辉三角形

题目 代码1 public class YangHuiTriangle {public static void main(String[] args) {print(10);}public static void print(int num) {int[][] arr new int[num][];for (int i 0; i < num; i) { // 第一行有 1 个元素, 第 n 行有 n 个元素arr[i] new int[i…

STL源码剖析 基本算法 equal | fill | iter_awap | lexicographical_compare | max | min | swap |mismatch

Equal 两个序列在[first,last)区间内相等&#xff0c;equal()返回true。以第一个序列作为基准&#xff0c;如果第二序列元素多不予考虑&#xff0c;如果要保证两个序列完全相等需要比较元素的个数 if(vec1.size() vec2.size() && equal(vec1.begin(),vec1.end(),vec2…

svm分类器训练详细步骤_「五分钟机器学习」向量支持机SVM——学霸中的战斗机...

大家好&#xff0c;我是爱讲故事的某某某。 欢迎来到今天的【五分钟机器学习】专栏内容 --《向量支持机SVM》 今天的内容将详细介绍SVM这个算法的训练过程以及他的主要优缺点&#xff0c;还没有看过的小伙伴欢迎去补番&#xff1a;【五分钟机器学习】向量支持机SVM——学霸中的…

STL源码剖析 数值算法 copy 算法

copy复制操作&#xff0c;其操作通过使用assignment operator 。针对使用trivial assignment operator的元素型别可以直接使用内存直接复制行为(使用C函数 memove或者memcpy)节约时间。还可以通过函数重载(function overloading)、型别特性(type traits)、偏特化(partial speci…

STL源码剖析 数值算法 copy_backward 算法

copy_backward 时间技巧和copy类似主要是将[first&#xff0c;last)区间范围内的元素按照逆行方向复制到以result-1为起点&#xff0c;方向同样是逆行的区间上返回的迭代器的类型是result - (last - first)copy_backward支持的类型必须是BidirectionalIterators &#xff0c;才…

STL源码剖析 Set相关算法 并集 set_union|交集 set_intersection|差集 set_difference |对称差集 set_symmetric_difference

注意事项 四种相关算法&#xff1a;并集、交集、差集、对称差集本章的四个算法要求元素不可以重复并且经过了排序底层接受STL的set/multiset容器作为输入空间不接受底层为hash_set和hash_multiset两种容器 并集 set_union s1 U s2考虑到s1 和 s2中每个元素都不唯一&#xff0…

python sqlserver 数据操作_python对Excel数据进行读写操作

python对Excel数据进行读写操作将学习到的基础操作记录在这里&#xff0c;便与复习查看1.python读取Excel工作簿、工作表import xlrd # 读取工作簿 wbxlrd.open_workbook(招生表.xls) # 读取工作簿下所有的工作表 wswb.sheets() # 读取工作簿下所有工作表名称 wsnamewb.sheet_n…

Arrays数组工具类

介绍 代码 package lesson.l8_arrays;import java.util.Arrays;/*** Illustration** author DengQing* version 1.0* datetime 2022/6/23 16:53* function Arrays数组工具类*/ public class ArraysUtil {public static void main(String[] args) {int[] arr1 new int[]{1, 12…