[STL]List的实现

STL(Standard template Library):c++的标准模板库


STL是算法和数据结构的软件框架,它包含了六大组件:算法、迭代器、容器、仿函数、配接器、空间配置器。

迭代器:我们可以把迭代器相当于智能指针,(其实指针也就是一个迭代器)迭代器的类型有:前向迭代器(适用于单链表),双向迭代器(适用于双向链表),随机迭代器(随机指向),反向迭代器。

容器:像vector,list,map,set这些都属于容器。分为序列式容器、关联式容器及其他容器。

序列式容器:vector、list、deque、string

关联式容器:set、map、multiset、multimap、hash_set、hash_map、hash_multiset、hash_multimap

其他容器:stack、queue、bitset

仿函数我们之前在冒泡排序的时候用opertor()模拟实现仿函数,来实现排序的升序和降序。

配接器:是一种设计模式,它在原有类型的基础上扩展成为另一个接口,使原本因为接口不兼容而不能合作的类型可以一起工作。就类似于电源适配器一样。这里有应用于容器的适配器,应用于仿函数的适配器,应用于配接器的适配器等。

空间配置器:用户数据都是保存在内存中的,那么内存的申请释放(malloc/free)工作都是由“空间配置器”承担的。标准c++中提供了std::allocator类。

我们今天要模拟实现STL中的list:(STL库中的list是一个双向循环且带头节点的链表)

它的节点原型是这样的:

template<typename T>
struct ListNode
{
    struct ListNode* _next;             //指向下一个节点,即后继节点
    struct ListNode* _prev;             //指向前一个节点,即前驱节点
    T _data;
    ListNode(const T& data)
        :_data(data)
        ,_next(NULL)
        ,_prev(NULL)
    {}
};

我们还要用到迭代器,重载了++、--、*、->、==、!=等操作符。主要实现了以下函数:


begin:指向的是第一个元素的位置,即头节点的下一个位置;

end:指向的是最后一个元素的下一个位置,即头节点的位置。

如图:


对于迭代器,还有一个重要的问题就是迭代器失效的问题,当然迭代器失效和指针指向未知区域的道理是一样的。存在的原因是删除的时候,在链表中,你删除一个元素后,指针必须修改位置,即保存起来以便找到下一个位置。

举个例子(迭代器失效):

//list.cpp

