首先介绍一下C++中class
和struct
的区别吧
struct
默认访问权限和默认继承方式都是public
,对外部都是可见的,方便访问,如果用class
还得设置public
class
都是private
struct
一般用于简单的数据结构,用于打包一些数据成员
class
一般用于封装行为和数据的复杂类型,有继承、多态等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template <typename T> class myList{ private: struct Node{ T data; Node* next; Node* prev;
Node(const T& data_, Node* next_ = nullptr, Node* prev_ = nullptr) : data(data_), next(next_), prev(prev_) {} }; Node* head; Node* tail; size_t current_size;
public:
};
|
-
每个节点都是一个结构体,有三样东西
data
是节点的数据,类型为T
next
是一个指针,指向下一个节点
prev
是一个指针,指向上一个节点
-
struct
里面也有构造函数,进行了初始化
-
还有着头节点head
和尾部节点prev
1 2 3 4 5
| myList() : head(nullptr), tail(nullptr), current_size(0) {} ~myList(){ clear(); }
|
同样有构造函数和析构函数,clear
下面实现
接下来就是功能的编写了
push_back
的实现
1 2 3 4 5 6 7 8
| void push_back(const T& value){ Node* temp = new Node(value, nullptr, tail); if (tail) tail -> next = temp; else head = temp;
tail = temp; current_size++; }
|
-
首先为传进来的值创建一个节点,这个节点的next
指针肯定是空的(因为新加进来的),prev
指针,肯定指向之前链表的尾部
-
这个节点的前后指针处理好了,就要考虑之前链表尾部的节点了,也要处理一下这个节点的前后指针
- 如果之前是有节点的,那么就把这个节点的
next
指针也指向新加进来的节点,prev
指针已经在创建节点到时候就弄好了
- 如果之前根本没有节点,也就是
tail
是空的,那么头节点也就是最新加进来的节点
-
然后就是尾部节点要更新为最新加进来的节点
-
尺寸+1
-
这边注意不能delete temp
,这样的话会释放这个节点对应的内存空间,就会破坏链表结构了,但是可以把temp置空,因为temp本来就是一个临时的,但其他除了函数他就会自己销毁,加不加都一样
pop_back
和pop_front
的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void pop_back(){ Node* temp = tail -> prev; delete tail; tail = temp;
if (tail) tail -> next = nullptr; else head = nullptr; current_size--; }
void pop_front(){ Node* temp = head -> next; delete head; head = temp;
if (head) head -> prev = nullptr; else tail = nullptr; current_size--; }
|
这边讲其中一个就行,原理都差不多
- 首先就是直接删除了尾部节点,并得到新的尾部节点
tail
- 接下去就开始处理新尾部节点的指针问题
- 然后看看弹出的前一个节点是否存在
- 如果存在,就把新尾部节点的
next
指针 为空
- 如果不存在,说明链表中已经没有节点了,头节点指针 置空
- 尺寸-1
size
获取长度
1 2 3 4
| size_t size(){ return current_size; }
|
clear
清空list
1 2 3 4 5 6 7 8 9
| void clear(){ while (head){ Node* temp = head; head = head -> next; delete temp; } tail = nullptr; current_size = 0; }
|
- 需要考虑节点占用的内存,所以要循环释放,不能直接
current_size = 0
- 从前往后删除,处理完头节点之后记得把尾节点指针也置空
- 尺寸为0
重写[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| T& operator[](size_t index) { if (index >= current_size) throw std::out_of_range("越界访问");
Node* temp = head; for (size_t i = 0; i < index; i++) { temp = temp->next; } return temp->data; }
const T& operator[](size_t index) const { if (index >= current_size) throw std::out_of_range("越界访问");
Node* temp = head; for (size_t i = 0; i < index; i++) { temp = temp->next; } return temp->data; }
|
empty
检测链表是否为空
1 2 3
| bool empty(){ return current_size == 0; }
|
find
函数是找值所对应节点的数据的地址
1 2 3 4 5 6 7 8 9
| T* find(const T& val){ Node* temp = head; while (temp && temp -> data != val){ temp = temp -> next; } if (temp) return &temp -> data; else return nullptr; }
|
- 就是循环遍历直到找到那个值对应的第一个节点,然后返回节点数据的指针,也就是地址
remove
函数的实现,去除值为val
的第一个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void remove(const T& val){ Node* temp = head; while (temp && temp -> data != val){ temp = temp -> next; } if(!temp) return; if (temp != head && temp != tail){ temp -> prev -> next = temp -> next; temp -> next -> prev = temp -> prev; } else if (temp == head && temp == tail){ head = nullptr; tail = nullptr; } else if (temp == head){ head = head -> next; head -> prev = nullptr; } else { tail = tail -> prev; tail -> next = nullptr; } delete temp; temp = nullptr; current_size--; }
|
- 首先要先找到这个节点,找不到就直接
return
- 然后要删除这个节点的时候,要分四种情况
- 如果是中间节点的话,要处理好这个节点的前节点和后节点的指针关系
- 如果只有一个节点的情况下(即是头节点,又是尾节点),要把头尾指针置空
- 如果只是头节点,处理好第二个节点的
prev
指针,同时更新头节点
- 如果只是尾节点,处理好倒二个节点的
next
指针,同时更新尾节点
- 最后释放
temp
指向的内存空间(delete
释放,但实际上temp
仍指向这块内存),并且把这个指针置空(可选,防止悬挂指针)
- 尺寸-1
然后是迭代器接口的编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Node* begin(){ return head; }
Node* end(){ return nullptr; }
const Node* begin() const { return head; }
const Node* end() const { return nullptr; }
|
那肯定还是比不上标准的STL库,完整代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
| #include <stdexcept> #include <cstddef> #include <algorithm> using namespace std;
template <typename T> class myList{ private: struct Node{ T data; Node* next; Node* prev;
Node(const T& data_, Node* next_ = nullptr, Node* prev_ = nullptr) : data(data_), next(next_), prev(prev_) {} }; Node* head; Node* tail; size_t current_size;
public: myList() : head(nullptr), tail(nullptr), current_size(0) {} ~myList(){ clear(); }
void push_back(const T& value){ Node* temp = new Node(value, nullptr, tail); if (tail) tail -> next = temp; else head = temp;
tail = temp; current_size++; }
void pop_back(){ Node* temp = tail -> prev; delete tail; tail = temp;
if (tail) tail -> next = nullptr; else head = nullptr; current_size--; }
void pop_front(){ Node* temp = head -> next; delete head; head = temp;
if (head) head -> prev = nullptr; else tail = nullptr; current_size--; }
size_t size(){ return current_size; }
void clear(){ while (head){ Node* temp = head; head = head -> next; delete temp; } tail = nullptr; current_size = 0; }
T& operator[](size_t index) { if (index >= current_size) throw std::out_of_range("越界访问");
Node* temp = head; for (size_t i = 0; i < index; i++) { temp = temp->next; } return temp->data; }
const T& operator[](size_t index) const { if (index >= current_size) throw std::out_of_range("越界访问");
Node* temp = head; for (size_t i = 0; i < index; i++) { temp = temp->next; } return temp->data; }
bool empty(){ return current_size == 0; }
T* find(const T& val){ Node* temp = head; while (temp && temp -> data != val){ temp = temp -> next; } if (temp) return &temp -> data; else return nullptr; }
void remove(const T& val){ Node* temp = head; while (temp && temp -> data != val){ temp = temp -> next; } if(!temp) return; if (temp != head && temp != tail){ temp -> prev -> next = temp -> next; temp -> next -> prev = temp -> prev; } else if (temp == head && temp == tail){ head = nullptr; tail = nullptr; } else if (temp == head){ head = head -> next; head -> prev = nullptr; } else { tail = tail -> prev; tail -> next = nullptr; } delete temp; temp = nullptr; current_size--; }
Node* begin(){ return head; }
Node* end(){ return nullptr; }
const Node* begin() const { return head; }
const Node* end() const { return nullptr; }
};
|
测试用例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <iostream> #include <string>
int main() { myList<int> lst;
std::cout << "== 测试 push_back ==" << std::endl; lst.push_back(10); lst.push_back(20); lst.push_back(30);
for (auto p = lst.begin(); p; p = p->next) { std::cout << p->data << " "; } std::cout << std::endl;
std::cout << "== 测试 pop_back ==" << std::endl; lst.pop_back();
for (auto p = lst.begin(); p; p = p->next) { std::cout << p->data << " "; } std::cout << std::endl;
std::cout << "== 测试 pop_front ==" << std::endl; lst.pop_front();
for (auto p = lst.begin(); p; p = p->next) { std::cout << p->data << " "; } std::cout << std::endl;
std::cout << "== 测试 operator[] ==" << std::endl; try { std::cout << "lst[0] = " << lst[0] << std::endl; std::cout << "lst[1] = " << lst[1] << std::endl; } catch (const std::out_of_range& e) { std::cout << "异常捕获:" << e.what() << std::endl; }
std::cout << "== 测试 find ==" << std::endl; int* p = lst.find(20); if (p) std::cout << "找到元素: " << *p << std::endl; else std::cout << "未找到元素" << std::endl;
std::cout << "== 测试 remove ==" << std::endl; lst.push_back(40); lst.push_back(50); lst.remove(40); lst.remove(20); lst.remove(50);
for (auto p = lst.begin(); p; p = p->next) { std::cout << p->data << " "; } std::cout << std::endl;
std::cout << "== 测试 clear ==" << std::endl; lst.clear(); std::cout << "是否为空: " << (lst.empty() ? "是" : "否") << std::endl;
return 0; }
|
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| == 测试 push_back == 10 20 30 == 测试 pop_back == 10 20 == 测试 pop_front == 20 == 测试 operator[] == lst[0] = 20 lst[1] = 异常捕获:越界访问 == 测试 find == 找到元素: 20 == 测试 remove ==
== 测试 clear == 是否为空: 是
|