一、链表概念
链表是一种数据结构。链表与数组具有类似的概念——都可以批量处理数据。
但事实上,链表弥补了数组的一些天生局限:
二、链表的意义(优点)
1.数组占有固定的内存空间,常常需要顾忌浪费内存空间或者越界的麻烦;而链表具有灵活动态的内存空间,可以随时增加与停止。
2.数组的每个数据在内存上都是连续的,这导致想要在中间增减某个数据就需要牵动到整个数组,带来不便;而链表的每个数据在内存上不是严格连续的,每个数据之间的链接关系可以由我们控制并随时改变。
三、链表的建立操作
1、链节(linker)
链表中的每个链节都是结构体。对结构体的唯一要求是必须有一个成员为指针,除此之外的其他数据都可以按需要定义在结构体中。以一个存放学生分数的链表为例:
//定义链节结构体
struct linker{
char name;//姓名
int sco;//分数
linker *next;//下一个节点的地址
};
char型与int型的数据只是为了满足设计的个性化需求,在其他需求背景下还可以定义其他类型数据。而 linker *next;的定义是链表能连接起来的关键。关于该操作有两个原理问题:
1)为什么能在结构体尚未定义完之前,用该结构体类型定义指针。
也就是说为什么能在结构体的定义中,像递归函数一样,使用该结构体类型定义指针:首先,除了定义该结构体的指针以外,其他涉及到该结构体类型的定义都不成立(例如不可以在里面定义一个“linker example”)。理由是这个结构体定义还未完成,占用内存大小不确定,系统无法为变量分配内存。这个回答也是为什么指针可以的原因,因为任何类型的指针(包括int、char以及结构体)占用的空间都是确定且相等的八个字节,系统在分配内存时不会有任何疑惑。
2)为什么链节能靠其中的指针实现链接关系
每个链节作为独立的数据都有属于自己的地址,而只要上一个链节中的指针储存了下一个链节的地址,就可以实现数据的连续性。需要注意的是:因为指针要储存下一个链节的地址,所以定义的指针类型是该结构体类型(例如上面的linker *next;)。至于指针名推荐使用“next”,也是因为表示指针指向下一个链节的地址。链接关系可参考下图理解:
2、头节点 (head)
//创立头节点
static linker *head=new linker; //利用动态内存分配命令new创建节点
head->name=1;// 对头节点的数据name赋值
head->sco=60;//对头节点的数据sco赋值
head->next=NULL;//对头节点的next指针赋值:由于下一个节点还未创立,所以先指向NULL
如上图。用static修饰了头节点,是为了防止头节点被篡改导致丢失。由于整个链表都从头节点开始,调用时也从头节点出发,所以一旦头节点丢失,那么整个链表中的所有数据都无法使用,也就是说整个链表都找不到了。这一点对于理解下面的链表遍历操作很重要。
特殊步骤解释:
*需要利用动态内存分配命令new创建节点:因为每个结构里的成员所需空间大小未知。
*创建每个节点时,节点内的指针都先赋NULL,因为下一个节点还未创立,尚不知道该指向哪里。
3、中间节点与尾节点(tail)
//创立中间节点和尾节点
linker *tail=head; //使得tail能从head开始创建
for(int i=0;i<=n;i++)//创建了n个节点
{
tail->next=new linker;//先创建出 tail->next
tail->next->name=i;//为tail->next赋值i
tail->next->sco=(rand()%(100-0))+0;//随机数为tail->next赋值sco
tail->next->next=NULL;为tail->next赋值next
tail=tail->next;//指向跳到tail->next
}
这里的tail节点即指尾节点。它是一个动态的节点,建立完一个节点后就会跳到下一个节点,永远指向链表的最后。除了头节点需要单独定义以外,链表的所有数据都是在tail的动态移动中创建出来的。(注意:在tail未跳到之前,中间节点是不存在的,每个节点都是tail跳过来才创建的)
具体步骤解释:
1)为什么要创建一个等于head的tail节点,而不直接用已有的head节点去动态赋值
在上面对头节点的介绍中已经给出了答案:如果这里用head跳到后面动态赋值,我们就失去了整个链表的开始位置,最终只能得到最后一个链节(虽然还是叫head,但已经不是头链节了)。由于本文所讲的是单向链表,每个链节只有下一个链节的指针,也就是说只能向后遍历,所以前面的数据都找不到了。
为了防止这种情况发生,我们定义了一个动态的tail链节。在tail定义时为tail赋值了head,使得tail与head有了相同的首地址(也就是说指向同一个链节),tail向后延伸,其实是在弥补head之后的空白。
2)tail所指向的节点始终是赋完值了的节点(保证作为最后一个节点存在着)。每次赋值是在为tail->next赋值。在tail没跳到之前,tail->next是不存在的,所以需要用动态内存分配命令new创建出来,再为 tail->next中的每个成员赋值。
3)tail->next创建完毕后,关键的一步就是令tail跳到tail->next,此时刚刚创立完成的tail->next成了新的tail。那么进入下一个循环后,继续创立的tail->next则是相对于新的tail而言的tail->next。
4、完整链表建立函数
//链表建立函数
linker *creat(int n)//表示创建有n个节点的链表
{
//如果创建0个节点的链表(即什么都没有)
if(n==0) return NULL;
//如果创建1个节点的链表(即只有头链节)
//创立头节点
static linker *head=new linker; //利用动态内存分配命令new创建节点
head->name=1;// 对头节点的数据name赋值
head->sco=60;//对头节点的数据sco赋值
head->next=NULL;//对头节点的next指针赋值:由于下一个节点还未创立,所以先指向NULL
if(n==1) return head;
//如果创建多于一个节点的链表
linker *tail=head;
for(int i=2;i<=n;i++)//创建了n-1个节点
{
tail->next=new linker;//先创建出 tail->next
tail->next->name=i;//为tail->next赋值i
tail->next->sco=(rand()%(100-0))+0;//随机数为tail->next赋值sco
tail->next->next=NULL;为tail->next赋值next
tail=tail->next;//指向跳到tail->next
}
return head; //返回head即可使用整个链表
}
四、链表的遍历 (move)
//链表打印函数
void print(linker *head)
{
linker *move=head;
while(move)// 即:move链节存在就打印出来
{cout<<move->name<<" "<<move->sco<<endl;
move=move->next;}
}
如上面链表打印函数的示例
创立move与tail的意义相同,都是为了防止head改变导致整个链表丢失。
由于除了头链节以外的其他链节的地址都不知道,所以想要访问链表中的任何一个链节,都需要从头遍历过去,在找到后停下。这一点算是链表的一个弊端。
五、链表的删除(deletelinker)
原理示意图:
代码实现:
//链表定点删除函数
void deletelinker(linker *head,int n)//表示删除第n个链节
{
linker *move=head;
int i=1;
while(i!=n-1) move=move->next; //寻找所需删除的链节的前一个链节
move->next=move->next->next;//直接跳过想要删除的链节,重新链接
}
*操作注意:
1、由于每个链节都是用new开辟出来的,所以,在中途删除的链节空间需要记得及时单独释放(delete)
2、示例中通过链节的排序位置进行寻找,实际问题中,可以灵活根据情境寻找相应的链节。比如说,还是这个存放学生分数的链表,我们也可以寻找存放学生姓名为LiMing的链节进行删除操作,那么寻找的条件就可以换成“while(move->next->name!=”LiMing”) move=move->next;”。
六、链表的插入(insertlinker)
原理示意图:
代码实现:
//链表定点插入函数
void insertlinker(linker *head,linker *newnode,int n) //表示在第几个链节后插入新的链节 (newnode)
{
int i=1;
while(i!=n-1) move=move->next;
newnode->next=move->next;
move->next=newcode;
}
*操作注意:
需要先让插入链节连接上下一个链节(第n+1个),再使上一个链节(第n个)改变为连接插入链节。如果颠倒操作顺序,那么第n+1个链节及之后的链节都会丢失。
今天的文章线性表的链式表示和实现_数据结构和链表的意义分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/84568.html