讨论顺序表

news2024/11/17 10:07:53

讨论顺序表

  • C++中的`vector`
  • 模拟实现
    • 成员变量
    • 尾插数据`push_back`
      • 扩容`reserve`
    • 构造函数和析构函数
    • 拷贝构造函数
    • 指定位置插入数据
    • 指定位置删除数据
    • 迭代器失效
    • 完整代码

C++中,vector是可以改变大小的数组的序列容器。可以看做底层就是一个数组,容量满时扩容。

C++中的vector

  • C++标准库中的vector是个模板类,可以存放不同数据类型的数据。
vector<int> v1;
vector<string> v2;
vector<double> v3;
vector<vector<int>> v4;

模拟实现

成员变量

  • 采用底层是数组模拟实现vector,参考stl_vector源码

  • protected:
      typedef simple_alloc<value_type, Alloc> data_allocator;
      iterator start;
      iterator finish;
      iterator end_of_storage;
    
  • 使用指针指向数组起始位置,结束位置下一个以及容量位置。

template<class T>
class vector {
private:
    iterator _first;
    iterator _finish;
    iterator _end_of_storage;
};

尾插数据push_back

扩容reserve

  • 插入数据,首先还是需要判断容量是否满足条件。_finish == _end_of_storage即容量已满,需要扩容。
  • 防止浅拷贝,就不能拷贝地址空间,需要再次动态申请空间。
void reserve(size_t n) {
    if(n > capacity()) {
        T* tmp = new T[n];
        size_t len = size();

        if(_first) {
            for (size_t i = 0; i < len; ++i) {
                tmp[i] = _first[i];
            }
            delete[] _first;
        }
        _first = tmp;
        _finish = _first + len;
        _end_of_storage = _first + n;
    }
}
void push_back(const T& val) {
    // 判断扩容
    if(_finish == _end_of_storage) {
        reserve(capacity() == 0 ? 5 : 2 * capacity());
    }
    *_finish = val;
    ++_finish;
}

构造函数和析构函数

构造函数

  • 针对初始化n个元素的构造函数,可以复用尾插push_back函数。
vector() {}

vector(size_t n, const T &val = T()) {
    reserve(n);
    for (size_t i = 0; i < n; ++i) {
        push_back(val);
    }
}
  • 析构函数只需要释放_first所指向的空间即可。
~vector() {
    delete[] _first;
    _first = _finish = _end_of_storage = nullptr;
}

拷贝构造函数

  • 同样复用尾插push_back即可。
vector(const vector<T> &v) {
    reserve(v.capacity());
    for (auto &e: v) {
        push_back(e);
    }
}
  • 赋值构造。
vector<T> &operator=(vector<T> tmp) {
    swap(tmp);
    return *this;
}
  • 迭代器构造函数
template<class InputIterator>
vector(InputIterator first, InputIterator last) {
    while(first != last) {
        push_back(*first);
        ++first;
    }
}

指定位置插入数据

  • 指定位置插入数据不仅要判断容量,还需要判断位置是否满足要求,需要在_first_finish之间。
iterator insert(iterator pos, const T &x) {
    assert(pos >= _first);
    assert(pos <= _finish);

    // 判断容量
    if (_finish == _end_of_storage) {
        size_t len = pos - _first;
        reserve(capacity() == 0 ? 5 : 2 * capacity());
        pos = _first + len; // 防止异地扩容时,pos位置失效
    }

    iterator end = _finish - 1;
    while (end >= pos) {
        *(end + 1) = *end;
        --end;
    }
    *pos = x;
    ++_finish;
    return pos;
}

指定位置删除数据

iterator erase(iterator pos) {
    assert(pos >= _first);
    assert(pos < _finish);

    iterator start = pos;
    while (start < _finish - 1) {
        *start = *(start + 1);
        ++start;
    }
    --_finish;
    return pos;
}

