线性表 — 数组描述
线性表:也称有序表(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();//删除容器中所有元素