C++线性表之数组实现

线性表-数组描述

1. 线性表定义

1.1 文字描述

1.2 抽象数据结构(ADT)-linearList -- 数据结构三要素(逻辑结构、物理结构和数据的运算)

抽象数据类型 linearList{
实例:
  有限个元素的有序集合; // 一般使用数组
操作:
  empty();// 若表空,返回true,否则返回false
  size(); // 返回线性表的大小(表中元素的个数)
  get(index); // 返回线性表中索引为index的元素
  indexOf(x); // 返回线性表中第一次出现x的索引。若x不存在,则返回-1
  erase(index); // 删除索引为index的元素,索引大于index的元素其索引-1
  insert(index,x); // 把x插入线性表中索引为index的位置上,索引大于等于index的元素其索引+1
  output();// 输出表中元素
}

2. 抽象类 - linearList(ADT的C++代码描述)

C++使用抽象类来描述线性表的ADT

class linearList{
public:
    // 析构函数都是虚函数
    virtual ~linearList(){};
    // 不修改线性表的方法声明为常函数
    virtual bool empty() const = 0;
    virtual int size() const = 0;
    virtual T& get(int index) const = 0;
    // 形参要加const,保护数据不被修改
    virtual int indexOf(const T& theElement) = 0;
    virtual T earse(int theIndex) = 0;
    virtual T insert(int index,const T& theElement) = 0;
    // 把线性表插入输出流out中
    virtual void output(ostream& out) = 0;
    // 末尾追加元素进线性表
    virtual void empty_push(const T& theElement) = 0;
};

3. 线性表的数组实现 -- 自定义

1.1 定义地址映射函数 --- 这个思想必须有,地址都是我们定义映射函数得到的

1.描述:

线性表中第i个元素存储在数组的哪个位置?

2.地址映射公式

location(i) = i;     ---  我们线性表使用这个地址映射公式
// 第i个线性表元素放到数组的第i个下标上
location(i) = arrayLength-i-1;   --- 这个线性表也可以使用,但是一般不用// 从数组的右端一次存储线性表左边->右边的元素
location(i) = (location(0)+t)%arrayLength;   --- 适合循环队列的地址映射函数
// 第1个线性表的元素放到数组的第t个位置,如果i+t>=arrayLength,则之后的线性表元素从0开始插入
// 1->t
// 2->t+1
// ...i+t>=arrayLenth
// i->0
// i+1->1
// ... 插入到t-1位置后,数组就满了

2.2 c++中迭代器iterator的结构

template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
           typename _Pointer = _Tp*, typename _Reference = _Tp&>
    struct iterator
    {
      /// One of the @link iterator_tags tag types@endlink.
      typedef _Category  iterator_category;
      /// The type "pointed to" by the iterator.
      typedef _Tp        value_type;
      /// Distance between iterators is represented as this type.
      typedef _Distance  difference_type;
      /// This type represents a pointer-to-value_type.
      typedef _Pointer   pointer;
      /// This type represents a reference-to-value_type.
      typedef _Reference reference;
    };

C++迭代器

为了简化迭代器的开发和基于迭代器的通用算法的分类,C++的STL定义了5种迭代器,输入、输出、向前、双向和随机访问。

所有的迭代器都具备操作符==,!=,*的重载。

2.3 arrayList的类的定义 -- 带迭代器实现

template<class T>
class arrayList:public linearList<T>{
public:
    arrayList(int inintialCapacity = 10);
    // 复制构造函数
    arrayList(const arrayList<T>&);
    ~arrayList(){delete []element;cout<<"arrayList destructor";}

    // ADT方法
    bool empty() const {return listSize == 0;}
    int size() const {return listSize;}
    T& get(int index) const ;
    int indexOf(const T &theElement) const ;
    void erase(int index);
    void insert(int theIndex,const T& theElement);
    void output(ostream&);
    void empty_push(const T &theElement) override;

    // 定义访问元素的迭代器   -- 迭代器实现,读者要重点看一下
    // 核心:*重载,构造函数定义
    class iterator{
    public:
        // 用c++的typedef语句实现双向迭代器
        typedef bidirectional_iterator_tag iterator_category;
        typedef T value_type;
        typedef ptrdiff_t difference_type;
        typedef T* pointer;
        typedef T& reference;

        // 构造函数
        iterator(T* thePosition = 0){position = thePosition;};
        // 迭代器必须有的重载==,!=,*
        // 解引用操作符    --- 迭代器访问数组元素,靠的就是"*"运算符的重载
        T& operator*()const {return *position;}
        T* operator->()const {return &*position;}
        //==,!=运算符重载
        bool operator==(const iterator right) const {return position == right.position;}
        bool operator!=(const iterator right) const {return position != right.position;}
        //迭代器可选重载
        //++,--运算符重载
        iterator& operator++(){
            position++;
            return *this;
        }
        iterator& operator++(int){
            iterator it = *this;
            position++;
            return it;
        }
        iterator& operator--(){
            --position;
            return *this;
        }
        iterator& operator--(int){
            iterator it = *this;
            --position;
            return it;
        }
    protected:
        // 指向表中元素的指针
        T* position;
    };
    iterator begin() const{return iterator(element);}
    iterator end() const {return iterator(element+listSize);}

    // 友元函数重载<<,输出线性表
    friend ostream& operator<<(ostream& a,const arrayList<T>& t);
protected:
    // 存储线性元素的一维动态数组
    T* element;
    // 一维数组的长度
    int arrayLenth;
    // 线性表元素的个数
    int listSize;
    // 检查下标
    void checkIndex(int theIndex) const ;
    // 扩容函数
    void changeLength(T*& a,int oldLength,int newLength);
};