迭代器失效

  • 在特定位置插入或者删除元素时,都有可能造成迭代器失效。
  • 如在插入数据时,如果不对pos位置做检查,异地扩容后还在原有pos位置插入数据,则数据无效,因此需要重新检查pos位置是否有效。
  • 而在删除元素时,如果循环删除特定数据,随着元素的移动,迭代器也会失效导致结果不正确,因此在编写指定位置删除数据的代码时,设定返回删除数据下一个位置的迭代器,来解决迭代器失效问题。
max::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(9);
v.push_back(6);

auto it = v.begin();
while(it != v.end()) {
    if(*it % 2 == 0) {
        it = v.erase(it);
    } else {
        ++it;
    }
}

for(int n : v) {
    cout << n << " ";
}
cout << endl;

删除数据结果

完整代码

namespace max {
    template<class T>
    class vector {
    public:
        typedef T *iterator;
        typedef const T *const_iterator;

        vector() {}

        vector(size_t n, const T &val = T()) {
            reserve(n);
            for (size_t i = 0; i < n; ++i) {
                push_back(val);
            }
        }

        vector(int n, const T &val = T()) {
            reserve(n);
            for (int i = 0; i < n; ++i) {
                push_back(val);
            }
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last) {
            while(first != last) {
                push_back(*first);
                ++first;
            }
        }

        void swap(vector<T> &v) {
            std::swap(_first, v._first);
            std::swap(_finish, v._finish);
            std::swap(_end_of_storage, v._end_of_storage);
        }

        vector(const vector<T> &v) {
            reserve(v.capacity());
            for (auto &e: v) {
                push_back(e);
            }
        }

        vector<T> &operator=(vector<T> tmp) {
            swap(tmp);
            return *this;
        }

        ~vector() {
            delete[] _first;
            _first = _finish = _end_of_storage = nullptr;
        }

        iterator begin() {
            return _first;
        }

        iterator end() {
            return _finish;
        }

        const_iterator begin() const {
            return _first;
        }

        const_iterator end() const {
            return _finish;
        }

        void resize(size_t n, const T &val = T()) {
            if(n <= size()) {
                _finish = _first - n;
            } else {
                reserve(n);
                while(_finish < _first + n) {
                    *_finish = val;
                    ++_finish;
                }
            }
        }

        void reserve(size_t n) {
            if (n > capacity()) {
                T *tmp = new T[n];
                size_t len = size();

                if (_first) {
                    for (size_t i = 0; i < len; ++i) {
                        tmp[i] = _first[i];
                    }
                    delete[] _first;
                }
                _first = tmp;
                _finish = _first + len;
                _end_of_storage = _first + n;
            }
        }

        void push_back(const T &val) {
            // 判断扩容
            if (_finish == _end_of_storage) {
                reserve(capacity() == 0 ? 5 : 2 * capacity());
            }
            *_finish = val;
            ++_finish;
        }

        void pop_back() {
            assert(size() >= 0);
            --_finish;
        }

        iterator insert(iterator pos, const T &x) {
            assert(pos >= _first);
            assert(pos <= _finish);

            // 判断容量
            if (_finish == _end_of_storage) {
                size_t len = pos - _first;
                reserve(capacity() == 0 ? 5 : 2 * capacity());
                pos = _first + len; // 防止异地扩容时,pos位置失效
            }

            iterator end = _finish - 1;
            while (end >= pos) {
                *(end + 1) = *end;
                --end;
            }
            *pos = x;
            ++_finish;
            return pos;
        }

        iterator erase(iterator pos) {
            assert(pos >= _first);
            assert(pos < _finish);

            iterator start = pos;
            while (start < _finish - 1) {
                *start = *(start + 1);
                ++start;
            }
            --_finish;
            return pos;
        }

        T &operator[](size_t pos) {
            return _first[pos];
        }

        const T &operator[](size_t pos) const {
            return _first[pos];
        }

        size_t size() const {
            return _finish - _first;
        }

        size_t capacity() const {
            return _end_of_storage - _first;
        }

    private:
        iterator _first = nullptr;
        iterator _finish = nullptr;
        iterator _end_of_storage = nullptr;
    };
}

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

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

相关文章

nacos的创建

