1.概述:
鏈式描述中,線性表的元素存在內存中的位置是隨機的。
基於數組的描述中,元素的地址是由數學公式決定的,而在鏈式描述中,元素的地址是隨機分佈的.
2.單向鏈表:
2.1 描述
數據對象的實例的每個元素都用一個單元或節點來描述。
每個節點都明確包含另一個相關節點的位置信息,稱爲鏈或指針。
一般來說,爲找到索引爲theIndex的元素,需要從firstNode開始,跟蹤theIndex個指針才能找到.
2.2結構chainNode
爲用鏈表描述線性表,定義一個結構chainNode和一個類chain.
結構chainNode代碼如下:
//鏈表節點的結構定義
template<class T>
struct chainNode
{
T element;
chainNode<T>* next;
chainNode(){}
chainNode(const T & element)
{
this->element=element;
}
chainNode(const T & element,chainNode<T>* next)
{
this->element=element;
this->next=next;
}
};
類chain的定義代碼如下:
// 鏈表節點的結構定義
template<class T>
class chain:public linearList<T>
{
public:
chain(int initialCapacity=10);
chain(const chain<T> &);
~chain();
//ADT方法
bool empty()const{ return listSize==0;}
int size()const {return listSize;}
T & get(int theIndex)const;
int indexOf(const T &theElement);
void erase(int theIndex,const T & theElement);
void output(ostream & out)const;
protected:
void checkIndex(int theIndex)const;
//數據成員
chainNode<T>* firstNode;//指向鏈表第一個節點的指針
int listSize;//線性表的元素個數
};
鏈表的構造和複製構造函數、析構函數代碼如下:
//構造函數
template<class T>
chain<T>::chain(int initialCapacity)
{
if (initialCapacity<1)
{
ostringstream s;
s<<"Initial Capacity="<<initialCapacity<<" Must be >0";
throw illegalParameterValue(s.str());
}
firstNode=NULL;
listSize=0;
}
//複製構造函數
template<class T>
chain<T>::chain(const chain <T>& theList)
{
listSize=theList.listSize;
if (listSize==0)
{
firstNode=NULL;
return;
}
//鏈表節點爲非空
chainNode<T>* sourceNode=theList.firstNode;//要複製的theList的節點
firstNode=new chainNode<T> (sourceNode->element);//複製鏈表theList的首元素
sourceNode=sourceNode->next;
chainNode<T>* targetNode=firstNode;//當前鏈表*this的最後一個節點
while (sourceNode!=NULL)
{
targetNode->next=new chainNode<T>* (sourceNode->element);
targetNode=targetNode->next;
sourceNode=sourceNode->next;
}
}
//析構函數
template<class T>
chain<T>::~chain()
{
while(firstNode!=NULL)
{
chainNode<T>*nextNode=firstNode->next;
delete firstNode;
firstNode=nextNode;
}
}
時間複雜度:
構造函數爲O(1),複製構造函數要複製鏈表theList的每一個節點,因此時間複雜度是O(max{ListSize,theList.listSize);
析構函數要逐個清楚鏈表的節點,策略是重複清楚鏈表的首元素節點,直到鏈表爲空,必須在清楚首元素節點之前用變量保存第2個元素節點的指針。析構函數爲O(listSize);
其他方法定義如下:
// get方法的返回值是索引爲theIndex的元素
template<class T>
T & chain<T>::get(int theIndex)const
{
checkIndex(theIndex);
chainNode<T>* currentNode=firstNode;
for(int i=0;i<theIndex;i++)
currentNode=currentNode->next;
return currentNode->element;
}
//返回元素theElement首次出現時的索引
template<class T>
int chain<T>::indexOf(const T & theElement)const
{
chainNode<T>* currentNode=firstNode;
int index=0;
while(currentNode!=NULL &¤tNode->element!=theElement)
{
currentNode=currentNode->next;
index++;
}
//確定是否找到所需要的元素
if (currentNode==NULL)
{
return -1;
}
else
return index;
}
//刪除索引爲theIndex的元素
template<class T>
void chain<T>::erase(int theIndex)
{
//首先檢查索引是否有效
checkIndex(theIndex);
//索引有效,需找到要刪除的元素節點
chainNode<T>* deleteNode;
if (theIndex==0)
{
deleteNode=firstNode;
firstNode=firstNode->next;
}
else
{
//用指針p指向要刪除的節點的前驅節點
chainNode<T>*p=firstNode;
for (int i=0;i<theIndex-1;i++)
{
p=p->next;
}
}
listSize--;
delete deleteNode;
}
//插入元素theElement 並使其索引爲theIndex
template<class T>
void chain<T>::insert(int theIndex,const T & theElement)
{
if (theIndex<0||theIndex>listSize)
{
ostringstream s;
s<<"index="<<theIndex<<" size="<<listSize;
throw illegalIndex(s.str());
}
if (theIndex==0)
{
firstNode=new chainNode<T>(theElement,firstNode);
}
else
{
chainNode <T>* p=firstNode;
for (int i=0;i<theIndex-1;i++)
{
p=p->next;
}
p->next=new chainNode<T>(theElement,p->next);
//覺得此處有問題,插入之後爲什麼沒有和後面的節點連接起來
}
listSize++;
}
//方法output、輸出鏈表
template<class T>
void chain<T>::output(ostream & out)const
{
for(chainNode<T>* currentNode=firstNode;currentNode!=NULL;currentNode=currentNode->next)
{
out<<currentNode->element<<" ";
}
}
//重載<<
template<class T>
ostream & operator<<(ostream & out,const chain<T>& x)
{
x.output(out);
return out;
}
時間複雜度:
get()複雜度在鏈表中O(theIndex),在數組描述的線性表中O(1).
indexOf():在鏈表描述的線性表中,唯一的方法是用當前節點的指針確定下一個相鄰節點的位置,複雜度爲O(listSize).
erase():複雜度爲O(theIndex),而arrayList::erase 的時間複雜度是O(listSize-theIndex).因此在接近表頭的位置實施刪除操作時,鏈式描述的線性表比數組描述的線性表有更好的時間性能.
insert():爲在鏈表中索引爲theIndex的位置上插入一個新元素,需要首先找到索引爲theIndex-1的元素節點,然後在該節點之後插入新元素節點,複雜度爲O(theIndex).
output():複雜度爲O(listSize).
3.循環鏈表:
在鏈表的前面增加一個節點,稱爲頭節點,只要將單向鏈表的尾節點與頭節點鏈接起來,單向鏈表就稱爲循環鏈表.下面是帶有頭節點的循環鏈表的構造函數代碼和indexOf代碼.
//6.13搜索帶有頭節點的循環鏈表
template<class T>
circularListWithHeader<T>::circularListHeader()
{
headerNode=new chainNode<T>();
headerNode->next=headerNode;
listSize=0;
}
template<class T>
int circularListWithHeader<T>::indexOf(const T & theElement) const
{
//將元素放入頭節點
headerNode->element=theElement;
//在鏈表中搜索元素theElement
chainNode<T>*currentNode=headerNode->next;
int index=0;//當前節點的索引
while(currentNode->element!=theElement)
{
//移動到下一個節點
currentNode=currentNode->next;
index++;
}
//確定是否找到元素theElement
if (currentNode==headerNode)
return -1;
else
return index;
}
4.雙向鏈表:
暫時留着,後序補充上來.