线性表的链式描述

来源:互联网 发布:真盖塔 知乎 编辑:程序博客网 时间:2024/05/25 20:00

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 并使其索引为theIndextemplate<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.双向链表
暂时留着,后序补充上来.

原创粉丝点击