线性表的顺序存储结构是用一段地址连续的存储单元依次存储线性表中的数据元素。
使用一维数组实现顺序存储结构。
template <typename T> class SeqList:public List<T> { protected: T* m_array;//顺序存储空间 int m_length;//当前线性表的长度 };
一维顺序存储结构能够是原生数组或是动态分配的空间。所以,根据空间分配的不一样,分为静态线性表和动态线性表。
(1)元素的获取
顺序存储结构的元素获取操做:
A、判断目标位置是否合法
B、将目标位置做为数组下标获取元素数组
bool get(int index, T& value) { //判断目标位置是否合法 bool ret = (0 <= index) && (index < m_length); if(ret) { //将目标位置做为下标获取元素 value = m_array[index]; } return ret; }
(2)元素的插入操做
顺序存储结构的元素插入操做:
A、判断目标位置是否合法
B、将目标位置后的全部元素向后移一位
C、将新元素插入目标位置
D、线性表长度增长1安全
bool insert(int index, const T& value) { //判断目标位置是否合法 bool ret = (0 <= index) && (index <= m_length); ret = ret && ((m_length + 1) <= capacity()); if(ret) { //将目标位置后的全部元素后移一个位置 for(int i = m_length -1; index <= i; i--) { m_array[i+1] = m_array[i]; } //将新元素插入到目标位置 m_array[index] = value; //线性表长度加1 m_length++; } return ret; }
(3)元素的删除操做
顺序存储结构的元素删除操做:
A、判断目标位置是否合法
B、将目标位置后的全部元素前移一个位置
C、线性表长度减1数据结构
bool remove(int index) { //判断目标位置是否合法 bool ret = (0 <= index) && (index < m_length); if(ret) { //将目标位置后的全部元素前移一位 for(int i = index; i < m_length-1; i++) { m_array[i] = m_array[i+1]; } //将线性表长度减1 m_length--; } return ret; }
(4)元素值的修改
顺序存储结构中元素值的修改:
A、判断目标位置是否合法
B、修改目标位置元素的值ide
bool set(int index, const T& value) { //判断目标位置是否合法 bool ret = (0 <= index) && (index < m_length); if(ret) { //修改目标位置元素的值 m_array[index] = value; } return ret; }
(5)线性表长度的获取
顺序存储结构线性表的长度获取函数
int length()const { //线性表长度 return m_length; }
(6)线性表的清空this
void clear() { //清空线性表 m_length = 0; }
(7)[]操做符重载spa
//[]操做符重载 T& operator[](int index) { //判断目标位置是否合法 if((0 <= index) && (index < m_length)) { //返回下标相应的元素 return m_array[index]; } else { THROW_EXCEPTION(IndexOutOfBoudsException, "Paramenter index is invalid..."); } } //const对象的[]重载 T operator[](int index)const { //转换为非const对象后使用[]操做 return (const_cast<SeqList<T>&>(*this))[index]; }
(8)顺序存储结构空间的大小
virtual int capacity()const = 0;
因为存储空间在子类分配,所以为纯虚函数。设计
template <typename T> class SeqList:public List<T> { public: bool insert(int index, const T& value) { //判断目标位置是否合法 bool ret = (0 <= index) && (index <= m_length); ret = ret && ((m_length + 1) <= capacity()); if(ret) { //将目标位置后的全部元素后移一个位置 for(int i = m_length -1; index <= i; i--) { m_array[i+1] = m_array[i]; } //将新元素插入到目标位置 m_array[index] = value; //线性表长度加1 m_length++; } return ret; } bool remove(int index) { //判断目标位置是否合法 bool ret = (0 <= index) && (index < m_length); if(ret) { //将目标位置后的全部元素前移一位 for(int i = index; i < m_length-1; i++) { m_array[i] = m_array[i+1]; } //将线性表长度减1 m_length--; } return ret; } bool set(int index, const T& value) { //判断目标位置是否合法 bool ret = (0 <= index) && (index < m_length); if(ret) { //修改目标位置元素的值 m_array[index] = value; } return ret; } bool get(int index, T& value)const { //判断目标位置是否合法 bool ret = (0 <= index) && (index < m_length); if(ret) { //将目标位置做为下标获取元素 value = m_array[index]; } return ret; } int length()const { //线性表长度 return m_length; } void clear() { //清空线性表 m_length = 0; } int find(const T& value)const { int ret = -1; //遍历线性表 for(int i = 0; i < m_length; i++) { //若是找到元素,退出循环 if(m_array[i] == value) { ret = i; break; } } return ret; } //[]操做符重载 T& operator[](int index) { //判断目标位置是否合法 if((0 <= index) && (index < m_length)) { //返回下标相应的元素 return m_array[index]; } else { THROW_EXCEPTION(IndexOutOfBoudsException, "Paramenter index is invalid..."); } } //const对象的[]重载 T operator[](int index)const { //转换为非const对象后使用[]操做 return (const_cast<SeqList<T>&>(*this))[index]; } virtual int capacity()const = 0; protected: T* m_array;//顺序存储空间 int m_length;//当前线性表的长度 };
数据元素的前移: 将后面的数据元素向前移动,须要先将前面的数据元素前移,依次移动,直到最后一个数据元素前移,通常用于删除一个数据元素后将后面的数据元素前移。 //将目标位置后的全部元素前移一位 for(int i = index; i < m_length-1; i++) { m_array[i] = m_array[i+1]; } 数据元素的后移: 将前面的数据元素向后移动,须要先将最后的数据元素后移,依次移动,直到第i个数据元素被后移,通常用于插入一个新的数据元素,须要先将插入位置后的全部数据元素后移,在位置处放入新的数据元素。 //将目标位置后的全部元素后移一个位置 for(int i = m_length -1; index <= i; i--) { m_array[i+1] = m_array[i]; }
使用原生数组做为顺序存储空间,使用模板参数肯定数组大小。
template <typename T, int N> class StaticList:public SeqList<T> { public: StaticList() { this->m_array = m_space;//指定父类指针指向的空间 this->m_length = 0;//设置初始长度 } int capacity() const { return N; } protected: T m_space[N];//顺序存储空间,N为模板参数 };
须要父类的实现纯虚函数capacity()。
使用动态申请的堆空间做为顺序存储空间,动态设置顺序存储空间的大小并确保重置顺序存储空间大小时的异常安全。
函数异常安全要求不泄漏任何资源,不容许破坏数据。为了确保异常安全,在异常抛出时,必须确保:
A、对象内的任何成员仍然能保持有效状态
B、没有数据的破坏及资源泄漏。指针
template <typename T> class DynamicList:public SeqList<T> { public: DynamicList(int capacity) { //申请动态空间 this->m_array = new T[capacity]; if(this->m_array) { this->m_length = 0;//初始长度 this->m_capacity = capacity;//空间大小 } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } int capacity() const { return m_capacity; } void resize(int capacity) { if(capacity != m_capacity) { T* array = new T[capacity]; if(array) { int length = (this->length() < capacity ? this->length():capacity); for(int i = 0; i < length; i++) { array[i] = this->m_array[i]; //若是抛出异常,原数组状态没有改变 } T* temp = this->m_array; this->m_array = array; this->m_capacity = capacity; this->m_length = length; delete[] temp; //若是抛出异常,重置后的数组状态已经改变 } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } } ~DynamicList() { delete[] this->m_array;//释放申请的动态空间 } protected: int m_capacity;//顺序存储空间的大小 };
顺序存储结构线性表的插入和删除操做的时间复杂度都是O(n),可是因为插入和删除操做都会涉及到线性表中数据元素的移动,所以会频繁涉及拷贝构造函数和赋值操做符的调用,若是数据元素对象内部的资源较多,对象间的复制、赋值将是很是耗时的,同时因为线性表是泛型模板,没法肯定实际实例化的对象类型是不是占有资源的类类型,所以须要禁用拷贝构造函数和赋值操做符,禁用的方式只须要将拷贝构造函数和赋值操做符声明为protected便可。code
template <typename T> class List:public Object { protected: List(const List<T>& other); List<T>& operator=(const List<T>& other); public: List(){} virtual bool insert(int index, const T& value) = 0; virtual bool remove(int index) = 0; virtual bool set(int index, const T& value) = 0; virtual bool get(int index, T& value) = 0; virtual int length()const = 0; virtual void clear() = 0; };
顺序存储结构线性表重载了[]操做符,所以可使用[]操做符访问顺序存储结构线性表中的元素。可是线性表中必须存在要访问的元素,即先插入元素才能使用[]操做符访问元素。
要建立一个数组类取代原生数组,数组类须要避免原生数组的缺陷:
A、数组类包含长度信息
B、数组类可以主动发现越界访问
数组类的设计以下:
A、抽象类模板,存储空间的位置和大小由子类完成
B、重载数组操做符,判断访问下标是否越界
C、提供数组长度的抽象访问函数
D、拷贝构造函数和赋值操做符须要在子类实现
数组的实现:
template <typename T> class Array:public Object { public: virtual bool set(int index, const T& value) { bool ret = (0 <= index) && (index < length()); if(ret) { m_array[index] = value; } return ret; } virtual bool get(int index, T& value) { bool ret = (0 <= index) && (index < length()); if(ret) { value = m_array[index]; } return ret; } T& operator[](int index) { if((0 <= index) && (index < length())) { return m_array[index]; } else { THROW_EXCEPTION(IndexOutOfBoudsException, "Parameter index is valid..."); } } T operator[](int index)const { return const_cast<T&>(*this)[index]; } virtual int length()const = 0; protected: T* m_array; }; }
指定原生数组做为数组类的存储空间实现静态数组类,使用模板参数指定数组大小,实现函数返回数组的长度,实现重载拷贝构造函数和赋值操做符。
template <typename T,int N> class StaticArray:public Array<T> { public: StaticArray() { this->m_array = m_space; } //拷贝构造函数 StaticArray(const StaticArray<T,N>& other) { this->m_array = m_space; for(int i = 0; i < N; i++) { m_space[i] = other.m_space[i]; } } //赋值操做符 StaticArray& operator=(const StaticArray<T,N>& other) { if(this != &other) { for(int i = 0; i< N; i++) { m_space[i] = other.m_space[i]; } } return *this; } int length() const { return N; } protected: T m_space[N];//存储空间 };三、动态分配数组实现
使用动态分配的堆空间做为数组类的存储空间实现动态分配数组类。 类模板设计须要动态肯定分配存储空间的大小,实现函数返回数组大小,实现拷贝构造函数和赋值操做符。template <typename T> class DynamicArray:public Array<T> { public: //构造函数 DynamicArray(int length) { this->m_array = new T[length]; if(this->m_array != NULL) { this->m_length = length; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } //拷贝构造函数 DynamicArray(const DynamicArray<T>& other) { this->m_array = new T[other.m_length]; if(this->m_array != NULL) { this->m_length = other.m_length; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } //赋值操做符 DynamicArray<T>& operator=(const DynamicArray<T>& other) { if(this != &other) { T* array = new T[other.m_length]; if(array != NULL) { for(int i = 0; i < other.m_length; i++) { array[i] = other.m_array[i]; } T* temp = this->m_array; this->m_array = array; this->m_length = other.m_length; delete[] temp; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } return *this;
} int length()const { return m_length; } //重置数组长度 void resize(int length) { if(this->m_length != length) { T* array = new T[length]; if(array != NULL) { int size = (length < this->m_length)?length:this->m_length; for(int i = 0; i < size; i++) { array[i] = this->m_array[i]; } T* temp = this->m_array; this->m_array = array; this->m_length = length; delete[] temp; } else { THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory..."); } } } ~DynamicArray() { delete[] this->m_array; }
protected:int m_length;//数组长度};