《数据结构、算法与应用》 C++描述 -- 第5章 线性表 -- 数组描述 学习笔记

线性表 — 数组描述

线性表:也称有序表(ordered list),它的每一个实例都是元素的一个有序集合。

1. 抽象类 linearList

C++ 支持两种类 — 抽象类和具体类。

  • 抽象类:包含着没有实现代码的成员函数(即纯虚函数)。
  • 具体类:没有纯虚函数的类。

注意:只有具体类才可实例化。

一个线性表的抽象类描述:

#include <iostream>
using namespace std;

template<class T>
class linearList {
public:
    virtual ~linearList() {};
    virtual bool empty() const = 0;
    virtual int size() const = 0;
    virtual T& get(int index) const = 0;
    virtual int indexof(const T& theElement) const = 0;
    virtual void erase(int index) = 0;
    virtual void insert(int index, const T& theElement) = 0;
    virtual void output(ostream& out) const = 0;
};

2. 数组描述

2.1 变长一维数组

步骤:

  • 首先,要建立一个具有新长度的数组。
  • 然后,将数组a的元素复制到这个新数组中。
  • 最后,改变数组a的值,使其能够引用新数组。

注意:当数组满了且需要加大数组长度时,数组扩容的长度常常要加倍。称为,数组倍增。

code:

#include <iostream>
using namespace std;

template<class T>
void changeLength1D(T*& a, int oldLength, int newLength) {
    if (newLength < 0) {  //参数合法性检查,不符则抛出异常
        throw illegalParamenterValue("new length must bu >= 0");
    }
    T* tmp = new T[newLength];    //新建临时数组
    int number = min(oldLength, newLength);  //确定复制元素个数
    copy(a, a + number, tmp);  //复制
    delete [] a;   //释放原数组内存空间
    a = tmp;
}

2.2 类arrayList

arrayList 定义

使用上述的C++抽象类linearList的派生类arrayList,arrayList是一个具体类,必须实现抽象类linearList的所有方法。

template<class T>
class arrayList : public linearList<T> {
public:
    //构造函数
    arrayList(int initialCapacity = 10);
    arrayList(const arrayList<T>&);  //拷贝构造函数
    ~arrayList() {delete [] element;} //析构函数
    
    bool empty() const {return listSize == 0;}
    int size() const {return listSize;}
    T& get(int theIndex) const;
    int indexOf(const T& theElement) const;
    void erase(int theIndex);
    void insert(int theIndex, const T& theElement);
    void output(ostream& out) const;

    //其他方法
    int capacity() const {return arrayLength;}
private:
    void checkIndex(int index) const;
    T* element;   //存储线性表的一维数组
    int arrayLength;  //一维数组的容量
    int listSize;   //线性表的元素个数
};

构造函数和拷贝构造函数

//构造函数
template<class T>
arrayList<T>::arrayList(int initialCapacity) {
    if (initialCapacity < 1) {
        throw illegalParamenterValue("Initial capacity must be > 0");
    }
    arrayLength = initialCapacity;
    listSize = 0;
    element = new T[initialCapacity];
}
//拷贝构造函数
template<class T>
arrayList<T>::arrayList(const arrayList<T>& theList) {
    arrayLength = theList.arrayLength;
    listSize = theList.listSize;
    element = new T[arrayLength];
    copy(theList.element, theList.element + listSize, element);
}

arrayList实例化:

//创建容量为100的线性表
linearList *x = (linearList) new arrayList<int>(100);
arrayList<double> y(100);

//利用容量缺省创建
arrayList<char> z;
//复制创建
arrayList<double> w(y);

其他基本方法实现:

template<class T>
void arrayList<T>::checkIndex(int index) const {
    if (index >= listSize || index < 0) {
        throw illegalIndex("index error")
    }
}
template<class T>
T& arrayList<T>::get(int index) const {
    checkIndex(index);
    return element[index];
}

