数据结构之 Array/Vector

数据结构之 Array/Vector


写在前面

数据结构是数据项的结构化集合,其结构性表现为数据项之间的相互联系以及作用,可以理解为定义于数据项之间的
某种逻辑次序。

数据结构(按数据项间的逻辑次序划分)

  • 线性结构
    • 线性表\序列(Sequence)
    • 栈(stack)
    • 队列(queue)
  • 半线性结构
    • 树(Tree)
  • 非线性结构
    • 图(graph)

线性表/序列(按存储结构划分)

  • 顺序表/向量
    在这里把顺序表和向量放在一起了,其本质都是数组,但是在一般数据结构课中,更多地,我们称为顺序表。进一步地,在C++的STL中,数组被封装为成为适用更加广泛的Vector了,通过模板参数制定数据元素的类型,还有大量的ADT接口使用起来特别方便。当然二者还是有一些区别的,在下面讲解操作时会具体说。
    但本质上,二者的逻辑操作与实现是一致的,因此放在一起写。
  • 链表/列表
    同上,一般数据结构课上就直接讲链表,但在STL中封装为List了。

顺序表/向量

  • 通常,程序语言都会把数组作为一种内置的数据类型,支持其对一组相关元素的存储组织与访问操作。
  • 具体来讲,一个集合S有n个元素,它们集中地存放于起始地址为A、物理地址连续地一段存储空间,并统称为数组(array)。通常以A作为该数组的标识。并且,用A[i]表示这一段连续存储空间中的第i+1个元素,值得注意的是,计算机中的i通常从0开始计数,而日常人们计数则从1开始。
    即 A = { a0, a1, a2, ……, a(n-1) }
  • 由此可以看出,A中任意一个元素 ai 都有唯一的 i 可以对其进行直接访问,元素A[i]对应的物理地址为$ A + i * s $,其中单个元素占 s s 个单位空间。
  • 对于任何 0 i < j < n 0 \le i \lt j \lt n , A[i]都是A[j]的前驱,特别地,当 i = j 1 i=j-1 时,A[i]是[j]的直接前驱,A[j]的所有前驱称为其前缀;相应地,A[j]都是A[i]的后继,特别地,当 i + 1 = j i+1=j 时,A[j]是A[i]的直接后继,A[i]的所有后继称为其后缀。

基本操作(ADT接口)

Vector()

  • 构造函数,其实现方式最多样,具体可参见下面的代码。
  • 这是一个重点。array的初始化一般采用固定长度的方式,也就是说,其大小是在赋值之前就确定的,而Vector模板类采用动态分配空间方式,因此稍显灵活。但当其规模频繁变化时,二者都需要动态调整,不仅费时间,而且还会遇到没有这么大规模的连续空间的问题。所以,在需要规模频繁变化时,一般不采用顺序表,而采用链表/列表。

size()

  • 返回顺序表/向量的规模
  • 值得注意的是,在insert或者remove这样的会引起元素数量变化的时候,要及时地对size进行更新,这样才会保证每次调用此接口不用遍历一遍计数,而是直接返回size变量的值时间复杂度为O(1)

get( r )

  • 返回秩(rank,也就是通常所讲的下标)为r的元素
  • 时间复杂度O(1),充分体现了顺序表可以随机访问的特征
  • 这里需要注意的是,虽然C++ STL中重载了[ ],也就是说vector仍然可以使用vec[i]的方式访问某个元素,但实际上作为一个封装的抽象类,使用get( r )的方式更能体现其与底层无关的特点。

insert(r, e)

  • 将元素e插到秩为r的位置
  • 时间复杂度O(n),在插入之前,需将秩为n-1到r的所有元素依次向后移动一个位置,空出A[r]。r可为0, 1, 2……, n,则每次需要移动n, n-1, n-2……, 0次,平均移动 n / 2 n/2 次,即时间复杂度为O(n)

remove( r )

  • 删除秩为r的元素
  • 和插入一样,删除后需将后面的元素向前移动一个位置,时间复杂度为O(n)

find(e)

  • 查找值等于e且秩最大的元素
  • 当有多个值为e的元素时,返回秩最大的(至于原因,在实现排序时就能理解了),则可以从后往前依次查找,时间复杂度O(n)

traverse()

  • 遍历所有元素
  • 显然,当访问每个元素只进行常数复杂度的操作时,此接口的时间复杂度为O(n)

以下是根据邓公学习时自己仿写的,只给出头文件,部分简单的接口已经实现了,剩下的将会在结合具体算法讲的时候在贴出来。虽然顺序表很简单,但是越简单的东西可以变化的招式就越多,自己写完之后才能体会哪里是容易栽跟头的地方。
另外,同一种接口有多种实现方式,因此,可结合复杂度分析,判断优劣。

#ifndef VECTOR_H
#define VECTOR_H
// Vector template

typedef int Rank;  //秩
#define DEFAULT_CAPACITY 3  //默认的初始容量(实际应用中可设置为更大)

template <typename T>
class Vector {  //向量模板类
   protected:
    Rank _size;
    int _capacity;
    T* _elem;                                     //规模、容量、数据区
    void copyFrom(T const* A, Rank lo, Rank hi);  //复制数组区间A[lo, hi)
    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:
    // 构造函数
    Vector(int c = DEFAULT_CAPACITY, int s = 0, T v = 0){  //容量为c、规模为s、所有元素初始为v
        _elem = new T[_capacity = c];
        for (_size = 0; _size < s; _elem[_size++] = v);
    }                                                  // s<=c
    Vector(T const* A, Rank n) { copyFrom(A, 0, n); }  //数组整体复制
    Vector(T const* A, Rank lo, Rank hi) { copyFrom(A, lo, hi); }  //区间
    Vector(Vector<T> const& V) {
        copyFrom(V._elem, 0, V._size);
    }  //向量整体复制
    Vector(Vector<T> const& V, Rank lo, Rank hi) {
        copyFrom(V._elem, lo, hi);
    }  //区间
    // 析构函数
    ~Vector() {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;  //重载下标操作符,可以类似于数组形式引用各元素
    Vector<T>& operator=(Vector<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&);  //遍历(使用函数对象,可全局性修改)
};                        // Vector

#endif
发布了12 篇原创文章 · 获赞 2 · 访问量 3201

猜你喜欢

转载自blog.csdn.net/wq_151/article/details/102995407
今日推荐