1. 程式人生 > >14、靜態資料結構和動態資料結構

14、靜態資料結構和動態資料結構

線性表(list)的表現形式:零個或多個數據元素組成的集合,資料元素在位置上是有序列的,資料元素的個數是有限的,資料元素的型別必須相同。

線性表的抽象定義:線性表是具有相同型別的n個數據元素的有限序列,每一個叫表項,n是表長度

純虛擬函式的作用:為了方便使用多型特性,我們常常需要在基類中定義虛擬函式。在很多情況下,基類本身生成物件是不合理的,例如,動物作為一個基類派生出老虎獅子等,但動物本身生成物件不合理。為了解決上述問題,引入純虛擬函式的概念,將函式定義為純虛擬函式,則編譯器要求再派生類中必須予以重寫以實現多型性,同時含有純虛擬函式的類稱為抽象類,它不能生成物件。這樣就很好的解決了上述兩個問題。

//模板的方式描述線性表,不要List.cpp
#ifndef LIST_H_
#define LIST_H_
#include "Wobject.h"
namespace WSlib
{
template <typename T>
class List:public Wobject
{
public:
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)=0;
virtual int length()const =0;
virtual void clear()=0;
};
}
#endif
//線性表是資料元素的有序並且有限的集合,線性表中的資料元素必須是型別相同的,可用於描述排隊關係的問題
//線性表在c++中表現為一個抽象類
//線性表的順序儲存結構,指的是用一段地址連續的儲存單元依次儲存線性表中的資料元素
//可以用一維陣列來實現順序儲存結構,儲存空間:T* m_array: 當前長度:int m_length;
//順序儲存結構的元素獲取操作:判斷目標位置是否合法,將目標位置作為陣列下標獲取元素
//順序表的插入操作:1、判斷目標位置是否合法2、將目標位置之後的所有元素後移一個位置3、將新元素插入目標位置4、線性表長度加1
//順序儲存結構的元素刪除操作:1、判斷目標位置是否合法2、將目標位置後的所有元素前移一個位置3、將線性表長度減一
//完成順序儲存結構線性表SeqList的抽象實現,設計要點:

//抽象類模板,儲存空間的位置和大小由子類完成,實現順序儲存結構線性表的關鍵操作(增,刪,查),提供陣列操作符,快速訪問資料

#ifndef SEQLIST_H_
#define SEQLIST_H_
#include "List.h"
#include "Exception.h"
namespace WSlib
{
template<typename T>
class SeqList:public List<T>
{
protected:
T* m_array;  //順序儲存空間
T 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 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=((i>=0)&&(i<m_length));
if(ret)
{
m_array[i]=e;
}
return ret;
}
bool get(int i,T& e)
{
bool ret=((i>=0)&&(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))   //沒有等於 mmp
{
return m_array[i];
}
else
{
THROW_EXCEPTION(IndexOutOfBoundsException, "parameter is invald....");
}
}


/*T& operator[](int i) const
{
return (const_cast<SeqList<T>&>(*this))[i];
//return (*this)[i];
}*/
//順序儲存空間的容量
virtual int capacity()const=0;  //獲取最大容量,純虛擬函式交給子類完成
};
}
#endif
/********************************************************************************
#include<iostream>
#include"SeqList.h"
using namespace std;
using namespace WSlib;

int main()
{
SeqList<int>* l;

return 0;
}

**********************************************************************************/

//SeqList僅僅把關鍵的操作實現了,順序儲存空間的指定在它的子類實現
//思考StaticList和Dynaminlist如何實現,差異在哪裡?
//StaticList設計要點:類模板,使用原生陣列作為順序儲存空間,使用模板引數決定陣列大小
#ifndef STATICLIST_H_
#define STATICLIST_H_
#include "SeqList.h"
namespace WSlib
{
template <typename T,int N>
class StaticList:public SeqList<T>
{
protected:
T m_space[N]; //順序儲存空間,N為模板引數
public:
StaticList()     //指定父類成員的初始值
{
this->m_array=m_space; //this->m_array=m_space;
this->m_length=0;      //this->m_length=0;
}
int capacity() const   //成員函式需要考慮一下是否是const屬性
{
return N;
}
};
}
#endif
/*************************************************************************
#include<iostream>
#include"StaticList.h"
using namespace std;
using namespace WSlib;

int main()
{
StaticList<int,5> l;
for(int i=0;i<l.capacity();i++)
{
l.insert(0,i);
}
for(int i=0;i<l.length();i++)
{
cout<<l[i]<<endl;
}
l[0]*=l[0];
for(int i=0;i<l.length();i++)
{
cout<<l[i]<<endl;
}
try
{   
  l[5]=5;
}
catch(const Exception&e)
{
cout<<e.message()<<endl;
cout<<e.location()<<endl;
}
return 0;
}

*************************************************************************/