//返回元素theElement第一次出现的索引
template<class T>
int arrayList<T>::indexOf(const T& theElement) const {
    int Index = (int) (find(element, element + listSize, theElement) - element)
    if (Index >= element + listSize || index < element) {
        return -1;
    } else {
        return Index;
    }
}

删除元素:

    删除索引为index的元素,首先,要确定其是合法的。然后,利用copy()算法将索引 index + 1及其后的元素均向左移动一位。最后将数组长度listSize值减1.
template<class T>
void arrayList<T>::erase(int index) {
    checkIndex(index);  //检查索引是否合法
    copy(element + index + 1, element + listSize, element + index); //移动元素
    element[--listSize].~T(); // 调用析构函数,释放最后一位空间。
}

插入元素:

插入索引为index的元素,利用copy_backward() 算法将索引为index及其后的元素均向右移一位。最后将数组长度listSize的值加1。

template<class T>
void arrayList<T>::insert(int index, const T& theElement) {
    if (index < 0 || index > listSize);
    throw illegalIndex("Index error!");
    //索引有效,但数组已满,需要进行扩容,翻倍扩充避免不断的对数组进行扩容
    if (listSize == arrayLength) {
        changeLength1D(element, arrayLength, 2 * arrayLength);
        arrayLength *= 2;
    }
    //将index及其以后的元素向右移一位
    copy_backward(element + index, element + listSize, element + listSize + 1);
    element[index] = theElement;
    listSize++;
}

注意:数组扩容时,将其容量翻倍,而不是将其长度增加1或2的原因是,为了避免频繁插入的情况下不断的对数组进行扩容。

输出output 和重载 <<:

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

template<class T>
ostream& operator<<(ostream& out, const arrayList<T>& arr) {
    arr.output(out);
    return out;
}

3. 迭代器

迭代器(iterator)是一个指针,指向对象的一个元素。我们可以使用迭代器逐个访问对象的所有元素。

//arrayList 的迭代器
template<class T>
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;
    }
    //前++
    iterator&  operator++() {
        ++position;
        return *this;
    }
     //后++
    iterator operator++(int) {
        iterator old = *this;
        ++position;
        return old;
    }
    //迭代器前--;
    iterator& operator--() {
        --position;
        return *this;
    }
    //迭代器后--
    iterator operator--(int) {
        iterator old = *this;
        --position;
        return old;
    }

    bool iterator != (const iterator right) const {
        return position != right.position;
    }

    bool iterator == (const iterator right) const {
        return position == right.position;
    }

    iterator begin() {
        return iterator(element);
    }

    iterator end() {
        return iterator(element + listSize);
    }

private:
    T* position; //指向表元素的指针;
};

4. vector 容器

vector 的数据安排以及操作方式,与 array 非常相似,两者的唯一差别在于空间的运用的灵活性。 array 是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。 vector 是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。

动态增加大小,并不是在原空间之后续接新空间(因无法保证原空间之后仍有可配置的空间),而是到一块更大的内存空间,然后将原数据拷贝到新空间中并释放原空间。
注意:一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。

常用API:

//大小操作
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(int num);//重新指定容器的长度为 num,若容器变长,则以默认值填充新位置。如果容器变
//短,则末尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为 num,若容器变长,则以 elem 值填充新位置。如
//果容器变短,则末尾超出容器长>度的元素被删除。
capacity();//容器的容量
reserve(int len);//容器预留 len 个元素长度,预留位置不初始化,元素不可访问。

//数据存储操作
at(int idx); //返回索引 idx 所指的数据,如果 idx 越界,抛出 out_of_range 异常
operator[];//返回索引 idx 所指的数据,越界时,运行直接报错
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素


//插入和删除操作
insert(const_iterator pos, int count,ele);//迭代器指向位置 pos 插入 count 个元素 ele
push_back(ele); //尾部插入元素 ele
pop_back();//删除最后一个元素
erase(const_iterator start, const_iterator end);//删除迭代器从 start 到 end 之间的元素
erase(const_iterator pos);//删除迭代器指向的元素
clear();//删除容器中所有元素
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值