#include<iostream>
using namespace std;
#include<list>
#include<algorithm>void Print(list<int> l)
{list<int>::iterator it = l.begin();while(it != l.end()){cout<<*it<<" ";++it;}
}int main()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);list<int>::iterator it = l.begin();while(it != l.end()) {if(*it == 2) {l.erase(it);      //it删除后不知道指向哪里}++it;}Print(l);return 0;
}

看一下监视窗口:

删除前:


删除后:


那么怎么解决迭代器失效问题呢?

只要保存之前it的指向,以便找到下一个指向就ok啦。

	list<int>::iterator it = l.begin();while(it != l.end()){if(*it == 2){it = l.erase(it);       //此处用it来接收解决迭代器失效问题}++it;}

运行结果



再来说说list,对于库里边的list的函数,如push_back,pop_back,push_front,pop_front,Insert,Erase等函数,下面一 一实现一下哦。

//List.h

#pragma once
#include<iostream>
using namespace std;
template<typename T>
struct ListNode
{struct ListNode* _next;struct ListNode* _prev;T _data;ListNode(const T& data):_data(data),_next(NULL),_prev(NULL){}
};template<typename T,typename Ref,typename Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T,T&,T*> Iterator;typedef ListIterator<T,const T&,const T*> ConstIterator;typedef ListIterator<T,Ref,Ptr> Self;ListIterator(Node* node):_node(node){}bool operator==(const Self& x){return _node == x._node;}bool operator!=(const Self& x){return _node != x._node;}Ref operator*(){return (*_node)._data;}Ref operator*() const{return (*_node)._data;}Ptr operator->(){return &(*_node)._data;}Self& operator++(){_node = (*_node)._next;return *this;}Self operator++(int){Self tmp = *this;++(*this);return tmp;}Self& operator--(){_node = (*_node)._prev;return *this;}Self operator--(int){Self tmp = *this;--(*this);return tmp;}Node* _node;
};template<typename T>
class List
{
public:typedef ListNode<T> Node;typedef ListIterator<T,T&,T*> Iterator;typedef ListIterator<T,const T&,const T*> ConstIterator;
public:Node* BuyNode(const T& data){return new Node(data);}
public:List():_head(BuyNode(T())){_head->_next = _head;_head->_prev = _head;}
public:Iterator Begin()              {return (*_head)._next;}Iterator End(){return _head;}ConstIterator Begin() const{return (*_head)._next;}ConstIterator End() const{return _head;}public:void PushBack(const T& data)              //尾插{Node* tmp = BuyNode(data);Node* tail = _head->_prev;tail->_next = tmp;tmp->_prev = tail;tmp->_next = _head;_head->_prev = tmp;}void PopBack()                //尾删{Node* del = _head->_prev;Node* pre = del->_prev;Node* next = del->_next;pre->_next = next;next->_prev = pre;delete del;}void PushFront(const T& data)          //头插{Node* tmp = BuyNode(data);Node* next = _head->_next;_head->_next = tmp;tmp->_prev = _head;tmp->_next = next;next->_prev = tmp;}void PopFront()               //头删{Node* prev = _head->_prev;Node* next = _head->_next;prev->_next = next;next->_prev = prev;_head = next;}Iterator Insert(Iterator pos,const T& data)      //某一个位置前插入{Node* tmp = BuyNode(data);Node* prev = pos._node->_prev;Node* cur = pos._node;prev->_next = tmp;tmp->_prev = prev;tmp->_next = cur;cur->_prev = tmp;return tmp;}ConstIterator Insert(ConstIterator pos,const T& data)      //某一个位置前插入{Node* tmp = BuyNode(data);Node* prev = pos._node->_prev;Node* cur = pos._node;prev->_next = tmp;tmp->_prev = prev;tmp->_next = cur;cur->_prev = tmp;return tmp;}Iterator Erase(Iterator pos)            //删除某个位置{assert(pos._node && pos._node != _head);    //判断节点是否为空,或只剩一个头节点Node* prev = pos._node->_prev;Node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return Iterator(next);           //返回下一个位置}bool Empty()        //判断链表是否为空{return _head == _head->_next;}T& Front(){return _head->_next->_data;}T& Back(){return _head->_prev->_data;}const T& Front() const{return _head->_next->_data;}const T& Back() const{return _head->_prev->_data;}void Swap(List<T>& l){swap(_head,l._head);}template<typename InputIterator>void Insert(Iterator pos,InputIterator first,InputIterator last){while(first != last){Insert(pos,*first);++first;}}void Insert(Iterator pos,ConstIterator first,ConstIterator last)  //在pos位置插入一段区间{for(;first != last;++first)Insert(pos,*first);}
private:Node* _head;
};void PrintList(List<int>& l)
{List<int>::Iterator it = l.Begin();while(it != l.End()){cout<<*it<<" ";++it;}cout<<endl;
}


//List.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
#include<vector>
int main()
{List<int> l;List<int> l1;const List<int> l3;l.PushBack(1);l.PushBack(2);l.PushBack(3);l.PushBack(4);l.PopBack();PrintList(l);l.PushFront(0);PrintList(l);l.PopFront();l.Insert(l.End(),4);l.Insert(l.Begin(),0);PrintList(l);cout<<"Front:"<<l.Front()<<endl;cout<<"Back:"<<l.Back()<<endl;cout<<"Empty:"<<l.Empty()<<endl;l1.Swap(l);PrintList(l1);PrintList(l);l.PushBack(10);l.PushBack(11);l.PushBack(12);l1.Insert(l1.Begin(),l.Begin(),l.End());PrintList(l1);vector<int> v;v.push_back(55);v.push_back(66);l1.Insert(l1.Begin(),v.begin(),v.end());PrintList(l1);char str[] = {'a','b'};l1.Insert(l1.Begin(),str,str+2);PrintList(l1);return 0;
}

运行结果:


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382776.shtml

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

相关文章

vc++6.0的应用程序打不开肿么办

今天早起&#xff0c;有同学问到我关于vc6.0的安装过程中遇到的问题&#xff0c;我听了之后想想还是写篇博客给大家看一下吧。因为我之前也遇到过类似的问题。当时也是挺着急的。 大家遇到的问题估计就是这样吧~~&#xff08;下载后打不开&#xff09; 请看-->解决步骤&…

【数据结构】广义表

一、问题概述 广义表是非线性的数据结构&#xff0c;是由若干个元素组合而成的&#xff0c;广义表中可以有子表&#xff0c;类似这样的&#xff1a; 我们以C(a,b,(c,d))为例&#xff0c;将它定义为这样的数据结构&#xff1a; 我们会给定字符串的形式&#xff0c;如&#xff…

【数据结构】普通二叉树的实现

一、问题概述 树是n个有限个数据的集合&#xff0c;形如&#xff1a; 它像不像倒着的树呢&#xff1f;我们把它看成是一种数据结构----树。它的第一个节点称作树的根&#xff0c;最底下的那些节点称作树的叶子。 我们今天所要研究的是二叉树&#xff0c;即父节点最多只有两个孩…

setitimer用法说明

函数原型&#xff1a; int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value) 函数作用&#xff1a; 可用来实现延时和定时的功能 头文件&#xff1a; #include <sys/time.h> 参数详解 用一把&#xff1a;一个例子 #include &…

哈希表(闭散列、拉链法--哈希桶)

哈希表&#xff0c;也称散列表&#xff0c;是一种通过key值来直接访问在内存中的存储的数据结构。它通过一个关键值的函数&#xff08;被称为散列函数&#xff09;将所需的数据映射到表中的位置来访问数据。 关于哈希表&#xff0c;主要为以下几个方面&#xff1a; 一、哈希表…

僵尸进程的产生和SIGCHLD信号

核心句子 子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数。 僵尸进程的产生&#xff1a; #include "head.h" #include <unistd.h> #include <signal.h>int main() {key_t key ftok(&quo…

海量数据处理--位图(BitMap)

对于海量数据这个词&#xff0c;大家不难理解吧。主要是针对给定的数据量特别大&#xff0c;占用内存特别大的情况。那么和位图有什么关系呢。看下面一个腾讯的海量数据的例子吧。 例&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0…

ps命令与top命令参数意义详解

文章目录1.ps -l2.ps aux3.top面试经常被问道&#xff0c;特别是top。1.ps -l 参数解释F代表这个程序旗标 (process flags)&#xff0c;说明这个程序的总结权限&#xff0c;常见号码有&#xff1a;o 若为 4 表示此程序的权限为 root &#xff1b;o 若为 1 则表示此子程序仅进行…

哈希拓展--布隆过滤器

一、问题概述 布隆过滤器是由布隆提出来的&#xff0c;是由一个很长的二进制序列和一系列的映射函数组成。主要用于检测一个元素是否在一个集合中。当然在设计计算机软件时&#xff0c;我们也经常会判断一个元素是否在一个集合中。比如&#xff1a;在字处理软件中&#xff0c;…

排序(Sort)--【一】

排序&#xff0c;对于大家再熟悉不过了吧。我们之前在学习c语言的时候接触过的冒泡排序&#xff0c;选择排序等。今天给大家介绍两种新的排序。 1、直接插入排序 升序排列&#xff1a;将第一个数确定好&#xff0c;从下标为1的数开始插入&#xff0c;如果插入的数比前一个数大…

快速排序--全集

快速排序&#xff1a;一听名字就知道这种排序很快的&#xff0c;是吧&#xff1f;没错&#xff0c;它是一种效率比较高的排序算法。 快速排序采用的是分治的思想。 比如&#xff0c;将一串数中的一个元素作为基准&#xff0c;然后将比它小的数排在它的左边&#xff0c;比它大…

task_struct结构体查找

网上有很多解析task_struct结构体的文章&#xff0c;可是都没有说这个结构体到底在哪里&#xff1f; 这个结构体位于头文件 shced.h cd / find -name sched.h 显示结果如下 注意只有 位于内核中的include 才是正确的。 /usr/src/kernels/2.6.32-431.el6.i686/include/linux…

闹钟函数alarm()的解释与实践

alarm 定义 也称为闹钟函数&#xff0c;它可以在进程中设置一个定时器&#xff0c;当定时器指定的时间到时&#xff0c;它向进程发送SIGALRM信号。可以设置忽略或者不捕获此信号&#xff0c;如果采用默认方式其动作是终止调用该alarm函数的进程。 #include "head.h&quo…

Linux下如何设置权限让用户只删除自己的文件(粘滞位)

之前我们知道如何针对用户和用户组来设置文件权限。通常是用三个八进制来设置权限的&#xff0c;这里我要说的是&#xff0c;其实是由四个八进制表示的。其中第一个八进制我们通常是忽略的。第二个到第四个是对应于SUID,SGID,sticky-bit。 SUID&#xff1a;设置了SUID 位的文件…

Vim简单配置

vim配置&#xff1a; &#xff08;在Centos6.5下配置vim&#xff09; 1.找到用户的主工作目录&#xff0c;ls看是否有.vimrc文件&#xff0c;有的话打开即可。没有的话自己touch一个。vim进入.vimrc中&#xff1a; set nu 设置行数 colorscheme desert syntax enabl…

[WPS笔试题]实现栈的push,pop,max且时间复杂度为O(1)

今天做了一下WPS的笔试题&#xff0c;遇到了一道关于栈的题&#xff0c;觉得挺有意思的&#xff0c;就写篇博客分享一下吧~~ 题目要求&#xff1a;要求实现栈的数据结构&#xff0c;在该类型中实现一个能够得到栈的最大元素的max函数&#xff0c;在该栈中&#xff0c;调用max,…

[剑指Offer]替换空格

今天看题的时候&#xff0c;遇到一个替换空格的题目&#xff0c;分析一下哈。 题目要求&#xff1a;把字符串中的每个空格替换成“%20”。例如输入“we are happy”&#xff0c;则输出“we%20are%20happy”。 解题思路&#xff1a;我们首先想到的是&#xff1a;移位思想。遇到…

C语言关键字 ISO/ANSI C90 C99 C11

面试考点 https://blog.csdn.net/csdn_kou/article/details/81113215 * 有的常用的我们都不知道是关键字&#xff0c;比如sizeof.这是面试中的考点&#xff0c;要注意。 * 同时当回答C语言中有多少关键字时&#xff0c;要回答前题条件&#xff0c;时针对哪一个版本

关于sudo

之前&#xff0c;我们使用sudo的时候&#xff0c;是因为其用户本身具有root权限&#xff0c;所以可以sudo后执行相关操作&#xff0c;但是对于普通用户来说&#xff0c;它是既不具有sudo权限&#xff0c;又不在sudo用户组中&#xff0c;那么我们来研究一下如何将新创建的用户添…