数据结构(一) --vector的简单实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TailorWu/article/details/80007756

向量vector


#ifndef MYVECTOR_H
#define MYVECTOR_H

typedef int Rank; //秩
#define DEFAULT_CAPACITY  3 //默认的初始容量

template <typename T>   //向量魔板类
class myVector{
protected:
    Rank _size;   //规模
    int _capactity; //容量
    T* _elem;   //数据区

    void copyFrom(const T* A, Rank lo, Rank hi);  //复制数组区间A
    void expand();  //空间不足时扩容
    void shrink();  //装填因子过小时压缩

    bool bubble(Rank lo, Rank hi);   //扫描交换
    void bubbleSort(Rank lo, Rank hi);  //冒泡排序算法
    Rank max(Rank lo, Rank hi);   //选取最大元素
    void selectionSort(Rank lo, Rank hi);   //选择排序算法
    void merge(Rank lo, Rank mi, Rank hi);  //归并排序
    void mergeSort(Rank lo, Rank hi);   //归并排序算法
    Rank partition(Rank lo, Rank hi);  //轴点构造算法
    void quickSort(Rank lo, Rank hi);  //快速排序算法
    void heapSort(Rank lo, Rank hi);   //堆排序算法

public:
    //构造函数
    myVector(int c = DEFAULT_CAPACITY, int s = 0, T v = 0){  //容量为c,规模为s,所有元素初始化为v
        _elem = new T[_capactity = c];
        for (_size = 0;  _size< s; ++_size)
        {
            _elem[_size] = v;
        }
    }
    myVector(const T* A, Rank lo, Rank hi){
        comyFrom(A, lo, hi);
    }
    myVector(T const* A, Rank n) { copyFrom(A, 0, n); } //数组整体复制
    myVector(myVector<T> const& V, Rank lo, Rank hi) { copyFrom(V._elem, lo, hi); } //向量区间复制
    myVector(myVector<T> const& V) { copyFrom(V._elem, 0, V._size); } //向量整体复制

    // 析构函数
    ~myVector() { delete[] _elem; } //释放内部空间
    // 只读访问接口
    Rank size() const { return _size; } //觃模
    bool empty() const { return !_size; } //刞空
    int disordered() const; //刞断向量是否已排序
    Rank find(T const& e) const { return find(e, 0, _size); } //无序向量整体查找
    Rank find(T const& e, Rank lo, Rank hi) const; //无序向量区间查找
    Rank search(T const& e) const //有序向量整体查找
    { return (0 >= _size) ? -1 : search(e, 0, _size); }
    Rank search(T const& e, Rank lo, Rank hi) const; //有序向量区间查找
    // 可写访问接口
    T& operator[](Rank r) const; //重载下标操作符,可以类似于数组形式引用各元素
    myVector<T> & operator=(myVector<T> const&); //重载赋值操作符,以便直接克隆向量
    T remove(Rank r); //删除秩为r的元素
    int remove(Rank lo, Rank hi); //删除秩在区间[lo, hi)之内的元素
    Rank insert(Rank r, T const& e); //插入元素
    Rank insert(T const& e) { return insert(_size, e); } //默认作为末元素插入
    void sort(Rank lo, Rank hi); //对[lo, hi)排序
    void sort() { sort(0, _size); } //整体排序
    void unsort(Rank lo, Rank hi); //对[lo, hi)置乱
    void unsort() { unsort(0, _size); } //整体置乱
    int deduplicate(); //无序去重
    int uniquify(); //有序去重
    // 遍历
    void traverse(void(*)(T&)); //遍历(使用函数指针,只读或局部性修改)
    template <typename VST> void traverse(VST&); //遍历(使用函数对象,可全局性修改)
}; //myVector
#endif

基于赋值的构造方法

  • copyFrom()根据待复制区间的边界,换算出新向量的初试规模,再以双倍的容量,为内部数组_elem[]申请空间
  • 忽略开辟新空间所需的时间,运行时间应正比于区间宽度,即 O(hi - lo) = O(size)
