前言
小猿根据实际需求,此次实现的是双向链表类,并附有详细的代码注释,双向链表是工作最实用的数据结构类型,和小猿一起学习吧。
一、链表的特点及和数组的区别
特点:
1、 链表是一种非连续的存储结构(相对于数组)。
2、 链表由一系列的结点组成,每个结点包括两个部分:数据和指针。
3、 链表通过结点中的指针来确定逻辑顺序。
区别:
1、 数组通过数组名字以及[]中的下标来访问数据,链表通过指针找到数据。
2、 数组无法动态增删元素(只能改),链表可以动态增、删、改结点。
3、 数组的定义、使用和回收空间操作简单,链表以上都复杂。
因此,在已知所存储数据的总数,且需要快速查找,一般用数组;存储数据需要经常增、删,一般用链表,实际工作中链表的使用范围更广。
二、具体实现
1.定义类
1.1结点类
//结点类
class Node
{
public:
string ip;//客户端ip
string name;//客户端名字
Node *next;//指向下一个结点
Node *prev;//指向上一个结点
int len;
Node()
{
//当前对象不用加前缀
next = nullptr;
prev = nullptr;
}
void display()
{
cout << ip <<" "<< name << endl;
}
};
因为NULL也可以表示为零,而int *p 和p = NULL可能没连在一起;所以为了明确表示空指针,c++11版本以后用nullptr, 不能赋值整型,严格限制只能赋值指针,便于程序员区分。
1.2链表类
class MYList
{
public:
//头结点
Node *head;
MYList();
~MYList();
//增加一个结点
void addMYList(string,string);
//删除一个结点
void deleteMYList(string);
//修改一个结点
void modifyMYList(string,string);
//遍历链表
void traverse();
};
2.链表类的构造函数与析构函数
MYList::MYList()
{
//新建一个头结点
head = new Node();
//Node = new Node;堆空间都一样
// Node n;栈空间正确写法 ;Node n ();栈空间这样写错误,意思不一样
head->len = 0;
}
MYList::~MYList()
{
Node* p = head;
//伴随指针
Node* q = head;
while(p)
{
q->next = p->next;
delete p;
//delete p之后,p是一个无意义的值,无法操作p->next,p->prev
p = q->next;
}
}
3.增
//增加一个结点
void MYList::addMYList(string ip,string name)
{
//创建数据结点
Node* pnew = new Node();
if(pnew == nullptr)
return ;
pnew->ip = ip;
pnew->name = name;
//头插
//数据结点插在头结点后面(数据结点从无到有)
if(head->next == nullptr)
{
pnew->next = head->next;
pnew->prev = head;
head->next = pnew;
//记录结点个数
head->len++;
}
//从少到多
else
{
pnew->next = head->next;
pnew->prev = head;
head->next->prev = pnew;
head->next = pnew;
head->len++;
}
}
4.删
//根据客户端ip删除
void MYList::deleteMYList(string ip)
{
//p指向第一个数据结点
Node* p = head->next;
//Node* q = head->next;
//遍历
while(p)
{
if(p->ip == ip)
{
//只有一个数据结点
if(p->next == nullptr)
{
p->prev->next = NULL;
p->prev = NULL;
}
//多个
else
{
p->prev->next = p->next;
p->next->prev = p->prev;
p->next = nullptr;
p->prev = nullptr;
}
delete p
head->len--;
//ip唯一
break ;
//ip不唯一,需要伴随指针q
//p = q->next
}
else
p = p->next;
//q = q->next;
}
}
5.改
//根据ip修改名字
void MYList::modifyMYList(string ip,string name)
{
//p指向第一个数据结点
Node* p = head->next;
//遍历
while(p)
{
if(p->ip == ip)
{
//name形参
p->name = name;
}
else
p = p->next;
}
}
6.查
//遍历打印函数
void MYList::traverse()
{
Node* p = head->next;
if(p == nullptr)
return ;
//遍历
while(p)
{
//结点类打印方法
p->display();
p = p->next;
}
}
7.测试及结果
int main()
{
MYList h;
h.addMYList("2020","新能源汽车");
h.addMYList("2050","移民火星");
h.addMYList("2040","脑机接口");
h.addMYList("0000","马斯克");
h.traverse();
cout << h.head->len<<endl;
h.deleteMYList("0000");
h.traverse();
h.deleteMYList("2020");
cout << h.head->len<<endl;
h.modifyMYList("2050","飞向太阳");
h.traverse();
h.deleteMYList("2050");
h.deleteMYList("2040");
h.traverse();
cout << h.head->len<<endl;
return 0;
}
总结
实现链表类的时候需要注意单向链、双向链、循环链的不同情况,最好构建类模板。