STL 利用模板自制链表容器

迭代器:

在这里插入图片描述

1. 迭代器概念

在这里插入图片描述

  • 起始迭代器(红色): 遍历链表
  • 终止迭代器(蓝色): 结束标志

2. 迭代类的分类

在这里插入图片描述

  • STL标准模板库中,每一种迭代器都有4种类

2.1 正向非常迭代类

在这里插入图片描述

2.1.1 起始迭代器、终止迭代器

在这里插入图片描述

2.1.2 迭代器指定位置添加、删除节点

在这里插入图片描述
在这里插入图片描述

2.2 正向常迭代类

常迭代器本省没有常属性,迭代器可以改,可以++,--做链表遍历, 但是不能通过常迭代器更改链表中的节点。
和非常迭代器的区别就是:所有通过迭代器对链表进行改操作的路全被堵上。
所以对于常迭代类对象不会提供insert()和erase()函数
在这里插入图片描述

3. 查找功能

IT 就是迭代器类,查找不成功返回第二个参数(迭代器)
在这里插入图片描述

4 排序功能

在这里插入图片描述
迭代器i 和 j 是给的排序范围
迭代器p作为基准与 迭代器i 和 迭代器j 范围内的节点做顺序调整, p在整个快速排序过程中和 i或j相同。基准就是本次排序针对的是哪个节点
循环: 对p和i(j) 做一次判断可能会调整顺序,然后i++ 或者 j-- 缩小需要顺序调整的范围, 然后把p置为 i或j, 直到p, i, j 三个迭代器相同, 也就找到了基准在链表中的正确位置。
然后换基准,再重复上述过程,也就是递归。

自制链表容器:

