本文例程下載鏈接:ListDemo
鏈表 vs 數組
鏈表和數組的最大區別在於鏈表不支持隨機訪問,不能像數組那樣對任意一個(索引)位置的元素進行訪問,而需要從頭節點開始,一個一個往后訪問直到查找到目標位置。
單鏈表
與順序表相對,鏈表是一種鏈式存儲方式。單鏈表是實現順序表最簡單的一種鏈表,根據是否包含虛擬頭節點,分為含虛擬頭節點和不含虛擬頭節點兩種方式。本文以含虛擬頭節點為例,用C++實現單鏈表數據結構ADT。
節點:鏈表的組成元素,每個節點包含數據域data和指針域next。例程為了簡化設計,data用int類型表示。
虛擬頭節點:鏈表的第一個節點,為了維護鏈表操作方便,但不存放數據。
虛擬頭節點(dummy head) VS 無虛擬頭節點
含虛擬頭節點的鏈表優勢:所有的數據節點(除去頭節點)都是對等的,對鏈表節點的API操作一般不影響頭節點本身指針變化(除去頭節點next域)。
缺點:需要額外考慮頭節點影響,比如IsEmpty()判斷條件, 和Length()是否對等(IsEmpty <=> length==0?),頭節點對用戶是否可見,能否被用戶直接remove等。
單鏈表結構設計
含虛擬頭節點的單鏈表list如下圖所示,包含一個虛擬頭節點V0,而V1~Vn通過前驅節點next域進行鏈接,從而形成一個單向鏈式結構。
虛擬頭節點V0,不包含數據;V1~Vn包含數據,Vn->next = NULL
| 名稱 | 描述 | 代表自定義符號 |
| 虛擬頭節點 | 不包含數據 | Vh/head |
| 尾節點 | 鏈表的最后一個節點,特征:next=NULL | Vn-1/tail |
| 普通數據節點 | 包含具有實際意義數據的節點 | V0~Vn-1 |
| 長度 | 包含實際意義數據節點數為n(不包括Vh) | length |
| 空鏈表 | 虛擬頭節點為空,即head=NULL | V0=NULL |
| 位置 | 從V0開始(Vh后繼節點)開始計數0,直到Vn-1(尾節點)的節點對應位置。范圍[0,n-1] |
position |
| 插入節點 | 在指定位置處插入節點,除插入節點及前驅和后繼,不改變鏈表其他節點關系 | insert |
| 刪除節點 | 在指定位置處刪除節點, 除插入節點及前驅和后繼,不改變鏈表其他節點關系 | remove |

