构建简单双向链表与链表应用
绪论
按照常规的学习路径,当我们学习了一门高级编程语言之后,接着便要开始学习各种各样的基本数据结构了
最近刚好加入了西电的信安协会的二进制组,刚好其中有一条学习任务是针对会写链表的新生的,所以今天我来讲讲链表结构当中的双向链表的构造
通常来说只要能写好不出bug的双向链表,对于链表这个数据结构你就有一定的理解了
(然鹅本萌新依然什么都不知道QAQ)
(为什么NO.00001不先从更简单的数据结构开始写起?我也不知道啊!)
PS:其实这是一篇修订过错误的博文
简介:什么是链表?
链表是一种于物理储存单元上具有非连续性与非顺序性的数据结构(除了NOI里的数组(伪)链表,但仔细一想当初打OI的时候写的数据链表确实很方便…),链表之间依靠指针进行连接以建立一种奇特的逻辑顺序。
常规而言,一个链表由数个结点构成(我们将链表当中的每一个元素称之为结点),结点之间通过指针来连接彼此。因此一个结点通常由两部分组成:
1.储存数据元素的数据域
2.指向其他结点的指针
本篇博文讨论的是双向链表,因此每个结点由三个部分组成:指向上一结点的指针,指向下一结点的指针,储存数据元素的数据域
由于双向链表的结构特殊性,我们从双向链表的任意一个结点都可以很方便的访问其上一个结点(前驱结点)与下一个结点(后继结点)
双向链表结构图示
(图片来自于百度图片搜索,若有侵权请联系我,我会第一时间删除)
基本原理:结构体&指针
〇、基本框架
优点
可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理
链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取
(来源:百度百科:链表)
相信阅读这一篇博文的大佬们应该都知道指针和结构体这两个基本知识点,这里就不再过多解释了
从双向链表的基本结构,我们很容易想到:利用结构体可以很好地将双向链表的结点结构给构造出来:
struct LinkedList{
struct LinkedList *nextone;//指向下一个结点的指针
string the_name;//该结点的名字
int the_data;//该结点内所储存的数据,这里默认用一个int
struct LinkedList *lastone;//指向上一个结点的指针
};
我们需要实现的功能是:
1.创建链表
2.插入链表
3.删除链表
4.在链表中跳转
为了实现这些功能,我们使用一个指针指向当前所选取链表结点,用一个指针指向链表尾部,使用一个整型变量进行计数
int list_num=0;
struct LinkedList *location = NULL;
struct LinkedList *makingnow = NULL;
一、创建链表结点
利用new操作符搭配结构体指针,我们就能很好的实现链表的创建功能:
注意,在C++当中不能够使用malloc来为string类分配内存!!!
struct LinkedList *ptr = new struct LinkedList;
cout<<"Please input the name of this new List:";
cin>>ptr->the_name;
cout<<"Please input the data of this new List(it has to be a int num):";
cin>>ptr->the_data;
ptr->lastone = ptr->nextone = NULL;
if(location != NULL)
{
makingnow->nextone = ptr;
ptr->lastone = makingnow;
makingnow = ptr;
}
else
makingnow = location = ptr;
cout<<"Creation succeeeded!"<<endl;
list_num++;
二、插入链表
链表的插入过程如下所示:
借助指针的特性,我们可以很好的实现链表的插入功能:
1.新建链表e
//检测你是否已拥有一个及以上的链表
if(!list_num)
{
cout<<"You haven't got any List! Impossible to insert one!"<<endl;
break;//这段代码原位置是放在一个switch语句里的
}
cout<<"Please choose the location you'd like to insert.\"l\" for the last or \"n\" for the next."<<endl;
/*
选择插入链表的位置
输入l为在当前链表之前插入,n为在当前链表之后插入
*/
while((ch = getch()) != 'l' && ch != 'n')
cout<<"Wrong input! Please input again!"<<endl;
struct LinkedList *ptr2 = new struct LinkedList;
cout<<"Please input the name of this new List:";
cin>>ptr2->the_name;
cout<<"Please input the data of this new List(it has to be a num):";
cin>>ptr2->the_data;
ptr2->lastone = ptr2->nextone = NULL;
cout<<"Creation succeeeded!"<<endl;
list_num++;
2.设置链表e的prior指针指向链表ai
3.设置链表e的next指针指向链表ai+1
4.设置ai的next指针指向链表e
5.设置ai+1的prior指针指向链表e
switch(ch)
{
case 'l':
if(location->lastone == NULL)//检测当前所在链表结点是否是链表头
{
location->lastone = ptr2;
ptr2->nextone = location;
break;
}
ptr2->lastone = location->lastone;
location->lastone->nextone = ptr2;
ptr2->nextone = location;
break;
case 'n':
if(location->nextone == NULL)//检测当前所在链表结点是否是链表结尾
{
ptr2->lastone = location;
makingnow = location->nextone = ptr2;
break;
}
ptr2->nextone = location->nextone;
location->nextone->lastone = ptr2;
ptr2->lastone = location;
location->nextone = ptr2;
}
三、删除链表结点:delete操作符
我们在之前使用new操作符动态分配的内存可以使用其搭配的delete操作符进行释放
不能使用free()释放new所分配的内存
if(location->lastone == NULL)//检测所删除结点是否为头结点
{
if(list_num == 1)
{
delete location;
makingnow = location = NULL;
}
else
{
location = location->nextone;
free(location->lastone);
location->lastone = NULL;
}
}
else if(location->nextone == NULL)//检测所删除结点是否为尾结点
{
makingnow = location = location