采用模板類實現的好處是,不用拘泥於特定的數據類型。就像活字印刷術,制定好模板,就可以批量印刷,比手抄要強多少倍!
此處不具體介紹泛型編程,還是着重敘述鏈表的定義和相關操作。
鏈表結構定義
定義單鏈表的結構可以有4方式。如代碼所示。
本文采用的是第4種結構類型
1、復合類:在Node類中定義友元的方式,使List類可以訪問結點的私有成員
************************************************************************ */
class LinkNode
{
friend class LinkList;
private:
int data;
LinkNode *next;
};
class LinkList
{
public:
// 單鏈表具體操作
private:
LinkNode *head;
};
/* ************************************************************************
2、嵌套類:在List內部定義Node類,但是Node的數據成員放在public部分,使List
和Node均可以直接訪問Node的成員
************************************************************************ */
class LinkList
{
public:
// 單鏈表具體操作
private:
class LinkNode
{
public:
int data;
LinkNode *next;
};
LinkNode *head;
};
/* ************************************************************************
3、繼承:在Node類中把成員定義為protected,然后讓List繼承Node類,這樣就可以
訪問Node類的成員了。
************************************************************************ */
class LinkNode
{
protected:
int data;
LinkNode *next;
};
class LinkList : public LinkNode
{
public:
// 單鏈表具體操作
private:
LinkNode *head;
};
/* ************************************************************************
4、直接用struct定義Node類,因為struct的成員默認為公有數據成員,所以可直接
訪問(struct也可以指定保護類型)。
************************************************************************ */
struct LinkNode
{
int data;
LinkNode *next;
};
class LinkList
{
public:
// 單鏈表具體操作
private:
LinkNode *head;
};
單鏈表的模板類定義
使用模板類需要注意的一點是template<class T>必須定義在同一個文件,否則編譯器會無法識別。
如果在.h中聲明類函數,但是在.cpp中定義函數具體實現, 會出錯。所以,推薦的方式是直接在.h中定義。
template< class T>
struct LinkNode
{
T data;
LinkNode<T> *next;
LinkNode(LinkNode<T> *ptr = NULL){next = ptr;}
LinkNode( const T &item, LinkNode<T> *ptr = NULL)
// 函數參數表中的形參允許有默認值,但是帶默認值的參數需要放后面
{
next = ptr;
data = item;
}
};
/* 帶頭結點的單鏈表定義 */
template< class T>
class LinkList
{
public:
// 無參數的構造函數
LinkList(){head = new LinkNode<T>;}
// 帶參數的構造函數
LinkList( const T &item){head = new LinkNode<T>(item);}
// 拷貝構造函數
LinkList(LinkList<T> &List);
// 析構函數
~LinkList(){Clear();}
// 重載函數:賦值
LinkList<T>& operator=(LinkList<T> &List);
// 鏈表清空
void Clear();
// 獲取鏈表長度
int Length() const;
// 獲取鏈表頭結點
LinkNode<T>* GetHead() const;
// 設置鏈表頭結點
void SetHead(LinkNode<T> *p);
// 查找數據的位置,返回第一個找到的滿足該數值的結點指針
LinkNode<T>* Find(T &item);
// 定位指定的位置,返回該位置上的結點指針
LinkNode<T>* Locate( int pos);
// 在指定位置pos插入值為item的結點,失敗返回false
bool Insert(T &item, int pos);
// 刪除指定位置pos上的結點,item就是該結點的值,失敗返回false
bool Remove( int pos, T &item);
// 獲取指定位置pos的結點的值,失敗返回false
bool GetData( int pos, T &item);
// 設置指定位置pos的結點的值,失敗返回false
bool SetData( int pos, T &item);
// 判斷鏈表是否為空
bool IsEmpty() const;
// 打印鏈表
void Print() const;
// 鏈表排序
void Sort();
// 鏈表逆置
void Reverse();
private:
LinkNode<T> *head;
};
定位位置
/* 返回鏈表中第pos個元素的地址,如果pos<0或pos超出鏈表最大個數返回NULL */ template<class T> LinkNode<T>* LinkList<T>::Locate(int pos) { int i = 0; LinkNode<T> *p = head; if (pos < 0) return NULL; while (NULL != p && i < pos) { p = p->next; i++; } return p; }
插入結點
單鏈表插入結點的處理如圖
圖:單鏈表插入操作
要在p結點后插入一個新結點node,(1)要讓node的next指針指向p的next結點;(2)再讓p的next指向node結點(即斷開圖中的黑色實線,改成紅色虛線指向node)
接下來:node->next = p->next; p->next = node;
template<class T> bool LinkList<T>::Insert(T &item, int pos) { LinkNode<T> *p = Locate(pos); if (NULL == p) return false; LinkNode<T> *node = new LinkNode<T>(item); if (NULL == node) { cerr << "分配內存失敗!" << endl; exit(1); } node->next = p->next; p->next = node; return true; }
刪除結點
刪除結點的處理如圖:
圖:單鏈表刪除
刪除pos位置的結點,如果這個位置不存在結點,則返回false;
如果找到對應結點,則通過實參item輸出要刪除的結點的數值, 然后刪除結點並返回true。
template<class T> bool LinkList<T>::Remove(int pos, T &item) { LinkNode<T> *p = Locate(pos); if (NULL == p || NULL == p->next) return false; LinkNode<T> *del = p->next; p->next = del->next; item = del->data; delete del; return true; }
清空鏈表
遍歷整個鏈表,每次head結點的next指針指向的結點,直到next指針為空。
最后保留head結點。
template<class T> void LinkList<T>::Clear() { LinkNode<T> *p = NULL; //遍歷鏈表,每次都刪除頭結點的next結點,最后保留頭結點 while (NULL != head->next) { p = head->next; head->next = p->next; //每次都刪除頭結點的next結點 delete p; } }
求鏈表長度和打印鏈表
着兩個功能的實現非常相近,都是遍歷鏈表結點,不贅述。
template<class T> void LinkList<T>::Print() const { int count = 0; LinkNode<T> *p = head; while (NULL != p->next) { p = p->next; std::cout << p->data << " "; if (++count % 10 == 0) //每隔十個元素,換行打印 cout << std::endl; } } template<class T> int LinkList<T>::Length() const { int count = 0; LinkNode<T> *p = head->next; while (NULL != p) { p = p->next; ++count; } return count; }
單鏈表倒置
單鏈表的倒置處理如圖:

圖:單鏈表倒置
(1)初始狀態:prev = head->next; curr = prev->next;
(2)讓鏈表的第一個結點的next指針指向空
(3)開始進入循環處理,讓next指向curr結點的下一個結點;再讓curr結點的next指針指向prev。即:next = curr->next; curr->next = prev;
(4)讓prev、curr結點都繼續向后移位。即:prev = curr; curr = next;
(5)重復(3)、(4)動作,直到curr指向空。這時循環結束,讓haed指針指向prev,此時的prev是倒置后的第一個結點。即:head->next = prev;
template<class T> void LinkList<T>::Reverse() { LinkNode<T> *pre = head->next; LinkNode<T> *curr = pre->next; LinkNode<T> *next = NULL; head->next->next = NULL; while (curr) { next = curr->next; curr->next = pre; pre = curr; curr = next; } head->next = pre; }
