在使用顺序结构存储数据时,经常会有类似空间不足导致数据溢出,空间过大带来内存浪费等情况。最常见的应该就是我们的数组这种顺序存储结构。
C语言中,有没有办法解决这个问题呢?答案是有的,可以用链表这种数据结构来解决动态扩容的问题。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读
取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上
顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向
链表以及循环链表。
今天我们来谈一下关于单链表的问题,也就是上文中的单向链表。
首先定义一下链表的元素,也就是接下来要说的节点:
typedef int ElemType; typedef struct { ElemType data;//数据域 struct Node *next; //指针域,指向下一个节点 }Node; typedef Node* Linklist;
我们用一个结构体来表示一个节点,其中data是节点存放的数据,next是一个指针,指向下一个节点的地址。
其余两个typedef只是为了方便修改才定义的。
好,接下来要说一下我们得链表结构比起顺序结构好在哪里。如下图所示,链表一般都存在一个头结点(头结点不是必要的,但有头结点,就能较快捷地访问第一个节点,以后会说到),头结点不存放任何数据,或者可以存放当前链表的数据个数。头结点的next指针指向第一个节点。第一个节点的data存放数据,next指针指向第二个节点,以此类推,最后一个节点的data同样也是存储数据,但是next指针却是为空(即NULL),因为是单向链表,所以最后一个节点意味着数据的终结。
以上就是关于单链表的整体描述,说了这么多,到底它跟顺序结构相比好在哪里呢? 对,就是差在那个next指针上!我们大家都知道,申请数组的时候,比如一个int[100]的数组,它占据的内存空间是 100 * sizeof(int) 的容量,而且这些内存 一定 是连续的,这容量已经被定得死死的了。而链表呢,链表刚开始只需要定义一个头结点,如果此时来了一个元素,就用malloc函数开辟一个空间,让头结点的next指针指向这个新的元素,这样这个元素就属于这条链了!而且有了指针后,就算是这些数据不是连续的 也一点关系都没有!只要指针指向是正确的,那么这条链就是正确的,这就不会造成像顺序结构一样的内存碎片问题(不知道什么叫内存碎片的自行百度~)。
有了以上的认识,我们很快的就可以进行一个空链表的初始化工作:
void CreatListInit(Linklist *L)
{
*L = (Linklist)malloc(sizeof(Node));
(*L)->next = NULL;
}
这样就创建了一个空的链表,因为要修改数据,所以传进来的参数是Linklist *类型。
接下来,就要开始往链表里加数据了。初始化一个链表的方法一般有头插法和尾插法两种,头插法就是每次把新的元素都放在头结点之后,也就是第一个元素所在的地方,剩下的其他元素都排在它后面。而尾插法,对,就是插在后面,没错,跟大家想的一样,我就不啰嗦了。
如上图所示,头插法只要把头指针的next指针指向p,然后把p的指针指向之前的第一个节点(如果有的话)
而尾插法就更简单了。只要将最末尾的next指向p,再把p的next指针指向NULL。具体的实现步骤得自己好好想想,一般想通了,那你就已经学会单链表了!~
实现如下
头插法:
<table><tbody><tr><td class="number"><code></code></td><td class="content"><code class="comments">/* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */</code></td></tr></tbody></table>
int CreatListHead(Linklist *L , int n) //头插法创建单链表
{
int i;
Linklist p;
srand(time(0));
*L = (Linklist)malloc(sizeof(Node));
(*L)->Next = NULL;
for(i = 1;i<=n;i++)
{
p = (Linklist)malloc(sizeof(Node));
p->data = rand()%100+1;
p->Next = (*L)->Next;
(*L)->Next = p;
}
return 1;
}
尾插法:
void CreatListTail(Linklist *L ,int n) //尾插法创建单链表
{
int i;
Linklist p,r;
*L = (Linklist)malloc(sizeof(Node));
(*L)->Next = NULL;
r=*L;
for(i=1;i<=n;i++)
{
p = (Linklist)malloc(sizeof(Node));
p->data = i;
r->Next = p;
r = p;
}
r->Next = NULL;
}
这样一来,初始化空链表和单链表的工作就结束了。单链表接下来的任务就是数据的插入和删除(不是从头或者尾),和数据的查找。
说这么多好累~估计以后就写简洁一些