链表是由一个个节点组成的数组,节点之间的内存并不连续,为了搞清楚链表的原理,我们先来看看节点的形式。
template<class T>
struct chainNode
{
T element;
chainNode<T>*next;
chainNode(){}
chainNode(const T& element;)
chainNode(const T& element,chainNode<T>*next)
{
this->element=element;
this->next=next
}
}
以上是节点的代码,是个构造函数,里面有两个变量element和next一个用来存储元素,另一个用来存取下一个节点的
地址,通过next就能将分散栽在不同内存的节点链接起来,但是我们只能通过前面的节点找到后面的节点,所以这个叫
单向链表,里面有三个构造函数。这里提一下,结构体和类的构建和使用方法是非常类似的,可以使用结构体的地方我们
都可以用类来代替,反之大部分情况也是可以的。区别在于,构造体只能公有继承。
有了上面节点这个材料,我们就可以开始构建链表了。
template<class T>
class chain:public linearList<T>
{
public:
chain(int initialCapacity=10);
chain(const chain<T>&);
~chain();
bool empty()const {return listSize==0;}
int size() const {return listSize;}
T& get(int theIndex) const;
int indexOf(const T& theElement)const;
void erase(int theIndex);
void insert(int theIndex,const T& theElement);
void output(ostream& out)const;
protected:
void checkIndex(int theIndex)const;
chainNode<T>* firstNode;
int listSize;
}
以上就是链表的类定义,里面构造函数里面有capacity这个变量,但是类定义里面并没有这个成员变量,因为,链表的最大长度是无限的,写这个只是为了和arrayList进行兼容,然后size记录了当前节点的个数,在chain类里面,只有一个firstNode成员,这个成员指向链表的第一个节点,因为知道了链表的第一个节点,我们就可以找到链表的所有节点。
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;//获取第一个节点的地址
firstNode=new chainNode<T>(sourceNode->element);//new一个新节点作为新的firstNode。
sourceNode=sourceNode->next;//获取第二个节点的地址
chainNode<T>* targetNode=firstNode;//构建targetNode作为以下循环的中间变量
while (sourceNode!=NULL)
{
targetNode->next=new chainNode<T>(sourceNode->element);
targetNode=targetNode->next;
sourceNode=sourceNode->next;
}
targetNode->next=NULL;
}
以上为构造函数和复制构造函数,构造函数很简单我就不说了,复制构造函数就有点复杂了,得创建很多个新节点来容纳旧节点
的内容,首先获得旧节点的firstNode数据给sourceNode,获得这个数据后,旧链表就没用了。然后new一个新节点作为新链表的firstNode.然后不断new新节点将旧节点的数据写入,然后再前一个节点的next项写入新节点的地址,这样新链表就诞生了。这个过程我说的有点绕,仔细看还是能看懂的。所以说链表的复制构造非常麻烦,且浪费时间。
template<class T>
chain<T>::~chain()
{
while(firstNode!=NULL)
{
chainNode<T>* nextNode=firstNode->next;
delete firstNode;
firstNode=nextNode;
}
}
以上为析构函数,在删除第一个节点之前一定的先提出第二个节点的地址,否则将发生内存泄漏。
template<class T>
T& chain<T>::get(int theIndex)const
{
checkIndex(theIndex);
chainNode<T>*currentNode=firstNode;
for(int i=0;i<theIndexli++)
{
currentNode=currentNode->next;
}
return currentNode->element;
}
get函数必须得从第一个节点开始往后查找,效率非常低下。
template<class T>
T& 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;
}
indexOf函数一样得从头开始查找,速度也非常慢。
template<class T>
void chain<T>::erase(int theIndex)
{
checkIndex(theIndex);
chainNode<T>* deleteNode;
if(theIndex=0)
{
deleteNode=firstNode;
firstNode=firstNode->next;
}
else
{
chainNode<T>*p=firstNode;
for(int i=0;i<theIndex-1;i++)
p=p->next;
deleteNode=p-next;
p->next=p->next->next;
}
listSize--;
delete deleteNode;
}
template<class T>
void chain<T>::insert(int theIndex,const T& theElement)
{
checkIndex(theIndex);
if(theIndex==0)
firstNode=new chainNode(theElement,firstNode);
else
{
chainNode<T>* p=firstNode;
for(int i=0;i<theIndex-1;i++)
p=p->next;
p->next=new chainNode(theElement,p->next);//p->next在插入之前代表的是插入之后的那个地址。插入之后才是p->next->next.
}
listSize++;
}
以上就是删除和插入的代码,插入和删除都是一样的思想,它们分两种情况,第一种插入或者删除第一个节点内容,这时只需改变第一个firstNode的数据即可,第二种插在或者删除其他的节点,这时只需要改变插入或者删除节点的前一个节点的next数据和
插入节点的next数据即可,不过再删除节点后记得要析构掉数据,防止内存泄漏。
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;
}
以上为输出函数。
以上就是这个链表的基本功能了。