線性表的本質、操作及順序存儲結構(六)
那麽線性表(List)的表現形式是怎樣的呢?符合以下幾個特征:1、零個或多個數據元素組成的集合;2、數據元素在位置上是有序排列的;3、數據元素的個數是有限的;4、數據元素的類型必須是相同的。那麽線性表的抽象定義是怎麽定義的呢?線性表是具有相同類型的 n( ≥ 0 ) 個數據元素的有限序列。如下
下來我們來看看 List 的本質:1、a0 為線性表的第一個元素,只有一個後繼;2、an-1 為線性表的最後一個元素,只有一個前驅;3、除 a0
線性表在程序中表現為一種特殊的數據類型。定義如下
#ifndef LIST_H#define LIST_H #include "Object.h" namespace DTLib { template < typename T > class List : public Object { protected: List(const List&); List& operator= (const List&); public: List() {} virtual bool insert(const T& e) = 0; virtual bool insert(int i, const T& e) = 0; virtual bool remove(int i) = 0; virtual bool set(int i, const T& e) = 0; virtual bool get(int i, T& e) const = 0; virtual int length() const = 0; virtual void clear() = 0; }; } #endif // LIST_H
下面我們來看看順序存儲的定義:線性表的順序存儲結構,指的是用一段地址連續的存儲單元依次存儲線性表中的數據元素。如下
那麽我們該如何設計呢?可以使用一維數組來實現順序存儲結構,存儲空間:T* m_array;當前長度: int m_length;定義如下
template < typename T > class SeqList : public List<T> { protected: T* m_array; int m_length; //// }
那麽順序存儲結構的元素獲取操作怎樣來實現呢?一是判斷目標位置是否合法,二是將目標位置作為數組下標獲取元素
bool SeqList<T>::get(int i, T& e) const { bool ret = ((0 <= i) && (i < m_length)); if( ret ) { e = m_array[i]; } return ret; }
順序存儲結構的元素插入示例如下
bool SeqList<T>::insert(int i, const T& e) { bool ret = ((0 <= i) && (i <= m_length)); ret = ret && (m_length < capacity()); if( ret ) { for(int p=m_length-1; p>=i; p--) { m_array[p+1] = m_array[p]; } m_array[i] = e; m_length++; } return ret; }
順序存儲結構的元素刪除操作:1、判斷目標位置是否合法;2、將目標位置後的所有元素前移一個位置;3、線性表長度減一。刪除示例如下
bool SeqList<T>::remove(int i) { bool ret = ((0 <= i) && (i < m_length)); if( ret ) { for(int p=i; p<m_length-1; p++) { m_array[p] = m_array[p+1]; } m_length--; } return ret; }
我們完成了以上的幾個重要操作之後,我們來看看 SeqList 處於一個什麽樣的位置,如下圖所示
那麽這便是一個順序存儲結構線性表的抽象實現了。在 SeqList 設計時,我們要只有以下幾點:1、抽象類模板,存儲空間的位置和大小由子類完成;2、實現順序存儲結構線性表的關鍵操作(增、刪、查等);3、提供數組操作符,方便快速獲取元素。那麽它的抽象定義如下:
template < typename T > class SeqList : public List<T> { protected: T* m_array; int m_length; public: bool insert(int i, const T& e); bool remove(int i); bool set(int i, const T& e); bool get(int i, T& e) const; int length() const; void clear(); T& operator[] (int i); T& operator[] (int i) const; virtual int capacity() const = 0; };
下來我們來看看 SeqList 是怎麽寫的
SeqList.h 源碼
#ifndef SEQLIST_H#define SEQLIST_H #include "List.h" #include "Exception.h" namespace DTLib { template < typename T > class SeqList : public List<T> { protected: T* m_array; int m_length; public: bool insert(int i, const T& e) { bool ret = ((0 <= i) && (i <= m_length)); ret = ret && (m_length < capacity()); if( ret ) { for(int p=m_length-1; p>=i; p--) { m_array[p+1] = m_array[p]; } m_array[i] = e; m_length++; } return ret; } bool insert(const T& e) { return insert(m_length, e); } bool remove(int i) { bool ret = ((0 <= i) && (i < m_length)); if( ret ) { for(int p=i; p<m_length-1; p++) { m_array[p] = m_array[p+1]; } m_length--; } return ret; } bool set(int i, const T& e) { bool ret = ((0 <= i) && (i < m_length)); if( ret ) { m_array[i] = e; } return ret; } bool get(int i, T& e) const { bool ret = ((0 <= i) && (i < m_length)); if( ret ) { e = m_array[i]; } return ret; } int length() const { return m_length; } void clear() { m_length = 0; } T& operator[] (int i) { if( (0 <= i) && (i < m_length) ) { return m_array[i]; } else { THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ..."); } } T operator[] (int i) const { return (const_cast<SeqList<T>&>(*this))[i]; } virtual int capacity() const = 0; }; } #endif // SEQLIST_H
main.cpp 源碼
#include <iostream>#include "Seqlist.h" using namespace std; using namespace DTLib; int main() { SeqList<int>* l; return 0; }
經過編譯,編譯時通過的,說明我們已經完成了 SeqList 抽象類的實現了。接下來我們思考下,在前面的 SeqList 的實現中,我們在它的下面還有 StaticList 和 DynamicList 的兩個子類,那麽我們該如何去實現它們呢?StaticList 和 DynamicList 的差異在哪裏?是否可以將 DynamicList 作為 StaticList 的子類實現呢?通過對線性表的學習,總結如下:1、線性表是數據元素的有序並且有限的集合,並且線性表中的數據元素必須是類型相同的;2、線性表可用於描述派對關系的一系列問題;3、線性表在程序中表現為一種特殊的數據類型;4、線性表在 C++ 中表現為一個抽象類。
線性表的本質、操作及順序存儲結構(六)