template<class T>
arrayList<T>::arrayList(int inintialCapacity) {
    if (inintialCapacity < 1){
        ostringstream s;
        s<<"Initial capacity = "<<inintialCapacity<<"Must be > 0";
        throw "illegalParameterValue"+s.str();
    }
    arrayLenth = inintialCapacity;
    listSize = 0;
    element = new T[arrayLenth];
}

template<class T>
arrayList<T>::arrayList(const arrayList<T> & theList) {
    listSize = theList.listSize;
    arrayLenth = theList.arrayLenth;
    element = new T[arrayLenth];
    // 把theList.element中元素复制给element
    copy(theList.element,theList.element + listSize,element);
}

template<class T>
void arrayList<T>::checkIndex(int theIndex) const {
    // 检查下标是否合法
    if (theIndex <0 || theIndex >= arrayLenth){
        ostringstream s;
        s<<"index = "<<theIndex<<" size = "<<listSize;
        throw "illegaParameter"+s.str();
    }
}

template<class T>
void arrayList<T>::changeLength(T *&a, int oldLength, int newLength) {
    if (newLength < 0)
        throw "illegal newLength";
    T* temp = new T[newLength];
    int number = min(oldLength,newLength);
    copy(a,a+number,temp);
    delete [] a;
    a = temp;
}

template<class T>
T& arrayList<T>::get(int index) const {
    // 元素不存在则抛出异常
    checkIndex(index);
    return element[index];
}

template<class T>
int arrayList<T>::indexOf(const T &theElement) const {
    // 返回元素第一次出现的索引,没找到返回-1
    int index = -1;
    for (int i = 0; i < listSize; ++i) {
        if (element[i] == theElement)
            return i;
    }
    return index;
}

template<class T>
void arrayList<T>::erase(int index) {
    // 检查下标是否合法
    checkIndex(index);
    // index后面的元素向前移动一个单位
    copy(element+index+1,element+listSize,element+index);
    // 调用析构函数
    element[--listSize].~T();
}


template<class T>
void arrayList<T>::insert(int theIndex, const T &theElement) {
    // 插入位置不合法
    if (theIndex < 0 || theIndex > listSize){
        ostringstream s;
        s<<"Index = "<<theIndex<<" size = "<<listSize;
        throw "illegalParameter"+s.str();
    }
    // 判断数组是否满了
    if (listSize == arrayLenth){
        changeLength(element,arrayLenth,arrayLenth<<1);
        arrayLenth == arrayLenth<<1;
    }
    copy_backward(element+theIndex,element+listSize,element+listSize+1);
    element[theIndex] = theElement;
    listSize++;
}

template<class T>
void arrayList<T>::output(ostream &out) {
    // 把线性表插入到输出流中
    // ostream_iterator头文件 iterator
    copy(element,element+listSize,ostream_iterator<T>(cout," "));
}

template<class T>
ostream& operator<<(ostream& out,const linearList<T>*& t){
    t->ouput(out);
    return out;
}

template<class T>
void arrayList<T>::empty_push(const T &theElement) {
    // 判断线性表是否满了
    if (listSize == arrayLenth){
        changeLength(element,arrayLenth,arrayLenth<<1);
        arrayLenth = arrayLenth<<1;
    }
    element[listSize] = theElement;
    listSize++;
}

4.线性表基于数组实现vector -- STL标准库自带的

特点:

1.vector是一个可变大小数组的容器

2.vector采用连续存储空间来进行元素存储

3.vector使用动态分配数组来存储它的元素

4.支持下标随机访问元素

方法:

增(insert,push_back,empty_back)删(erase,pop_back,clear)改(下标,assign)查(下标,迭代器)

4.0 头文件

#include<vector>

4.1 构造函数

1.vector<T> v; // 空数组
2.vector<T> v(int size,int initialValue); // 长度为size的,每个空间都存储initialValue的数组
3.vector<T> v(const vector& t); // 复制t给v

4.2 末尾追加元素

vector<int> arr;
1.arr.push_back(1);   // 追加1
2.arr.emplace_back(2); // 追加2

4.3 插入元素 -- 使用迭代器地址插入元素

iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

4.4 删除元素 -- 使用迭代器地址删除元素

iterator erase(iterator it):删除向量中迭代器指向元素
iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
void pop_back():删除向量中最后一个元素
void clear():清空向量中所有元素

4.5 修改元素 -- 使用下标修改

1.使用下标修改

vector<int> a(0,1);
a[0] = 12;

2.使用assign函数赋值

void assign(int n,const T& x):设置向量中前n个元素的值为x
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
// 例如
arr.assign(a.begin(),a.end()); // 把arr元素变为a中元素,完全复制后,arr就是a的一个副本         

4.6 遍历元素

reference at(int pos):返回pos位置元素的引用
// 返回首尾元素的引用    
reference front():返回首元素的引用
reference back():返回尾元素的引用
// 返回迭代器地址(指针地址)    
iterator begin():返回向量头指针,指向第一个元素
iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin():反向迭代器,指向最后一个元素
reverse_iterator rend():反向迭代器,指向第一个元素之前的位置

4.7 容量函数

int size() const:返回向量中元素的个数
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size() const:返回最大可允许的vector元素数量值

4.8 判空函数

bool empty() const:判断向量是否为空,若为空,则向量中无元素
posted @ 2022-07-31 16:36  nanfengnan  阅读(41)  评论(0编辑  收藏  举报