線性表的順序存儲結構是用一段地址連續的存儲單元依次存儲線性表中的數據元素。
使用一維數組實現順序存儲結構。
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;//數組長度};