数据结构(一)传到网上之后得到了很多人的关注和同学的支持,在此表示感谢。这种关注和支持也给我带来了更大的动力,让我觉得应该更好的学习,然后把所学的知识和经验拿出来供大家探讨。在一定程度上即帮助的他人,也在完善了自己。
在本篇文章中我继续介绍线性表,它是另一种线性储存结构------链表。链表虽然和数组同是线性存储结构,但二者存在着很大的差别。根本的差别在于链表不是用连续的地址空间来存储,因此就不能向数组一样用下标的形式来获取数据。它是用一个被称作“Next”指针来指向下一个存储元素,当然也可以指向上一个存储元素,这个指针被称作“Prev”。如果,不能用下标的形式来存储元素被认为是链表缺点的话,那么链表可以轻松的改变自己的存储能力则是它的优点。正是因为链表不是用连续的地址空间来存储元素而是用指针来标记上一个元素和下一个元素的位置,所以就可以通过改变“Next”指针和“Prev”指针指向元素的地址,来改变链表的结构。例如:a->b->c,这个链上存储了三个元素。现在想删除b,同时改变了链表的长度。于是,a->c,delete b,实现了我们的目的。
在链表的家族中包含了单链表、双向链表和循环链表。根据不同的需要和不同的场合我们可以选择使用不同的形式链表来满足我们的需求。
上面对链表进行了简单的说明,也许你还不是很清楚,那么来看看下面的代码你就会豁然开朗了。这里以双向链表为例。
首先,我们要定义一个节点类(ListElement)。这个类里面包含了三个成员变量,一个用来存储数据(datum),称为数据域;一个用来指向上一个节点的地址(pPrev),称为Prev域;一个用来指向下一个节点的地址(pNext),称为Next域。其他的成员函数只做了些简单的操作处理。
template <class T>
class LinkedList;
template <class T>
class ListElement
{
private:
T datum;
ListElement *pPrev;
ListElement *pNext;
public:
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
ListElement()
{
datum = 0;
pPrev = 0;
pNext = 0;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
ListElement(T const &_datum, ListElement *_prev,ListElement *_next)
{
datum = _datum;
pPrev = _prev;
pNext = _next;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
ListElement(ListElement const &pClass)
{
if((pClass!= this) && (pClass ! = NULL))
{
this->datum = pClass.datum;
this->pPrev = pClass.pPrev;
this->pNext = pClass.pNext;
}
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
T const &GetDatum() const
{
return datum;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
void SetDatun(T Value)
{
datum = Value;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
ListElement *GetNext() const
{
return pNext;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
void SetNext(ListElement * const pNew)
{
pNext = pNew;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
ListElement *GetPrev() const
{
return pPrev;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
void SetPrev(ListElement * const pNew)
{
pPrev = pNew;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
ListElement &operator = (const ListElement &listElement)
{
if(&listElement != this)
{
this->datum = listElement.datum;
this->pPrev = listElement.pPrev;
this->pNext = listElement.pNext;
return *this;
}
return *this;
}
// friend LinkedList<T>;
};
LinkedList就是我们要用到的链表类。这个类会把每个节点穿起来,最终组成一个完整的链。我们让它成为ListElement类的友元,目的是为了能更好得更快捷的访问数据域、Prev域和Next域。
template <class T>
class LinkedList
{
private:
ListElement<T> *pHead;
ListElement<T> *pTail;
int count;
public:
LinkedList():pHead(0),pTail(0),count(0){}
LinkedList(ListElement<T> *_head,ListElement<T> *_tail)
{
pHead = _head;
pTail = _tail;
count = 0;
}
~LinkedList()
{
Purge();
}
LinkedList(const LinkedList &);
LinkedList &operator = (LinkedList const&);
ListElement<T> const *GetHead() const;
ListElement<T> const *GetTail() const;
ListElement<T> *GetElement(int pos);
bool IsEmpty() const;
ListElement<T> *GetFirst() const;
ListElement<T> *GetLast() const;
void Prepend(T const);
void Append(T const);
void DelElement(int pos);
void RemoveAt(T const&);
void Purge();
void InserAfter(ListElement<T> const*, T const&);
void InserBefore(ListElement<T> const*, T const&);
void SetCountPlus();
void SetCountSub();
int GetLength();
};
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template<class T>
inline void LinkedList<T>::Purge()
{
while(0 != pHead)
{
ListElement<T> *const pTemp = pHead;
pHead = pHead->GetNext();
SetCountSub();
delete pTemp;
}
pTail = 0;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline ListElement<T> *LinkedList<T>::GetFirst() const
{
if(0 == pHead)
{
try
{
throw domain_error( "List is empty!" );
}
catch (exception &e)
{
cerr << "Caught: " << e.what() << endl;
}
}
return pHead;//->GetDatum();
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline ListElement<T> *LinkedList<T>::GetLast() const
{
if(0 == pTail)
{
try
{
throw std::domain_error("List is empty");
}
catch (exception &e)
{
cerr << "Caught: " << e.what() << endl;
}
}
return pTail->GetDatum();
}
/*****************************************************************************
* Function Explain:first insert
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::Prepend(T const item)
{
ListElement<T> *pTemp = new ListElement<T>(item,0,pHead);
if(0 == pHead)
{
pTail = pTemp;
}
pHead = pTemp;
pTemp = NULL;
SetCountPlus();
}
/*****************************************************************************
* Function Explain:Last insert
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::Append(T const item)
{
ListElement<T> *pTemp = new ListElement<T>(item,pTail,0);
if(NULL == pHead)
{
pHead = pTemp;
}
else
{
pTail->SetNext(pTemp);
}
pTail = pTemp;
pTemp = NULL;
SetCountPlus();
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline LinkedList<T>::LinkedList(const LinkedList<T> &linkedList):pHead(0),pTail(0)
{
ListElement<T> const *ptr;
for(ptr = linkedList.pHead; ptr != 0; ptr = ptr->GetNext())
{
Append(ptr->GetDatum());
}
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline LinkedList<T> &LinkedList<T>::operator = (LinkedList<T> const &linkedList)
{
if(&linkedList != this)
{
Purge();
ListElement<T> const *ptr;
for(ptr = linkedList.pHead; ptr != 0; ptr = ptr->GetNext())
{
Append(ptr->GetDatum());
}
}
return *this;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::RemoveAt(T const &item)
{
ListElement<T> *ptr = pHead;
ListElement<T> *prevPtr = 0;
while((ptr != 0)&&(ptr->GetDatum() != item))
{
prevPtr = ptr;
ptr = ptr->GetNext();
}
if(0 == ptr)
{
throw std::invalid_argument("item not found");
}
if(ptr == pHead)
{
pHead->GetNext()->SetPrev(NULL);
pHead = ptr->GetNext();
}
else
{
ptr->GetNext()->SetPrev(prevPtr);
prevPtr->SetNext(ptr->GetNext());
}
if(ptr == pTail)
{
pTail = prevPtr;
prevPtr->SetNext(NULL);
}
SetCountSub();
delete ptr;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::InserAfter(ListElement<T> const *arg, T const &item)
{
ListElement<T> *ptr = const_cast<ListElement<T>*>(arg);
if(0 == ptr)
{
throw std::invalid_argument("invalid position");
}
//ListElement<T> *const pTemp = new ListElement<T>(item,ptr->GetNext(),0);
ListElement<T> *const pTemp = new ListElement<T>(item,ptr,ptr->GetNext());
ptr->SetNext(pTemp);
ptr->GetNext()->SetPrev(pTemp);
if(pTail == ptr)
{
pTail = pTemp;
}
SetCountPlus();
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::InserBefore(ListElement<T> const *arg, T const &item)
{
ListElement<T> *ptr = const_cast<ListElement<T>*>(arg);
if(0 == ptr)
{
throw std::invalid_argument("invalid position");
}
ListElement<T> *const pTemp = new ListElement<T>(item, ptr->GetPrev(), ptr);
if(pHead == ptr)
{
pHead->SetPrev(pTemp);
pHead = pTemp;
}
else
{
ptr->GetPrev()->SetNext(pTemp);
ptr->SetPrev(pTemp);
}
SetCountPlus();
}
/*****************************************************************************
* Function Explain:Get the element which is designated.
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline ListElement<T> *LinkedList<T>::GetElement(int pos)
{
try
{
if((pos < 0) || (pos > GetLength()))
{
throw std::domain_error("List is empty");
}
ListElement<T> *prevPtr = pHead;
for(int i = 1; i < pos; i++)
{
prevPtr = prevPtr->GetNext();
}
return prevPtr;
}
catch(exception &e)
{
cerr << "Caught: " << e.what() << endl;
return NULL;
}
}
/*****************************************************************************
* Function Explain:Delete the element which is designated.
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::DelElement(int pos)
{
ListElement<T> *tempElement = 0;//new ListElement<T>(0,0,0);
tempElement = GetElement(pos);
if((NULL == tempElement->GetPrev())&&(NULL != tempElement->GetNext()))
{
tempElement->GetNext()->SetPrev(0);
pHead = tempElement->GetNext();
}
else if((NULL != tempElement->GetPrev())&&(NULL == tempElement->GetNext()))
{
tempElement->GetPrev()->SetNext(0);
pTail = tempElement->GetPrev();
}
else
{
tempElement->GetPrev()->SetNext(tempElement->GetNext());
tempElement->GetNext()->SetPrev(tempElement->GetPrev());
}
SetCountSub();
delete tempElement;
}
/*****************************************************************************
* Function Explain:
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline bool LinkedList<T>::IsEmpty() const
{
if(0 == pHead)
{
return true;
}
else
{
return false;
}
}
/*****************************************************************************
* Function Explain:
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::SetCountPlus()
{
++count;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline void LinkedList<T>::SetCountSub()
{
--count;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline int LinkedList<T>::GetLength()
{
return count;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline ListElement<T> const *LinkedList<T>::GetHead() const
{
return pHead;
}
/*****************************************************************************
* Function Explain:
*
* Input Parameter:
* Output Parameter:
* Return Values:
*****************************************************************************/
template <class T>
inline ListElement<T> const *LinkedList<T>::GetTail() const
{
return pTail;
}
原来我把LinkedList<T>声明成ListElement<T>的友元。这样在LinkedList<T>中可以直接的访问私有成员。但是,后来我把这行代码注释掉了。觉得这样做虽然很方便,但不是很安全。于是采用了Get、Set的方法来修改ListElement<T>中的私有成员,这也就相当于作了一个接口。
在LinkedList<T>类中提供了一些方法。这些方法可能不实用,也可能还存在着Bug。但我只想通过它们给大家提供一个思路,如果你看了后能从领悟的一些东西的话,我已经觉得很欣慰了。