/*
設計要點:類模板
申請連續堆空間作為順序儲存空間,動態設定順序儲存空間的大小,保證重置順序儲存空間時的異常安全性
函式異常安全的概念:不洩漏任何資源,不允許破壞資料。
函式異常安全的基本保證:如果異常被丟擲,物件內的任何成員任然能保持有效狀態,沒有資料的破壞及資源洩漏
*/
#ifndef DYNAMICLIST_H_
#define DYNAMICLIST_H_
#include "SeqList.h"
namespace WSlib
{
template <typename T>
class DynamicList:public SeqList<T>
{
protected:
int m_capacity; //順序儲存空間的大小
public:
DynamicList(int capacity)//申請空間
{
this->m_array=new T[capacity];
if(this->m_array!=NULL)
{
this->m_length=0;
this->m_capacity=capacity;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"no memory to create dynamiclist object...");
}

}
int capacity() const
{
return m_capacity;
}
//重新設定順序儲存空間的大小
void resize(int capacity)
{
if(m_capacity!=capacity)
{
T* array=new T[capacity];
if(array!=NULL)
{
int length=(this->m_length<capacity? this->m_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_length=length;
this->m_capacity=capacity;
delete[] temp;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException,"no memory to create dynamiclist object");
}
}
/*我們要重置空間大小,但是之前的資料不能丟失,如果之前是5,resize為3,前三個資料要保留下來,
m_array指向之前的空間,delete的時候可能呼叫解構函式的呼叫,因為泛指型別,如果是類型別丟擲一個異常,
函式異常返回,則下面的賦值操作無法執行,不是異常安全的,先用temp指向之前的空間,3條賦值語句不會發生異常,
然後在delete,即使發生異常,賦值已經完成,還是可以用的,for迴圈裡邊的賦值操作有可能發生異常,泛指型別T導致的,交由第三方工程師處理*/
}
~DynamicList() //歸還空間
{
delete[] this->m_array;
}
};
}

#endif

/*************************************************************************************************************
#include<iostream>
#include"DynamicList.h"
using namespace std;
using namespace WSlib;
int main()
{
DynamicList<int> l(5);
for(int i=0;i<l.capacity();i++)
{
l.insert(0,i);
}
for(int i=0;i<l.length();i++)
{
cout<<l[i]<<endl;
}
l[0]*=l[0];
for(int i=0;i<l.length();i++)
{
cout<<l[i]<<endl;
}
try
{   
  l[5]=5;
}
catch(const Exception&e)
{
cout<<e.message()<<endl;
cout<<e.location()<<endl;
l.resize(10);
l.insert(5,50);
}
l[5]=5;
for(int i=0;i<l.length();i++)
{
cout<<l[i]<<endl;
}
l.resize(3);
for(int i=0;i<l.length();i++)
{
cout<<l[i]<<endl;
}
return 0;
}
//是否可以將dynamiclist作為staticlist的子類實現,不能,反之也不能,兩個類對順序儲存空間的指定是截然不同的,
//沒有關係,所以在類層次結構的地位是平等的。staticlist通過模板引數定義順序儲存空間,
//dynamiclist通過動態記憶體申請定義順序儲存空間,支援動態重置順序儲存空間的大小,resize的實現需要保證異常安全
*************************************************************************************************************/

靜態資料結構,例如陣列在記憶體中是連續的儲存區域,缺點是長度是固定的,新增或刪除某一資料花費的時間比較多。優點可以直接訪問各個資料,各個資料的地址都可以根據首地址得到,訪問時間複雜度O(1)。

動態資料結構,例如連結串列在記憶體中不是連續的儲存區域,每一個節點包含節點資料和下一個節點的指標。缺點是不利於節點的隨機訪問。訪問節點花費時間比較多,為O(n)。優點是能夠動態的調整容量,插入或者刪除資料方便。