template <typename T>
void myVector<T>::copyFrom(const T *A, Rank lo, Rank hi) {  //以数组区间A[lo,hi)为蓝本复制向量
    _elem = new T[_capactity = 2 * (hi-lo)];   //分配空间
    _size = 0;     //规模清零
    while(lo<hi)    //逐一复制
        _elem[_size++] = A[lo++];   //复制至_elem[0,hi-lo)
}

重载向量赋值操作符

  • 防止默认的赋值运算符不足以支持向量之间的直接赋值
template<typename T>
myVector<T>& myVector<T>::operator=(const myVector<T> & V) {
    if(_elem)
        delete [] _elem;    //释放原有内容
    copyFrom(V._elem,0,V.size());  //整体复制
    return *this;    //返回当前对象的引用,以便链式赋值
}

动态空间管理

  • 向量实际规模与其内部数组容量的比值(即 _size/_capac)称为装填因子(load factor),他是衡量空间利用率的重要指标
  • 扩充向量 :当原数组空间不足时,申请一个容量更大的数组B[ ],并将原数组中的元素全部赋值到新的空间,释放原数组所占的空间
  • 一般调用insert()之前都要先调用expand(),检查容量是否能够继续操作
template <typename T>
void myVector<T>::expand() {  //向量空间不足时扩容
    if (_size < _capactity) return ; //空间充足
    if (_capactity < DEFAULT_CAPACITY) _capactity = DEFAULT_CAPACITY;  //不低于最小容量
    T* oldElem = _elem;
    _elem = new T[_capactity <<= 1];  //容量加倍
    for (int i = 0; i <_size ; ++i) {
        _elem = oldElem[i];   //T为基本类型,或已重载赋值操作符'='
    }
    delete [] oldElem;   //释放原空间
}
  • 缩容
template <typename T>
void myVector<T>::shrink() {   //装填因子过小时压缩向量所占空间
    if (_capactity < DEFAULT_CAPACITY << 1) return;
    if (_size << 2 > _capactity) return ;  //25%为界
    T* oldElem = _elem;
    _elem = new T[_capactity >>= 1];  //容量减半
    for (int i = 0; i < _size; ++i) {  //复制原向量的内容
        _elem[i] = oldElem[i];
    }
    delete [] oldElem;  //释放原空间
}

常规使用向量


  • 直接引用元素
    与数组直接通过下标访问元素的方式相比,使用重载操作符”[ ]”更加自然
template <typename T>
T& myVector<T>::operator [] (myVector::Rank r) const {  //重载下标操作符
    return _elem[r];   //assert: 0 <= r < _size
}
  • 置换器
    置乱算法:从待置乱区间的末元素开始,逆序向前逐一处理各元素.
template <typename T>
void myVector<T>::unsort(myVector<T>::Rank lo, myVector<T>::Rank hi) {  
    T* V = _elem +lo;     //将子向量_elem[lo,hi)视作另一向量V[0,hi-lo]
    for (Rank i = hi-lo; i > 0 ; --i) {  //自后向前
        swap(V[i-1],V[rand() % i]);  //V[i-1][0,i)中某一元素随机交换
    }
}
  • 顺序查找(从后往前)
    时间复杂度为O(n),最好情况是O(1)
template <typename T>   //无序向量的顺序查找:返回最后一个元素e的位置;失败返回lo-1
Rank myVector<T>::find(const T &e, myVector<T>::Rank lo, myVector<T>::Rank hi) const {  //assert: 0<=lo < hi <= _size
    while((lo < hi--) && (e != _elem[hi]));   //从后向前,顺序查找
    return hi;    //若hi < lo,则意味着失败;否则hi即命中元素的秩
}
  • 插入
    平均时间复杂度O(n),总体时间复杂度O(_size - r -1)
template <typename T>  //将e作为秩为r元素插入
Rank myVector<T>::insert(myVector<T>::Rank r, const T &e) {  //assert:0 <=r <= _size
    expand();
    for (int i = _size; i > r ; --i) {
        _elem[i] = _elem[i-1];    //自后向前,后继元素顺次后移一个单元
    }
    _elem[r] = e;
    _size++;
    return r;   //返回秩
}
  • 删除
    有两个重载;讲单元素删除视作区间删除的特例,基于后者来实现前者
    时间复杂度:O(m+1) ,m为_size-hi+1;
    删除单个元素的时间复杂度O(_size-r),位置越靠后,所需时间越短