#include <stdexcept>
#include <iostream>
using namespace std;
// list<T>代表双向链表类,链表里的每个节点是T类型的数据
template <class T> class list {
public:
    // 缺省构造, 空链表
    list():m_head(NULL), m_tail(NULL){}
    
    // 拷贝构造, 用一个存在的链表拷贝构造一个新的链表(自己实现的拷贝构造都是深拷贝, 编译器提供的缺省都是浅拷贝)
    list(const list & that):m_head(NULL), m_tail(NULL){
        for(node* p_node = that.m_head; p_node; p_node = p_node->m_next){
            push_back(p_node->m_data);
        }
    }
    
    // 析构函数
    ~list(){
        clear();
    }
    // 链表判空
    bool empty(){
        return m_head == NULL && m_tail == NULL;
    }

    // 添加头结点 
    void push_front(T const& data){
        /*
        // 不考虑特殊情况代码如下
        // 1. 创建一个节点,前指针为NULL, 后指针指向头节点
        m_head = new node(NULL, data, m_head);
        // 2. 新头节点的后一个节点的前指针指向投头节点
        m_head->m_next->m_prev = m_head;
        */
        m_head = new node(NULL, data, m_head);
        // 2. 不能保证新new的节点有下一个节点
        if (m_head->m_next){
            // 空指针使用 ->m_next, 会有段错误
            m_head->m_next->m_prev = m_head;
        } else {    
            // 3. 如果没有, 现在添加的这个新节点既是头节点也是尾节点
            m_tail = m_head;
        }
    }

    // 删除头结点
    void pop_front(){
        /*
        不考虑特殊情况代码如下:
        // 1. 备份头节点的下一个节点,然后删掉头节点
        node* p_node = m_head->m_next;
        delete m_head;
        // 2. 把这个节点的前指针指向空
        p_node->m_prev = NULL;
        // 3. 再把这个节点设为头节点
        m_head = p_node;
        */
        // 防止用户调用pop时没有做非空链表判断
        if(empty()){
            return ;
        }
        node* p_node = m_head->m_next;
        delete m_head;
        // 没法保证头节点还有下一个节点
        // 2. 如果有,把它设置为头节点
        if(p_node){  // 空指针使用 ->m_prev 会有段错误
            p_node->m_prev = NULL;
        } else {
        // 3. 如果没有,那链表现在就成空链表了,把m_head和m_tail都置为NULL
            m_tail = NULL;
        }
        m_head = p_node;
    }

    // 获取头结点元素 (这里用的引用,所以可以通过返回的引用去更改头结点的数据)
    T& front(){
        if(empty()){
            throw underflow_error("null node");
        }
        return m_head->m_data;
    }

    // 下面写一个重载版本
    // C++中,只要常对象只能使用常函数
    // 一个常链表对象(任何节点不能被更改)只能调下面的函数,下面返回的也是一个const类型的引用,不能被修改
    T const& front()const{
        // 常对象调常函数,常函数把常对象的常属性再去调用非常函数, 然后再返回值用常引用接收
        return const_cast<list *>(this)->front();
    }

    // 添加尾结点
    void push_back(T const& data){
        m_tail = new node(m_tail, data, NULL);
        if(m_tail->m_prev){
           m_tail->m_prev->m_next = m_tail;
        } else {
            m_head = m_tail;
       }
    }

    // 删除尾结点
    void pop_back(){
        if(empty()){
            return;
        }
        node *p_node = m_tail-> m_prev;
        delete m_tail;
        if(p_node){
            p_node->m_next = NULL;
        } else {
            m_head = NULL;
        }
        m_tail = p_node;    
    }

    // 获取尾节点元素
    T& back(){
        if(empty()){
            throw underflow_error("null node");
        }
        return m_tail->m_data;
    }
    // 获取尾节点元素(重载版本)
    T const& back()const{
        return const_cast<list *>(this)->back();
    }

    // 清空链表
    void clear(){
        while (empty())
        {
            pop_front();
        }
    }

    // 获取链表大小
    size_t size(){
        size_t i = 0;
        for(node* p_node = m_head; p_node; p_node = p_node->m_next){
            i++;
        }
        return i;
    }

private:
    // 节点类(模板嵌套)
    class node {
    public:
        node(node* prev, T const& data, node* next):m_prev(prev), m_data(data), m_next(next){}
        node* m_prev;  // 前指针
        T m_data;       // 节点数据
        node* m_next;   // 后指针
    };

public:
    ///     正向非常迭代类(模板嵌套)    ///
    class iterator{
    public:
        iterator(node* start, node* cur, node* end):m_start(start), m_cur(cur), m_end(end){}
        T& operator*(){
            if(m_cur == NULL){
                throw underflow_error("null node");
            }
            return m_cur->m_data;
        }
        iterator& operator++(){
            if(m_cur == NULL){// 当迭代器m_cur指向尾节点的下一个节点(NULL)
                // 这里做一个循环迭代
                m_cur = m_start;
            } else {
                // 迭代器的m_cur指向下一个节点,然后返回迭代器本身
                m_cur = m_cur->m_next;
            }
            return *this;
        }
        iterator& operator--(){
            if(m_cur == NULL){  // 当迭代器m_cur指向头节点的前一个节点(NULL)
                m_cur = m_end;
            } else {
                m_cur = m_cur->m_prev;
            }
            return *this;
        }
        // 怕左操作数是常对象,这里搞成常函数
        bool operator==(iterator const& that)const{
            return m_start == that.m_start && m_cur == that.m_cur && m_end == that.m_end; 
        }
        bool operator!=(iterator const& that)const{
            // return m_start =! that.m_start || m_cur != that.m_cur || m_end != that.m_end;
            // 但是下面这样写更优雅
            return !(*this==that); //复用上面的==重载
        }
    private:
        node* m_start;  // 开始指向
        node* m_cur;    // 当前指向
        node* m_end;    // 终止指向
        friend class list;  // 在list类中可以使用 iterator 的私有成员
    };
    
    // 获取起始迭代器对象(红色)遍历链表
    iterator begin(){
        return iterator(m_head, m_head, m_tail);
    }

    // 获取终止迭代器对象(蓝色)结束标志
    iterator end(){
        return iterator(m_head, NULL, m_tail);
    }

    // 迭代器指向位置前方添加新节点
    void insert(iterator const& loc, T const& data){
        /*
        // 不考虑极限情况,代码如下
        // 1. 创建一个节点,前指针指向迭代器当前指向节点, 后指针指向迭代器当前指向的节点
        node* p_node = new node(loc.m_cur->m_prev, data, loc.m_cur);
        // 2. 完成添加新节点的步骤(双向)
        p_node->m_prev->m_next = p_node;
        p_node->m_next->m_prev = p_node;
        */
        // 极限情况,loc迭代器指向无效节点,尾节点的下一个位置, 添加新节点就是添加尾节点
        if(loc == end()){
            push_back(data);
        } else {    // loc指向有效节点
            node* p_node = new node(loc.m_cur->m_prev, data, loc.m_cur);
            // 但不能保证新new的节点一定有前一个节点, 但是可以保证新new的节点一定有后一个节点 loc.m_cur(之前做过判断是有效节点)
            if(p_node->m_prev){ // 如果有前一个节点
                p_node->m_prev->m_next = p_node;
            } else {   // 如果没有, 说明p_node是将要加入的头节点
                m_head = p_node;
            }
            p_node->m_next->m_prev = p_node;
        }
    }

    // 删除迭代器指向的节点
    void erase(iterator const& loc){
        /*
        // 不考虑极限情况
        // 1. 架空 p_del, 然后delete p_del
        p_del->m_prev->m_next = p_del->m_next;
        p_del->m_next->m_prev = p_del->m_prev;
        delete p_del; 
        */
        if(loc == end()){  // 如果loc指向无效节点
            return;
        }
        node *p_del = loc.m_cur;
        // 没有办法保证 p_del 一定有前一个节点(比如p_del是头节点)
        if (p_del->m_prev) {
            p_del->m_prev->m_next = p_del->m_next;
        } else {
            m_head = p_del->m_next;
        }
        // 没有办法保证 p_del 一定有下一个节点(比如p_del是尾节点)
        if(p_del->m_next){
            p_del->m_next->m_prev = p_del->m_prev;
        } else {
            m_tail = p_del->m_prev;
        }
        delete p_del; 
    }

    ///     正向常迭代类(模板嵌套)      /// 
    class const_iterator{
    public:
        const_iterator(iterator const& it):m_it(it){}
        T const& operator*(){
            // 触发 iterator的*操作符重载,然后用常引用接收
            return *m_it;
        }
        const_iterator& operator++(){
            ++m_it;
            return *this;
        }
        const_iterator& operator--(){
            --m_it;
            return *this;
        }
        bool operator==(const_iterator const & that)const{
            // m_it是iterator类对象,这里会触发 iterator 的 == 操作符重载
            return m_it==that.m_it;
        }
        bool operator!=(const_iterator const & that)const{
            // 触发 const_iterator 的 == 操作符重载
            // 然后再触发 iterator 的 == 操作符重载
            return !(*this==that);
        }
    private:
        iterator m_it;
    };
    
    // 获取起始常迭代器
    // 这里的const_iterator begin const()和 iterator begin() 构成重载
    // 如果是list类的常对象就调用下面这个begin()
    const_iterator begin() const{
        return iterator(m_head, m_head, m_tail);
    }

    // 获取终止常迭代器
    const_iterator end() const{
        return iterator(m_head, NULL, m_tail);
    }

    node* m_head;   // 链表头
    node* m_tail;   // 链表尾
};

