循環單鏈表與普通單鏈表的關鍵區別在於:尾節點的 next 指針指向頭哨兵節點,形成閉環結構。這帶來以下變化:
first->next == first(而非 nullptr)node != first(而非 node != nullptr)#ifndef MYLIST_H_INCLUDED
#define MYLIST_H_INCLUDED
#include <iostream>
#include <stdexcept>
using namespace std;
// 前向聲明
template <typename Type> class MyList;
template <typename Type> class ListIterator;
// 節點定義
template <typename Type>
class Node {
friend class MyList<Type>;
friend class ListIterator<Type>;
template <typename T>
friend ostream &operator<<(ostream &os, const MyList<T> &list);
private:
Node(const Type &dataValue) : data(dataValue), next(nullptr) {}
Type data;
Node *next;
};
// 循環鏈表類
template <typename Type>
class MyList {
template <typename T>
friend ostream &operator<<(ostream &os, const MyList<T> &list);
friend class ListIterator<Type>;
public:
MyList();
~MyList();
void insertFront(const Type &data);
void insert(const Type &data, int index);
void remove(const Type &data);
bool isEmpty() const;
private:
Node<Type> *first; // 頭哨兵節點
};
// --- 成員函數實現 ---
template <typename Type>
MyList<Type>::MyList() {
first = new Node<Type>(0);
first->next = first; // 關鍵:指向自己形成環
}
template <typename Type>
MyList<Type>::~MyList() {
Node<Type> *tmp = first;
first = first->next;
while (first != tmp) {
Node<Type> *del = first;
first = first->next;
delete del;
}
delete tmp; // 釋放頭哨兵
}
template <typename Type>
void MyList<Type>::insertFront(const Type &data) {
Node<Type> *newNode = new Node<Type>(data);
newNode->next = first->next;
first->next = newNode;
}
template <typename Type>
void MyList<Type>::insert(const Type &data, int index) {
int count = 1;
Node<Type> *searchNode = first;
// 終止條件改為 first
while (count < index && searchNode->next != first) {
++count;
searchNode = searchNode->next;
}
Node<Type> *newNode = new Node<Type>(data);
newNode->next = searchNode->next;
searchNode->next = newNode;
}
template <typename Type>
void MyList<Type>::remove(const Type &data) {
if (isEmpty()) return;
Node<Type> *previous = first;
for (Node<Type> *searchNode = first->next;
searchNode != first; // 終止條件
searchNode = searchNode->next) {
if (searchNode->data == data) {
previous->next = searchNode->next;
delete searchNode;
if (previous->next == first) break; // 到達尾部
searchNode = previous->next;
}
previous = searchNode;
}
}
template <typename Type>
bool MyList<Type>::isEmpty() const {
return first->next == first;
}
// 輸出運算符
template <typename Type>
ostream &operator<<(ostream &os, const MyList<Type> &list) {
for (Node<Type> *p = list.first->next; p != list.first; p = p->next) {
os << p->data;
if (p->next != list.first) cout << " -> ";
}
return os;
}
// ListIterator 適配:僅修改 isEmpty() 判斷邏輯
template <typename Type>
class ListIterator {
public:
ListIterator(const MyList<Type> &_list)
: list(_list), currentNode((_list.first)->next) {}
const Type &operator*() const throw(std::out_of_range);
Type &operator*() throw(std::out_of_range);
const Node<Type> *operator->() const throw(std::out_of_range);
Node<Type> *operator->() throw(std::out_of_range);
ListIterator &operator++() throw(std::out_of_range);
ListIterator operator++(int) throw(std::out_of_range);
bool isEmpty() const;
private:
const MyList<Type> &list;
Node<Type> *currentNode;
};
template <typename Type>
bool ListIterator<Type>::isEmpty() const {
return currentNode == list.first; // 判斷是否回到頭哨兵
}
// 其他運算符實現與單鏈表版本一致,此處省略
// ...
#endif
int main() {
MyList<int> myList;
for (int i = 0; i < 10; ++i) myList.insert(i+1, i+1); // 1~10
for (int i = 0; i < 5; ++i) myList.insertFront(i*10); // 40 30 20 10 0 ...
myList.insertFront(100); // 100 40 30 20 10 0 1 2 3 4 5 6 7 8 9 10
myList.remove(10);
myList.remove(8);
cout << "------------ MyList ------------" << endl;
for (ListIterator<int> iter(myList); !iter.isEmpty(); ++iter)
cout << *iter << ' ';
cout << endl;
cout << "Test: " << myList << endl;
return 0;
}
循環單鏈表通過將尾節點指向頭哨兵,消除了 nullptr 判斷,使得尾部操作更加統一。雖然單獨使用性能提升不明顯,但結合雙向鏈表(下一篇將介紹)可實現 O(1) 尾部插入刪除,性能提升顯著。本文作為承上啟下的過渡,重點展示了從單鏈表到循環鏈表的關鍵修改點。