双向链表的增删改查C++完整实现
Posted zorchp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双向链表的增删改查C++完整实现相关的知识,希望对你有一定的参考价值。
tags: C++ DSA LinkedList
写在前面
写一下双向链表的增删改查, 用C++实现.
完整代码可以看我的GitHub;
节点类-链表类
节点
class ListNode
public:
int val;
ListNode* prev; // 前驱结点
ListNode* next; // 后继结点
ListNode() : ListNode(0, nullptr, nullptr)
ListNode(int x) : ListNode(x, nullptr, nullptr)
ListNode(int x, ListNode* _next) : ListNode(x, nullptr, _next)
// 委托构造
ListNode(int x, ListNode* _prev, ListNode* _next)
: val(x), prev(_prev), next(_next)
;
链表
class DoubleLinkedList
public:
DoubleLinkedList() : head(nullptr)
DoubleLinkedList(int head_val) : head(new ListNode(head_val))
DoubleLinkedList(int head_val, vector<int> rest);
DoubleLinkedList(vector<int> nodes);
~DoubleLinkedList();
void print();
ListNode* first();
ListNode* last();
int len();
// visit by pos
/* int& operator[](int pos); */
ListNode* operator[](int pos);
ListNode* visit(int pos);
ListNode* rvisit(int pos);
// insert by pos
void insert(int pos, int val);
void append(int val);
void add2head(int val);
// delete by pos
void pop(int pos);
void pop_back();
void pop_front();
// delete by value
void remove(int val, int cnt = 1); // delete val cnt times
void modify(int val, int node, int cnt = 1); // modify val->node cnt times
int find(int node); // not found:-1
int rfind(int node); // not found:-1
private:
ListNode* head;
;
这里实现与之前写的单链表类似, 基本的增删改查肯定要有, 然后还有一个打印函数, (不然用重载的operator<<
访问私有成员头结点不太好)
然后对于删除给出了基于位置的和基于值的两种实现, 直接调用已有的成员函数即可, 减少代码量.
然后是一个从右向左查找的rfind函数(事实上比较鸡肋, 需要先遍历到最后一个节点然后开始找).
还有一个重载的operator[]
, 以及visit
函数, 用来基于位置找节点(指针).
一些辅助函数
双向链表格式化输出
ostream& operator<<(ostream& os, ListNode* lp)
if (lp == nullptr) return os << "∅" << endl;
os << "∅ <== ";
ListNode* cur = lp;
while (cur != nullptr)
os << cur->val << (cur->next ? " <=> " : " ==> ");
cur = cur->next;
os << "∅";
return os << endl;
采用连字字体看起来就很舒服.
void DoubleLinkedList::print() cout << head;
双向链表长度
int DoubleLinkedList::len()
int size = 0;
auto cur = head;
while (cur) ++size, cur = cur->next;
return size;
头尾结点
ListNode* DoubleLinkedList::first() return head;
ListNode* DoubleLinkedList::last()
auto cur = head;
while (cur->next) cur = cur->next;
return cur;
遍历(找节点指针)
ListNode* DoubleLinkedList::operator[](int pos)
if (head == nullptr)
cerr << "Attempt to get value from a NULL DoubleLinkedList\\n";
exit(1);
if (pos > len() - 1 || pos < 0)
cerr << "Wrong pos!\\n";
exit(1);
auto cur = head;
while (pos--) cur = cur->next;
return cur;
ListNode* DoubleLinkedList::visit(int pos) return this->operator[](pos);
ListNode* DoubleLinkedList::rvisit(int rpos)
// 从右往左遍历找目标位置
if (head == nullptr)
cerr << "Attempt to get value from a NULL DoubleLinkedList\\n";
exit(1);
if (rpos > len() - 1 || rpos < 0)
cerr << "Wrong rpos!\\n";
exit(1);
auto cur = visit(len() - 1);
while (rpos--) cur = cur->prev;
return cur;
构造函数/析构函数
这里和单链表实现一样, 给出了三个构造函数, 一个通过初值列实现, 另外两个直接读取vector, 还是比较方便的.
DoubleLinkedList::DoubleLinkedList(int head_val, vector<int> rest)
head = new ListNode(head_val);
ListNode *cur = head, *nxt = nullptr;
for (auto i : rest)
nxt = new ListNode(i);
cur->next = nxt;
nxt->prev = cur;
cur = cur->next;
DoubleLinkedList::DoubleLinkedList(vector<int> nodes)
if (nodes.empty())
cout << "Empty Data Source!\\n";
return;
head = new ListNode(nodes[0]);
auto cur = head;
for (auto i = nodes.begin() + 1; i < nodes.end(); i++)
auto nxt = new ListNode(*i);
cur->next = nxt;
nxt->prev = cur;
cur = cur->next;
DoubleLinkedList::~DoubleLinkedList()
ListNode* cur;
while (head->next)
cur = head->next;
cout << head->val << " ";
delete head;
head = cur;
cout << head->val << " ";
delete head;
cout << "deleted...\\n";
对于析构函数, 没什么好说的, 跟单链表一样, 主要说一下构造函数, 由于是双向链表, 那么就一定要注意prev指针.
一般来说, 每次添加一个新节点, 都要链接四根指针, 分别是前驱节点的next, 当前节点的prev和next, 后继结点的prev, 缺一不可.
添加节点
void DoubleLinkedList::insert(int pos, int val)
if (pos >= len())
auto cur = last(), nxt = new ListNode(val);
cur->next = nxt;
nxt->prev = cur;
else if (pos <= 0)
auto cur = head;
head = new ListNode(val);
head->next = cur;
cur->prev = head;
else // 中间位置
auto pre = head;
while (--pos) pre = pre->next;
// pre为待插入位置的前一个节点
auto cur = pre->next;
pre->next = new ListNode(val, pre, cur);
void DoubleLinkedList::add2head(int val) insert(0, val);
void DoubleLinkedList::append(int val) insert(len(), val);
删除节点
void DoubleLinkedList::pop(int pos)
if (head == nullptr)
cout << "Attempt to delete a NULL DoubleLinkedList!" << endl;
else if (head->next == nullptr)
delete head;
head = nullptr; // 指针置为空, 避免空悬指针, 下同
else
if (pos >= len() - 1)
// 使用内置的visit函数
auto cur = rvisit(1), tmp = cur->next;
delete tmp;
cur->next = nullptr; // 必须加, 否则析构时候会出错
else if (pos <= 0)
auto cur = head->next;
delete head;
head = cur;
else
auto cur = visit(pos - 1);
auto nxt = cur->next->next, tmp = cur->next;
cur->next = nxt;
delete tmp;
void DoubleLinkedList::pop_front() pop(0);
void DoubleLinkedList::pop_back() pop(len());
void DoubleLinkedList::remove(int val, int cnt)
int idx;
do
if ((idx = find(val)) == -1)
cout << "can not find val " << val << " to delete\\n";
break;
else
pop(idx);
while (--cnt);
修改节点
// modify val->node cnt times
void DoubleLinkedList::modify(int val, int node, int cnt)
int idx;
do
if ((idx = find(val)) == -1)
cout << "can not find val " << val << " to modify\\n";
break;
else
/* visit(idx)->val = node; */
(*this)[idx]->val = node;
while (--cnt);
查找节点
int DoubleLinkedList::find(int node)
if (head == nullptr) return -1;
auto cur = head;
int pos;
while (cur)
if (cur->val == node) return pos;
++pos;
cur = cur->next;
return -1;
int DoubleLinkedList::rfind(int node)
// 从右往左找
auto cur = last();
if (cur == nullptr) return -1;
int pos;
while (cur)
if (cur->val == node) return pos;
++pos;
cur = cur->prev;
return -1;
主函数: 测试
#include "Double_Linked_List.hpp"
// === test func === ===//
void test_ctor()
DoubleLinkedList ll1(12);
ll1.print();
DoubleLinkedList ll2(1, 2, 3, 4);
ll2.print();
DoubleLinkedList ll3(1, 2, 3, 4);
ll3.print();
/* ∅ <== 12 ==> ∅ */
/* ∅ <== 1 <=> 2 <=> 3 <=> 4 ==> ∅ */
/* ∅ <== 1 <=> 2 <=> 3 <=> 4 ==> ∅ */
/* 1 2 3 4 deleted... */
/* 1 2 3 4 deleted... */
/* 12 deleted... */
void test_fundamental()
DoubleLinkedList ll1(1, 2, 3, 4);
cout << "ll1=";
ll1.print();
cout << "len(ll1)=" << ll1.len() << endl;
cout << "first: " << ll1.first()->val << endl;
cout << "last: " << ll1.last()->val << endl;
cout << "visit(1) : " << ll1.visit(1)->val << endl;
cout << "rvisit(1) : " << ll1.rvisit(1)->val << endl;
/* ll1=∅ <- 1 <-> 2 <-> 3 <-> 4 -> ∅ */
/* len(ll1)=4 */
/* first: 1 */
/* last: 4 */
/* visit(1) : 2 */
/* rvisit(1) : 3 */
/* 1 2 3 4 deleted... */
void test_insert()
DoubleLinkedList ll1(1, 2, 3, 4);
ll1.print();
ll1.insert(0, 3);
ll1.insert(5, 3);
ll1.insert(2, 3);
ll1.print();
/* ∅ <- 1 <-> 2 <-> 3 <-> 4 -> ∅ */
/* ∅ <- 3 <-> 1 <-> 3 <-> 2 <-> 3 <-> 4 <-> 3 -> ∅ */
/* 3 1 3 2 3 4 3 deleted... */
void test_pop()
DoubleLinkedList ll1(0, 1, 2, 3, 4);
ll1.pop(12);
ll1.pop(-1);
ll1.pop(1);
ll1.print(); // ∅ <- 1 <-> 3 -> ∅
/* 1 3 deleted... */
void test_find_pop()
DoubleLinkedList ll1(1, 2, 3, 4);
ll1.append(5
以上是关于双向链表的增删改查C++完整实现的主要内容,如果未能解决你的问题,请参考以下文章