先直接上代码:
#include <iostream>
using std::cout;
using std::endl;
struct Node {
Node(int data = 0, Node *pre = nullptr, Node *next = nullptr)
: _data(data)
, _pre(pre)
, _next(next) {
cout << "Node(int = 0, Node * = nullptr, Node * = nullptr) " << endl;
}
~Node() {
cout << "~Node()" << endl;
}
int _data;
Node *_pre;
Node *_next;
};
class List {
public:
List()
: _head(new Node())
, _tail(new Node())
, _size(0) {
cout << "List()" << endl;
_head->_next = _tail;
_tail->_pre = _head;
}
//在头部进行插入
void push_front(int data) {
Node *newNode = new Node(data);
newNode->_pre = _head;
newNode->_next = _head->_next;
_head->_next->_pre = newNode;
_head->_next = newNode;
++_size;
}
//在尾部进行插入
void push_back(int data) {
Node *newNode = new Node(data);
newNode->_pre = _tail->_pre;
newNode->_next = _tail;
_tail->_pre->_next = newNode;
_tail->_pre = newNode;
++_size;
}
//在头部进行删除
void pop_front() {
if (size() > 0) {
Node *pNode = _head->_next;
pNode->_next->_pre = _head;
_head->_next = pNode->_next;
delete pNode;
pNode = nullptr;
--_size;
} else {
cout << "该链表为空,无法删除" << endl;
return;
}
}
//在尾部进行删除
void pop_back() {
if (size() > 0) {
Node *pNode = _tail->_pre;
pNode->_pre->_next = _tail;
_tail->_pre = pNode->_pre;
delete pNode;
pNode = nullptr;
--_size;
} else {
cout << "该链表为空,无法删除" << endl;
return;
}
}
//在中间进行插入
void insert(int pos, int data) {
if (pos < 0 || pos > size()) {
cout << "该位置异常,无法插入" << endl;
return;
}
Node *pNode = _head;
while (pos-- > 0) {
pNode = pNode->_next;
}
Node *newNode = new Node(data);
newNode->_pre = pNode;
newNode->_next = pNode->_next;
pNode->_next->_pre = newNode;
pNode->_next = newNode;
++_size;
}
//在中间进行删除
void erase(int data) {
bool flag = find(data);
if (flag) {
Node *pNode = _head->_next;
while (pNode && pNode != _tail) {
if (pNode->_data == data) {
Node *ptmp = pNode->_pre;
pNode->_next->_pre = ptmp;
ptmp->_next = pNode->_next;
delete pNode;
pNode = nullptr;
--_size;
pNode = ptmp;
}
pNode = pNode->_next;
}
} else {
cout << "该元素不在链表之中,无法删除" << endl;
return;
}
}
bool find(int data) {
if (size() > 0) {
Node *pNode = _head->_next;
while (pNode && pNode != _tail) {
if (data == pNode->_data) {
return true;
}
pNode = pNode->_next;
}
return false;
} else {
cout << "该链表为空,无法查找" << endl;
return false;
}
}
void display() const {
if (size() > 0) {
Node *pNode = _head->_next;
while (pNode && pNode != _tail) {
cout << pNode->_data << " ";
pNode = pNode->_next;
}
cout << endl;
} else {
cout << "该链表为空,无法打印" << endl;
return;
}
}
~List() {
cout << "~List()" << endl;
Node *deleteNode = _head->_next;
while (deleteNode) {
Node *ptmp = deleteNode->_next;
delete deleteNode;
deleteNode = nullptr;
deleteNode = ptmp;
}
delete _head;
_head = nullptr;
}
int size() const {
return _size;
}
private:
Node *_head;
Node *_tail;
int _size;
};
int main(int argc, char **argv) {
List lst;
lst.display();
cout << endl << "在链表头部进行插入 :" << endl;
lst.push_front(1);
lst.push_front(3);
lst.push_front(5);
lst.display();
cout << endl << "在链表尾部进行插入 :" << endl;
lst.push_back(11);
lst.push_back(33);
lst.push_back(55);
lst.display();
cout << endl << "在链表头部进行删除 :" << endl;
lst.pop_front();
lst.display();
cout << endl << "在链表尾部进行删除 :" << endl;
lst.pop_back();
lst.display();
cout << endl << "查找元素 :" << endl;
/* bool flag = lst.find(100); */
bool flag = lst.find(11);
cout << "flag = " << flag << endl;
cout << endl << "在中间进行插入" << endl;
/* lst.insert(10, 100); */
lst.insert(2, 100);
lst.display();
cout << endl << "在中间进行删除" << endl;
lst.erase(11);
lst.display();
return 0;
}
该方式模仿STL List容器的实现,但仅支持int数据类型,引入模板后可支持泛型。
需要注意的点为:当调用类的构造函数创建对象时,默认会创建头结点为尾结点,但头结点和尾结点不存储数据,这两个节点用于头插尾插头删尾删的定位操作。
代码解析
struct Node部分
struct Node {
Node(int data = 0, Node *pre = nullptr, Node *next = nullptr)
: _data(data)
, _pre(pre)
, _next(next) {
cout << "Node(int = 0, Node * = nullptr, Node * = nullptr) " << endl;
}
~Node() {
cout << "~Node()" << endl;
}
int _data;
Node *_pre;
Node *_next;
};
在C++中对结构体的功能做了增强,支持构造函数,该函数用于初始化对象。以上代码是双链表的数据类型定义和新结点的初始化操作。在C++中struct就是定义类的关键字,其默认访问权限为public,与之对应的定义类的关键字为class,class的默认访问权限为private.
List类部分
构造函数
List()
: _head(new Node())
, _tail(new Node())
, _size(0) {
cout << "List()" << endl;
_head->_next = _tail;
_tail->_pre = _head;
}
该函数的作用用于创建类对象和初始化数据成员,_head->_next = _tail;
_tail->_pre = _head;两行代码用于创建两个头结点为尾结点,其中头结点和尾结点不带数据,中间插入的节点带数据。
析构函数
~List() {
cout << "~List()" << endl;
Node *deleteNode = _head->_next;
while (deleteNode) {
Node *ptmp = deleteNode->_next;
delete deleteNode;
deleteNode = nullptr;
deleteNode = ptmp;
}
delete _head;
_head = nullptr;
}
析构函数的作用用于销毁对象的数据成员,从头结点的下一个结点开始遍历采用带头尾节点的双链表,每次有一个指针保存当前节点的下一个结点,删除当前结点,再将当前结点的下一个结点置为当前结点,依次循环删除,直到只剩下头结点,最后删除头结点即完成链表的销毁操作。该双链表每次开辟结点在堆申请空间,销毁链表时需要手动释放,否则造成内存泄漏(即分配完以后未释放,造成该片已分配的空间无法再次使用)。
头插push_front()
//在头部进行插入
void push_front(int data) {
Node *newNode = new Node(data);
newNode->_pre = _head;
newNode->_next = _head->_next;
_head->_next->_pre = newNode;
_head->_next = newNode;
++_size;
}
尾插
//在尾部进行插入
void push_back(int data) {
Node *newNode = new Node(data);
newNode->_pre = _tail->_pre;
newNode->_next = _tail;
_tail->_pre->_next = newNode;
_tail->_pre = newNode;
++_size;
}
头部删除和尾部删除同理
从头遍历链表
void display() const {
if (size() > 0) {
Node *pNode = _head->_next;
while (pNode && pNode != _tail) {
cout << pNode->_data << " ";
pNode = pNode->_next;
}
cout << endl;
} else {
cout << "该链表为空,无法打印" << endl;
return;
}
}
从尾部开始遍历的链表逻辑相似,把指针定位到尾指针的前一个位置,每次往前移动,遇到头指针停止
判断指定的元素是否存在(查找)
bool find(int data) {
if (size() > 0) {
Node *pNode = _head->_next;
while (pNode && pNode != _tail) {
if (data == pNode->_data) {
return true;
}
pNode = pNode->_next;
}
return false;
} else {
cout << "该链表为空,无法查找" << endl;
return false;
}
}
该函数仅用于确定存在性,而不返回元素的具体位置
从任意位置插入元素
//在中间进行插入
void insert(int pos, int data) {
if (pos < 0 || pos > size()) {
cout << "该位置异常,无法插入" << endl;
return;
}
Node *pNode = _head;
while (pos-- > 0) {
pNode = pNode->_next;
}
Node *newNode = new Node(data);
newNode->_pre = pNode;
newNode->_next = pNode->_next;
pNode->_next->_pre = newNode;
pNode->_next = newNode;
++_size;
}
先判断插入位置是否合法,若不合法则直接return,若合法则找插入位置,找到插入位置后插入元素
从任意位置删除元素
//在中间进行删除
void erase(int data) {
bool flag = find(data);
if (flag) {
Node *pNode = _head->_next;
while (pNode && pNode != _tail) {
if (pNode->_data == data) {
Node *ptmp = pNode->_pre;
pNode->_next->_pre = ptmp;
ptmp->_next = pNode->_next;
delete pNode;
pNode = nullptr;
--_size;
pNode = ptmp;
}
pNode = pNode->_next;
}
} else {
cout << "该元素不在链表之中,无法删除" << endl;
return;
}
}
先判断删除的位置是否合法,若不合法则退出,若合法则删除对应元素。
测试效果图:
进一步优化的建议:可以将find函数返回一个具体的数值,用于记录目标元素的位置,根据双链表的特性,判断元素位置和_size/2的大小。若小于可从头开始增删元素,若大于则从尾开始增删元素。