// 查找(利用"=="),放在了全局域. 是因为STL把容器的查找也放在了全局域
// b这个迭代器不参与查找, 返回值是迭代器b说明查找失败
template<class IT, class T>IT find(IT const& a, IT const& b, T const& data){
    for(IT it=a; it!=b; ++it){
        if(*it==data){
            return it;
        }
    }
    return b;
}

// 排序("<")
template <class IT> void sort(IT const& begin, IT const& end){
    IT p = begin;   // 基准
    IT last = end;
    --last; // 排序不包含迭代器end指向的节点
    // 找到p指向的节点在链表中的正确位置
    for(IT i = begin, j = last; i != j;){ // 最后一句写到for循环里也可以
        while (i!=p && *i<*p)
        {
            ++i;
        }
        if(i!=p){
            swap(*i, *p);
            p = i;
        }
        while (j!=p && *p<*j)
        {
            --j;
        }
        if(j!=p){
            swap(*p, *j);
            p = j;
        }
    }
    // 递归
    // 基准节点 前面或者后面 至少还有两个节点才能参与sort
    IT it = begin;
    ++it;
    if(p!=begin && p!= it){
        // 迭代
        sort(begin, p); // p不参与排序
    }
    it = p;
    ++it;
    if(it!=end && it!=last){
        sort(it, end);
    }
}

// 排序("比较器")
template <class IT, class CMP> 
void sort(IT const& begin, IT const& end, CMP cmp){
    IT p = begin;   // 基准
    IT last = end;
    --last; 
    for(IT i = begin, j = last; i != j;){ 
        while (i!=p && cmp(*i, *p)) // cmp 是对象,对象后面加操作符,触发操作符重载
        {
            ++i;
        }
        if(i!=p){
            swap(*i, *p);
            p = i;
        }
        while (j!=p && cmp(*p, *j))
        {
            --j;
        }
        if(j!=p){
            swap(*p, *j);
            p = j;
        }
    }
    // 递归
    // 基准节点 前面或者后面 至少还有两个节点才能参与sort
    IT it = begin;
    ++it;
    if(p!=begin && p!= it){
        // 迭代
        sort(begin, p ,cmp); // p不参与排序
    }
    it = p;
    ++it;
    if(it!=end && it!=last){
        sort(it, end, cmp);
    }
}

