上一节说到线性表的顺序实现,顺序表示是使用一段连续的内存空间存储表元素,这使得存取数据元素相当快,直接使用下标即可存取元素,但这种线性表表示方式的缺点就是在插入和删除元素是要移动数据元素,移动元素多少并且是和线性表的长度相关的,当表很长时插入删除元素要移动相当大量的数据。这一节将要说到线性表的另外一种存储方式——链式存储方式,这种方式不需要线性表元素在物理上相邻,插入和删除不需要移动元素,但也没有顺序存储的随机存储的优点。
删除节点首先也要寻找删除元素位置,然后再删除,时间主要消耗在寻找删除节点的位置。和顺序存储区别同插入节点类似。
链表使用完之后要记得释放链表。
- 链式存储数据结构
typedef struct lnode {
elemtype data;
struct lnode *next;
}lnode, *linklist;
- 初始化链表
int init_list(linklist *l)
{
if (((*l) = (linklist) malloc (sizeof(lnode))) == NULL)
return ERROR;
(*l)->next = NULL;
return OK;
}
初始化链表时就是建立一个链表的头节点,其next域为空即可。(注:作者所有给出算法都为标准C语言描述,并非伪代码。结合作者算法和相关教材看时要注意,大多教材算法描述是伪代码描述)
- 头插法建立链表(逆序建表)
void create_list_l(linklist l, int n)
{
int i;
linklist p;
for (i=0; i<n; i++) {
p = (linklist) malloc (sizeof(lnode));
scanf("%d",&p->data);
p->next = l->next;
l->next = p;
}
}
头插法建表就是插入节点在链表的前面,后进来的节点在链表的前面(有点像“后来居上”)。
- 尾插法建立链表
void create_list_r(linklist l, int n)
{
int i;
linklist p, s;
p = l;
for (i=0; i<n; i++) {
s = (linklist) malloc (sizeof(lnode));
scanf("%d", &s->data);
p->next = s;
p = s;
}
p->next = NULL;
}
尾插法建表和头插法建表相反,先进来的节点靠近头节点,后进来的节点插在链表的尾部。这种方法比头插法建表要多用一个数据空间,这个空间就是用来保存链表尾部节点的地址,以便后来插入的节点知道在何处添加节点。
- 插入节点
int insert_node(linklist l, int n, elemtype e)
{
int i;
linklist p, s;
p = l;
while ((p->next) && (i<n-1)) {
p = p->next;
++i;
}
if ((!p->next) || (i>n-1))
return ERROR;
if ((s = (linklist) malloc (sizeof(lnode))) == NULL)
return ERROR;
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
链式存储线性表插入要首先查找将要插入新节点的位置,然后再将节点添加到链表中。寻找消耗时间和链表长度相关,时间复杂度为O(n)。和顺序存储区别:1.链表要从头依次查找插入位置,时间主要消耗早寻找位置,而后者不需要寻找插入位置;2.链表将新节点添加到时间复杂度为O(1),不需要移动元素,而后者要移动元素,消耗时间。
- 删除节点
int del_node(linklist l, int n, elemtype *e)
{
int i = 0;
linklist p, s;
p = l;
while (p->next && i<n-1) {
p = p->next;
++i;
}
if (!(p->next) || i>n-1)
return ERROR;
s = p->next;
p->next = s->next;
*e = s->data;
free(s);
return OK;
}
- 查找节点
int search_node(linklist l, elemtype e)
{
int i = 0;
linklist p;
p = l->next;
while (p) {
++i;
if (p->data == e)
break;
p = p->next;
}
if (!p)
return ERROR;
return i;
}
既然都是查找节点就肯定不能随机存取,和顺序存储类似,在没有排序的情况下,查找从头开始,时间复杂度为O(n)。
- 取得节点
int get_node(linklist l, int n, elemtype *e)
{
int i = 1;
linklist p;
p = l->next;
while (p && i<n) {
p = p->next;
++i;
}
if (!p || i>n)
return ERROR;
*e = p->data;
return OK;
}
取节点数据要从头开始遍历整个链表,首先找到该节点的位置,然后再存取,时间复杂度O(n)。和顺序存储区别:随机存储直接根据下标取数据,时间复杂度O(1)。
- 取得链表长度
int get_list_length(linklist l)
{
int n = 0;
linklist p;
p = (linklist) malloc (sizeof(lnode));
p = l->next;
while (p) {
++n;
p = p->next;
}
return n;
}
取链表长度也必须从链表头节点开始遍历,一步一步计算链表长度。和顺序存储区别:顺序存储直接取得长度。
- 输出链表
void output_list(linklist l)
{
linklist p;
p = (linklist) malloc (sizeof(lnode));
p = l->next;
while (p) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
- 释放链表
void destory_list(linklist l)
{
linklist p, s;
p = l;
while (p) {
s = p;
p = p->next;
free(s);
}
}
- 实现源码
源码免费下载:http://download.csdn.net/detail/algorithm_only/3787318