线性表的链式描述
来源:互联网 发布:真盖塔 知乎 编辑:程序博客网 时间: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 &¤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 并使其索引为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.双向链表:
暂时留着,后序补充上来.
- 线性表的链式描述
- 数据结构之线性表——链表的链式存储(链式描述)
- [C++]数据结构:线性表的公式化描述和链式描述的结构特点与基本操作
- 链式线性表的实现
- 线性表的链式存储
- 线性表的链式表示
- 线性表的链式存储
- 线性表的链式存储
- 线性表的链式实现
- 线性表的链式存储
- 线性表的链式存储
- 线性表的链式存储
- 线性表的链式实现
- 线性表的链式存储
- 线性表的链式存储
- 《线性表的链式存储》
- 线性表的链式存储
- 线性表的链式存储
- angularjs2 如何嵌套多层循环
- XML文档解析之DOM4J解析
- ajax 重定向跨域问题
- ios 收集crash xcode 调试 捕捉signal信号
- Android 当前日期,计算时间推后几天和提前几天的封装方法
- 线性表的链式描述
- Python:MECARD字符串转json
- 微信分享
- 百度地图的集成过程
- iOS
- 统计胜负
- android Studio 加快编译速度(2017-06-21版)
- 传统的操作数据库的主要步骤
- kali2.0 metasploit安装xssf模块