單鏈表ADT設計
1. 鏈表節點ADT Node.h
/**
* 單鏈表節點類
*/
class Node
{
int data;
Node *next;
public:
Node();
Node(Node*, int);
};
2. 單鏈表ADT LinkedList.h
/**
* 單鏈表類
* @description 帶虛擬頭節點
*/
class LinkedList
{
private:
Node *head;
Node *tail;
public:
LinkedList();
virtual ~LinkedList();
Status Init(); // 初始化鏈表
Status Destroy(); // 銷毀鏈表
int Length(); // 求鏈表長度(有效節點)節點個數
bool IsEmpty(); // 鏈表是否為空
Status Insert(int pos, int value); // 在指定位置生產節點, 並插入數據
Status Remove(int pos); // 刪除指定位置節點
int GetValue(int pos); // 讀取指定位置節點數據
Node* GetAddress(int pos); // 獲取知道位置節點的地址
int SearchPosition(int value); // 搜索第一個出現的元素值的節點位置
Status Update(int pos, int value); // 更新指定位置節點數據
Status ClearList(); // 清除鏈表數據(不包含頭節點)
Status PrintList(); // 順序打印鏈表節點數據
Status Reverse(); // 反轉鏈表
};
3. Node實現 Node.cpp
#include "Node.h"
#include <cstdlib>
Node::Node() {
data = 0;
next = NULL;
}
Node::Node(Node* newNext, int newValue)
{
data = newValue;
next = newNext;
}
Node::~Node()
{
// TODO Auto-generated destructor stub
}
4. 鏈表實現LinkedList.cpp
#include "LinkedList.h"
#include <cstdlib>
#include <iostream>
using namespace std;
LinkedList::LinkedList() {
head = NULL;
tail = NULL;
}
LinkedList::~LinkedList() {
Destroy();
}
/**
* 初始化鏈表
*/
Status LinkedList::Init()
{
// 創建虛擬頭節點
head = new Node(NULL, 0);
if(head != NULL)
{
return OK;
}
return ERROR;
}
/**
* 銷毀鏈表
* @description 與初始化操作相對, 刪除所有鏈表節點, 包括頭節點
*/
Status LinkedList::Destroy()
{
if(head)
{
Node *curP = head;
while(head)
{
curP = head->next;
delete head;
head = curP;
}
return OK;
}
else
{
cout <<"The list is NULL"<<endl;
return ERROR;
}
}
/**
* 求鏈表長度(有效節點)節點個數
* @description 從虛擬頭節點的下一個節點開始計算有效節點數
*/
int LinkedList::Length()
{
if(!head)
{
cout <<"The list is NULL"<<endl;
exit(-1);
}
Node *curP = head->next;
int n = 0;
while(curP != NULL)
{
n ++;
curP = curP->next;
}
return n;
}
/**
* 判斷鏈表是否為空
* @description 判斷依據: 虛擬頭節點head == NULL
* 注: 長度為0 不代表鏈表為空
*/
bool LinkedList::IsEmpty()
{
return (head == NULL);
}
/**
* 在指定位置生產節點, 並插入數據
* @param pos [in] 待插入位置. 從頭節點的后繼開始為0計數,所需要經過的節點數。范圍:0~n-1
* @param value [in] 待插入節點數據域
*/
Status LinkedList::Insert(int pos, int value)
{
if(IsEmpty())
{
cout <<"The list is NULL. Pls Create an List and init it first."<<endl;
exit(-1);
}
// create a new Node
Node *newNode = new Node(NULL, value);
if(pos == 0)
{
newNode->next = head->next;
head->next = newNode;
}
else if(pos >0 && pos <= Length())
{
// find the predecessor node to be inserted
Node *p = GetAddress(pos-1);
// insert the new Node to List
if(p != NULL)
{
if(p->next != NULL)
{// not tail
newNode->next = p->next;
}
p->next = newNode;
}
}
else
{
cout<<"Error: Input param Pos is illegal(<0)"<<endl;
return ERROR;
}
return OK;
}
/**
* 刪除指定位置節點
* @param pos [in] 待插入位置. pos有效范圍: [0, n), 0代表虛擬節點后面一個節點, 虛擬節點無法通過此API刪除
*
*/
Status LinkedList::Remove(int pos)
{
Node *preNode = NULL;
Node *curNode = NULL;
Node *nextNode = NULL;
if(pos < 0 || pos >= Length())
{
cout << "Remove node with error position"<<endl;
exit (-1);
}
else if(pos == 0)
{// find the prior node, namely , head node
preNode = head;
}
else if(pos < this->Length())
{
// find the prior node
preNode = GetAddress(pos - 1);
}
if(!preNode)
{
return ERROR;
}
curNode = preNode->next;
if(curNode)
{
nextNode = curNode->next;
preNode->next = nextNode;
delete curNode;
}
return OK;
}
/**
* 讀取指定位置節點數據
*/
int LinkedList::GetValue(int pos)
{
// find the node
Node *curP = GetAddress(pos);
if(curP != NULL)
{
return curP->data;
}
else
{
return 0;
}
}
/**
* 獲取位置節點的地址
* @param pos [in] 從頭節點的后繼開始為0計數,所需要經過的節點數。范圍:[0, n-1], n是鏈表長度(有效數據節點數)
* @return 節點的地址
*/
Node* LinkedList::GetAddress(int pos)
{
// valid list is null or not
if(!head)
{
cout <<"The list is NULL"<<endl;
exit(-1);
}
// valid intput param
if(pos < 0 || pos >= Length())
{
cout <<"Insert position is out of the list's bounds"<<endl;
exit(-1);
}
// 順序查找位置pos的節點
int i = 0;
Node *curP = head->next;
while(curP != NULL)
{
if(i == pos)
{
return curP;
}
i ++;
curP = curP->next;
}
return NULL;
}
/**
* 搜索第一個出現的元素值的節點位置
* @param value 待查找值
* @return 鏈表第一個節點的數據域=value的位置
* - ERROR 錯誤
* - >=0 位置序號
*/
int LinkedList::SearchPosition(int value)
{
// valid list is null or not
if(!head)
{
cout <<"The list is NULL"<<endl;
exit(-1);
}
// sequential search
Node *p = head->next;
int i = 0;
while(p != NULL)
{
i ++;
if(p->data == value)
return i;
else
p = p->next;
}
cout<< "Can't find the value in list"<<endl;
return ERROR;
}
/**
* 更新指定位置節點數據
* @param pos [in] 待更新節點位置
* @param value [in] 待更新節點要修改的目標值
* @return 更新結果
* - OK 正常更新
* - ERROR 沒有找到待更新節點, 可能由位置錯誤或者鏈表為空導致
*/
Status LinkedList::Update(int pos, int value)
{
// find the node
Node *curP = GetAddress(pos);
if(curP != NULL)
{
curP->data = value;
return OK;
}
return ERROR;
}
/**
* 清除鏈表數據(不包含頭節點)
*/
Status LinkedList::ClearList()
{
// valid list is null or not
if(!head)
{
cout <<"The list is NULL"<<endl;
exit(-1);
}
Node *p = head->next;
Node *tmp = NULL;
while(p!=NULL)
{
tmp = p;
p = p->next;
delete tmp;
}
head->next = NULL;
return OK;
}
/**
* 順序打印鏈表節點數據
*/
Status LinkedList::PrintList()
{
// valid list is null or not
if(IsEmpty())
{
cout <<"The list is NULL. Pls Create an List and init it first."<<endl;
return ERROR;
}
cout <<"List:[ ";
if(Length() == 0)
{
cout<<"NULL";
}
else
{
Node *p = head->next;
while(p != NULL)
{
cout<<p->data<<" ";
p = p->next;
}
}
cout <<"] "<<endl;
return OK;
}
/**
* 反轉鏈表, 除虛擬頭節點
*/
Status LinkedList::ReverseList()
{
// valid list is null or not
if(IsEmpty())
{
cout <<"The list is NULL. Pls Create an List and init it first."<<endl;
exit(-1);
}
if(Length() == 0)
{
cout <<"The list length = 0. Pls insert new Node."<<endl;
exit(-1);
}
// reverse List
Node *pre = head->next; // precursor Node
Node *cur = pre->next; // current Node
Node *nxt = NULL; // successor Node
while(cur != NULL)
{
nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
// set the tail Node
head->next->next = NULL;
// set the head Node's next field
head->next = pre;
return OK;
}
6. 測試結果
可以看到已經實現了鏈表基本的插入、打印、反轉等功能。