template <typename T>  //删除区间[lo,hi]
int myVector<T>::remove(myVector<T>::Rank lo, myVector::Rank hi) {
    if(lo == hi) return 0;  //出于效率考虑,单独处理退化情况,比如remove(0,0)
    while (hi < _size)  //[hi,_size)顺次前移hi -lo 个单元
        _elem[lo++] = _elem[hi++];
    _size = lo;   //更新规模
    shrink();
    return hi -lo;
}
template <typename T> //删除向量中秩为r的元素,0 <= r < _size
T myVector<T>::remove(myVector<T>::Rank r) {  
    T e = _elem[r];
    remove(r,r+1); //调用区间删除算法,等效于对区间[r,r+1)的删除
    return e;
}
  • 唯一化
    剔除向量中的重复元素
    时间复杂度:O(n²)
template <typename T>  //删除无序向量中重复元素
int myVector<T>::deduplicate() {
    int oldSize = _size;
    Rank i = 1;  //从_elem[1]开始
    while (i < _size) //自前向后逐一考查各元素_elem[i]
        (find(_elem[i],0,i) < 0) ? i++ : remove(i);  //在其前缀总寻找与之相同者,如无相同继续考查其后继,如有则删除相同者
    return oldSize - _size;
}
  • 遍历
template <typename T>  //利用函数指针的机制遍历
void myVector<T>::traverse(void (*visit)(T &)) {
    for (int i = 0; i < _size; ++i) {
        visit(_elem[i]);
    }
}

template <typename T>
template <typename VST>
void myVector<T>::traverse(VST& visit) {
    for (int i = 0; i < _size; ++i) {
        visit(_elem[i]);
    }
}

有序向量

  • 有序性甄别
template <typename T>  //返回向量中逆序相邻元素的总数
int myVector<T>::disordered() const {
    int n = 0;
    for (int i = 0; i < _size; ++i) {
        if(_elem[i-1] > _elem[i])   //逐一检查_size -1对相邻元素
            ++n;
    }
    return n;   //如果n为0即为有序
}
  • 唯一化
    针对有序向量进行,时间复杂度为O(n),基本是最优解
template <typename T> //有序向量重复元素剔除(高效)
int myVector<T>::uniquify() {
    Rank i = 0,j = 0; //各对互异"相邻"元素的秩
    while(++j < _size)  //逐一扫描
        if (_elem[i] != _elem[j])  //跳过相同者
            _elem[++i] = _elem[j];  //发现不同元素时,向前移至紧邻前者右侧
    _size = ++i;  //直接截除尾部多余元素
    shrink();
    return j - i;  //向量规模变化量,被删除元素总数
}
  • 查找
    当有序向量中元素互异时,秩r即是S中小于S[r]的元素项目
    一般情况时,若小于,等于S[r]的元素各有i,k个,则该元素及其雷同元素应集中分布于S[i,i + k)
template <typename T> //在有序向量的区间[lo,hi)内,确定不大于e的最后一个节点的秩
Rank myVector<T>::search(const T &e, int lo, int hi) const {  //assert: 0 <= lo < hi <= _size
    //按各50%的概率随机使用  二分查找或Fibonacci查找
    return (rand() % 2)? binSearch(_elem,e,lo,hi) : fibSearch(_elem,e,lo,hi);
}
  • 二分查找

时间复杂度为O(log(n))

/       /二分查找(版本A):在有序向量的区间[lo,hi)内查找元素e, 0 <= lo <= hi <= _size
        static Rank binSerach(T* A,const T& e,Rank lo,Rank hi){
            while(lo < hi){
                Rank mi = (lo + hi) >> 1;  //以中点为轴点
                if (e < A[mi])
                    hi = mi;   //去前半段继续查找
                else if(A[mi] < e)
                    lo = mi + 1;  //去后半段继续查找
                else
                    return mi;  //命中
            }
            return -1;  //查找失败
       //有多个命中元素时,不能保证返回秩最大/最小者;
       //查找失败时简单返回-1,不能指示最后失败的位置
        }

来自于<<数据结构–C++语言描述>> 邓俊辉 第二章

猜你喜欢

转载自blog.csdn.net/TailorWu/article/details/80007756