nacos压缩包 链接&#xff1a;https://pan.baidu.com/s/1AYVKZvosDkcMMbTIB48Iew?pwd1234 提取码&#xff1a;1234 首先将下载好的nacos压缩包上传到linux环境中&#xff0c;然后解压缩&#xff08;解压缩命令&#xff09; tar -zxvf nacos-server-2.3.0.tar.gz解压成功后如…

【五】【QT开发应用】C++中lambda表达式,值捕获,引用捕获,隐式捕获,lambda表达式的返回类型

Lambda表达式 复盘 Lambda表达式 Lambda 表达式是 C11 引入的一种特性&#xff0c;用于定义匿名函数。它使得可以在代码中方便地定义和使用小段函数&#xff0c;而无需专门定义一个命名的函数。这在需要传递函数作为参数或者需要定义内联函数时非常有用。 基本语法 基本语法…

Python-矩阵元素定位

[题目描述] 小理得到了一个 n 行 m 列的矩阵&#xff0c;现在他想知道第 x 行第 y 列的值是多少&#xff0c;请你帮助他完成这个任务。输入格式&#xff1a; 第一行包含两个数 n 和m &#xff0c;表示这个矩阵包含 n行 m 列。从第 2 行到第 n1 行&#xff0c;每行输入 m 个整数…

告别繁琐代码,迈向编程新境界—Java集合与泛型全面解析

在Java编程的征途中&#xff0c;集合&#xff08;Collection&#xff09;与泛型&#xff08;Generics&#xff09;是两大里程碑式的特性&#xff0c;它们不仅极大地提升了代码的灵活性和安全性&#xff0c;还帮助开发者简化了数据结构的处理逻辑&#xff0c;让编程之旅变得更加…

Appium APP测试学习

1、安装client编程库(客户端) (1)如果遇到以下问题可以使用全路径安装 (2)安装后导致selenium升级&#xff0c;导致某些方法失效&#xff1a;如find_element_by_id。解决方法&#xff1a;卸载两个安装包&#xff0c;后面重新安装 2、安装appium Server:&#xff08;服务端&…

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中&#xff0c;少不了使用word&#xff0c;这个是大家必备的软件&#xff0c;今天给大家分享word设置上标快捷键&#xff0c;希望在办公中能帮到您&#xff01; 1、添加上标 在录入一些公式&#xff0c;或者是化学产品时&#xff0c;需要添加上标内容…

笔记:记录状态并判重的方法

题目&#xff08;八数码问题&#xff09; 编号为1-8的8个正方形滑块被摆成3行3列&#xff08;有一个格子留空&#xff09;&#xff0c;如下图所示 81573642 每次可以把与空格相邻的滑块&#xff08;有公共边才算相邻&#xff09;一道空格中&#xff0c;而它原来的位置就成为…

Vue3.3 的 defineOptions 的使用,方便在 setup 语法糖中为组件命名和控制父子属性透传,包含在线运行实例欧

defineOptions 是 Vue3.3 的新的宏&#xff0c;可以通过 defineOptions 宏在 <script setup> 中使用选项式 API&#xff0c;也就是说可以在一个宏函数中设置 name, props, emits, render, 控制是否允许父子非 props 的属性透传等功能。 defineOptions 可以直接在 setup …

新能源商业浪潮来袭:企业巨轮扬帆商海,共绘绿色未来蓝图

随着全球气候变化问题日益严峻&#xff0c;新能源产业正成为推动经济发展的新引擎。 在这场新能源商业浪潮中&#xff0c;众多企业如同巨轮一般&#xff0c;扬帆商海&#xff0c;共同绘制绿色未来的宏伟蓝图。 新能源产业的崛起&#xff0c;不仅为经济发展注入了新的活力&…

linux下进度条的实现

一、代码一版 使用模块化编程 1.processbar.h #include<stdio.h> #define capacity 101 //常量使用宏定义 #define style //符号方便后续修改 extern void processbar();修饰变量的时候一定要带extern&#xff0c;修饰函数的时候可以省略&#xff0c;因为没有函数体就…

