1.基本概念
鏈表 (Linked List)是一種線性表,但是在內存中不是按照線性的順序儲存數據,是通過每個節點的指針指向下一個節點的指針來鏈接。相對於順序儲存(例如數組),鏈表的插入操作更快( O(1) ),但是失去了隨機讀取的優點。
鏈表一般有單向鏈表,雙向鏈表,循環鏈表這三種形式。
2.單向鏈表
該種形式是鏈表中最簡單的,每個節點包含了數據域和指針域,數據域用來保存該節點的值,指針域用來指向下一個節點(鏈表的尾巴節點應指向NULL),它保存的是下一個節點的地址。
一般完整的鏈表會有頭節點,頭節點不儲存數據。查找數據的話要從頭節點開始,每次訪問下一個節點直到查找到數據或到達尾部。
單向鏈表典型的插入方法有頭插法和尾插法,頭插法只需要一個頭指針,而尾插法還需要一個指向尾部的尾指針。
單向鏈表的C++實現:

template<class T> struct Node // 節點的數據結構 { T data; Node *next; Node() :next(nullptr){} // 無參構造 Node(T t) :data(t), next(nullptr){} // 帶參數構造 }; template<class T> class LinkList //鏈表類 { private: Node<T> *head; // 頭指針 (空節點) Node<T> *tail; // 尾指針 (空節點) int size; public: LinkList() { head = new Node<T>; tail = new Node<T>; size = 0; } ~LinkList() { } public: /** * 在鏈表的頭部插入新節點 * @param val:賦給新節點的值 */ void insertOnHead(T val); /** * 在鏈表的尾部插入新節點 * @param val:賦給新節點的值 */ void insertOnTail(T val); /** * 在鏈表的某一位置插入新節點,插入成功返回true,否則返回false * @param i:指定的位置 * @param val:賦給新節點的值 */ bool insert(int i,T val); /** * 獲取某節點的值,獲取成功返回true,否則返回false * @param i:指定的位置 * @param val:賦給新節點的值 */ bool getData(int i,T &val); /** * 判斷空,空的話返回true,否則返回false */ bool isEmpty(); /** * 清空鏈表 */ void clear(); /** * 打印鏈表 */ void printList(); }; template<class T> void LinkList<T>::insertOnHead(T val) { Node<T> *newNode = new Node<T>; newNode->data = val; newNode->next = head->next; head->next = newNode; if (!size) tail->next = newNode; // 把尾指針指向第一個節點 ++size; } template<class T> void LinkList<T>::insertOnTail(T val) { Node<T> *newNode = new Node<T>; newNode->data = val; newNode->next = nullptr; tail->next->next = newNode; tail->next = newNode; ++size; } template<class T> bool LinkList<T>::insert(int i,T val) { if (i <= 0|| i > size) return false; Node<T> *newNode = new Node<T>; Node<T> *temp = head; if (i == size) tail->next = newNode; // 如果在最后插入,更新尾指針 for (int j = 0; j <= size; ++j) { if (i != 0) { temp = temp->next; --i; } else { newNode->data = val; newNode->next = temp->next; temp->next = newNode; break; } } ++size; return true; } template<class T> bool LinkList<T>::getData(int i, T &val) { if (i <= size && i > 0) { Node<T> *temp = head; for (int j = 0; j < i; ++j) { temp = temp->next; } val = temp->data; return true; } else { return false; } } template<class T> bool LinkList<T>::isEmpty() { return size ? false : true; } template<class T> void LinkList<T>::clear() { Node<T> *tempFront = head->next; // 指向下一個待刪除 Node<T> *tempBack = head->next; // 指向待刪除的元素 for (int i = 0; i < size; ++i) { tempBack = tempFront; tempFront = tempFront->next; delete tempBack; } size = 0; } template<class T> void LinkList<T>::printList() { if (!size) { cout << "empty link" << endl; return; } Node<T> * temp = head->next; for (int i = 0; i < size; ++i) { cout << temp->data << endl; temp = temp->next; } }
2.雙向鏈表
和單向鏈表類似,不過雙向鏈表的每個節點包含一個數據域和兩個指針域,一個前向指針和一個后向指針,相對單鏈表的優點是可以訪問前驅而不用從頭節點開始,其結構如下圖:
其實鏈表的操作是相似的,都是對節點的連接和斷開與銷毀,不過雙向鏈表需要注意的是增加了對前向指針的操作,新增節點的圖示如下:
雙向鏈表插入的C++示例如下,完整代碼見GitHub。
bool insert(int i,T val) { if (i <= 0|| i > size) return false; Node *newNode = new Node; Node *temp = head; if (i == size) tail->next = newNode; // 如果在最后插入,更新尾指針 for (int j = 0; j <= size; ++j) { if (i != 0) { temp = temp->next; --i; } else { newNode->data = val; newNode->pre = temp; newNode->next = temp->next; temp->next = newNode; break; } } ++size; return true; }