線性表的鏈式描述

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 &&currentNode->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.雙向鏈表
暫時留着,後序補充上來.

發佈了46 篇原創文章 · 獲贊 66 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章