将Windows11右键菜单修改为Windows10风格

Windows11更新后&#xff0c;右键菜单很多功能隐藏起来了&#xff0c;使用时需要点击“显示更多选型”才能获取完整功能。为了能获得Windows10右键菜单丝滑的体验&#xff0c;我总结了以下方法。 方法一&#xff1a;控制台操作法 按住winR打开运行窗口 输入cmd&#xff0c;…

护眼灯和普通台灯有什么区别?劣质护眼台灯宣传的三大套路

护眼灯和普通台灯有什么区别&#xff1f;围绕这一问题的讨论颇多。然而&#xff0c;真正体验过护眼台灯的人会深知&#xff0c;它与普通台灯之间的差异远非一般&#xff0c;涉及照明效果、色温调节、蓝光控制、闪烁问题及功能性设计等诸多层面。为了让更多人透彻理解这两者之间…

【代码阅读】SSC:Semantic Scan Context for Large-Scale Place Recognition

一、主函数 官方开源的代码提供了四个主函数&#xff0c;其中eval_pair.cpp和eval_top1.cpp是一组&#xff0c;分别用于计算两帧的相似度分数以及一帧点云在所有的51帧点云中相似度最高的25帧的相似度分数。eval_seq.cpp是在eval_top1.cpp的基础上&#xff0c;给了一堆序列&am…

视频格式转换方法:如何使用视频转换器软件转换视频

众所周知&#xff0c;目前存在许多不同的视频和音频格式。但我们的媒体播放器、移动设备、PC 程序等仅兼容少数特定格式。例如&#xff0c;如果不先将其转换为 MP4、MOV 或 M4V 文件&#xff0c;AVI、WMV 或 MKV 文件就无法在 iPhone 上播放。 视频转换器允许您将一种视频格式…

[经验] candy是什么意思英语翻译 #笔记#其他#职场发展

candy是什么意思英语翻译 1、candy的意思 Candy是英语中的一个词汇&#xff0c;意思是糖果、糖果制品。Candy意为果脯的意思也不是很常见。 糖果是一种富含糖分的食品&#xff0c;主要由砂糖、粘合剂和食用色素等组成。糖果的种类可以很多&#xff0c;有硬糖、软糖、巧克力、…

OpenAI CTO米拉·穆拉提谈未来:AI一年半后达到博士水平

人工智能&#xff08;AI&#xff09;领域近年来的发展迅猛&#xff0c;特别是在大语言模型&#xff08;LLM&#xff09;的进步上。最近&#xff0c;OpenAI的首席技术官&#xff08;CTO&#xff09;米拉穆拉提&#xff08;Mira Murati&#xff09;在达特茅斯学院的一次采访中&am…

捷瑞数字业绩波动性明显:关联交易不低,募资必要性遭质疑

《港湾商业观察》施子夫 5月22日&#xff0c;山东捷瑞数字科技股份有限公司&#xff08;以下简称&#xff0c;捷瑞数字&#xff09;及保荐机构国新证券披露第三轮问询的回复&#xff0c;继续推进北交所上市进程。 从2023年6月递表开始&#xff0c;监管层已下发三轮审核问询函…

QT实现人脸识别

QT实现人脸识别 Face.pro文件&#xff1a; QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c11# The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # d…

我在高职教STM32——LCD液晶显示(4)

大家好&#xff0c;我是老耿&#xff0c;高职青椒一枚&#xff0c;一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次&#xff0c;同行应该都懂的&#xff0c;老师在课堂上教学几乎是没什么成就感的。正因如此&#xff0c;才有了借助 CSDN 平台寻求认同感和成就…

AIGC发展方向和前景

引言 背景介绍 AIGC的定义及其发展历程 AIGC&#xff0c;即人工智能生成内容&#xff0c;是近年来在人工智能领域兴起的一项重要技术。它通过使用机器学习和深度学习等技术&#xff0c;使得计算机能够自动生成各种形式的数字内容&#xff0c;如文本、图像、音频和视频等。 …