// 以上代码模拟容器
// -----------------------
// 以下代码模拟普通用户
void print(string const & word, list<int>& l){
    cout << word << endl;
    typedef list<int>::iterator IT;
    // it就是 正向非常迭代类对象
    for(IT it=l.begin(); it != l.end(); ++it){
        cout <<*it << " ";
    }
    cout << endl << "----------------" << endl;
}

// 比较器类
class CMP{
public:
    bool operator()(int const& a, int const& b){
        return a > b;   // > 降序排序,< 升序排序
    }
};

int main() {
    list<int> ls;
    for(int i=0; i<5; i++){
        ls.push_front(i);
    }
    for(int i=0; i<5; i++){
        ls.push_back(i+10);
    }
    print("添加节点后: ", ls);
    ls.pop_front();
    ls.pop_back();
    print("删除头尾节点以后: ", ls);
    // 利用正向非常迭代器
    ls.insert(++ls.begin(), 1000);              // 增
    print("在迭代器指定的位置添加节点以后: ", ls);
    ls.erase(ls.begin());                       // 删
    print("删除迭代器指向的节点以后: ", ls);
    typedef list<int>::iterator IT;            
    IT it = ls.begin();
    *it  = 800;                                 // 改
    print("更改迭代器指向的节点以后: ", ls);
    
    IT fit = find(ls.begin(), ls.end(), 10);    // 查
    if(fit != ls.end()){
        ls.erase(fit);
    }
    print("找到10并删除后: ", ls);
    sort(ls.begin(), ls.end());                 // 排
    print("升序排序后: ", ls);
    CMP cmp;    // 比较器, 自己决定排序方式
    sort(ls.begin(), ls.end(), cmp);
    print("降序排序后: ", ls);
    
    // 利用正向常迭代器(但用的并不多)
    // 常属性链表,定义好就不能再修改了,这里用拷贝构造赋初值
    const list<int>c_ls(ls);
    typedef list<int>::const_iterator C_IT;
    // // 这里的c_ls.begin()调的也是常属性重载版本
    for(C_IT c_it = c_ls.begin(); c_it != c_ls.end(); ++c_it){
        cout << *c_it << ' ';
    }    
    cout << endl <<"----------------" << endl;
    // C_IT c_it = c_ls.begin();
    // *c_it = 100;  // 编译Error, 不能通过常迭代器修改链表的节点 
    
    return 0;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面向对象程序设计课程作业 1. 请创建一个数据类型为T的链表模板List,实现以下成员函数: 1) 默认构造函数List(),将该链表初始化为一个链表(10分) 2) 拷贝构造函数List(const List& list),根据一个给定的链表构造当前链表(10分) 3) 析构函数~List(),释放链表中的所有节点(10分) 4) Push_back(T e)函数,往链表最末尾插入一个元素为e的节点(10分) 5) operator<<()友元函数,将链表的所有元素按顺序输出(10分) 6) operator=()函数,实现两个链表的赋值操作(10分) 7) operator+()函数,实现两个链表的连接,A=B+C(10分) 2. 请编写main函数,测试该类模板的正确性: 1) 用List模板定义一个List类型的模板类对象int_listB,从键盘读入m个整数,调用Push_back函数将这m个整数依次插入到该链表中;(4分) 2) 用List模板定义一个List类型的模板类对象int_listC,从键盘读入n个整数,调用Push_back函数将这n个整数依次插入到该链表中;(4分) 3) 用List模板定义一个List类型的模板类对象int_listA,调用List的成员函数实现A = B + C;(4分) 4) 用cout直接输出int_listA的所有元素(3分) 5) 用List模板定义List类型的模板类对象double_listA, double_listB, double_listC,重复上述操作。(15分) 3. 输入输出样例: 1) 输入样例 4 12 23 34 45 3 56 67 78 3 1.2 2.3 3.4 4 4.5 5.6 6.7 7.8 2) 输出样例 12 23 34 45 56 67 78 1.2 2.3 3.4 4.5 5.6 6.7 7.8
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值