1.线性表的链式存储结构
1.1 顺序存储结构不足的解决办法
线性表的顺序存储结构最大的缺点是插入和删除时需要移动大量数据,这显然就需要消耗时间。本节讨论的链式存储结构可以很好滴解决这个问题。
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这些存储单元可以使连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存未被占用的任意位置。
以前的顺序结构中,每个数据元素只需要存数据元素的信息就可以了。现在链式结构中,除了要存数据元素信息外,还要存储它的后继元素的存储地址。
因此,为了表示每一个数据元素a(i)与其直接后继数据元素a(i+1)之间的逻辑关系。对数据a(1)来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域;把存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或链。这两部分信息组成元素a(i)的存储映像,称为结点。如果每个结点只包含一个指针域,叫做单链表。单链表正是通过每个节点的指针域将线性表的数据元素按其逻辑次序连接在一起,如图所示:
对于线性表来说,总要有头有尾,链表也不例外。我们把链表中第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行了。之后的每一个结点,其实就是上一个的后继指针指向的位置。最后有意义的是讨论最后一个结点的指针指向问题,暂时规定,线性链表的最后一个结点指针为“空”(NULL)
有时,我们为了更加方便地对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息,头结点的指针域存储指向第一个结点的指针,如图所示:
2.单链表操作
2.1 单链表的读取
在线性表的顺序存储结构中,我么要计算任意一个元素的存储位置时容易的。但是在单链表中,由于第i个元素到底在哪?没办法一开始就知道,必须从头开始找。
获得链表第i个数据的算法思路:
(1)声明一个结点p指向链表第一个结点,初始化j从1开始;
(2)当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
(3)若到链表末尾p为空,则说明第i个元素不存在;
(4)否则查找成功,返回结点p的数据。
代码实现如下:
Status GetElem( LinkList* L, int i, ElemType* e)
{
int j;
LinkList p; //声明一节点p
p = L->next; //让p指向链表L的第一个节点
j = 1;
while ( p && j<i) //p不为空 或者计数器j还没有等于i时,循环继续
{
p = p->next; //让p指向下一个结点
++j;
}
if (!p || j>1i)
return ERROR;
*e = p->data;
return OK;
}
说白了,就是从头开始找,知道第i个元素为止。由于这个算法的时间复杂度取决于i的位置,当i=1时,则不需要遍历,第一个就取出数据了,而当i = n时,则遍历n-1次就可以了,因此最坏情况的时间复杂度是O(n)。
由于单链表的结构中没有定义表长,所以不能事先知道要循环多少次,因此也就不方便使用for来控制循环。其主要核心思想就是“工作指针后移”。
2.3 单链表的元素删除
2.2 单链表的元素插入
单链表的元素插入用不着惊到其他结点,只需要让s->next和p->next的指针做一点改变即可。
s->next = p->next;
p->next =s;
单链表第i个数据插入节点的算法思路:
(1)声明一结点p指向链表第一个结点,初始化j从1开始;
(2)当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
(3)若到链表末尾p为空,则说明第i个元素不存在;
(4)否则查找成功,在系统中生成一个空节点s;
(5)将数据元素e赋值给s->data;
(6)单链表的插入标准语句s->next=p->next;p->next = s;
(7)返回成功。
实现代码算法如下:
Status ListInsert ( LinkList *L, int i, ElemType e)
{
int j;
LinkList p,s;
p = *L;'
j = 1;
while( p && j<i)
{
p = p->next;
++j;
}
if ( !p || j>1)
return ERROR;
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
设存储元素a(i)的结点为q,要实现将节点q删除单链表操作,其实就是将他的前几结点的指针绕过,指向它的后继结点即可,如图所示:
这里需要做的只有一步,p->next = p->next->next,用q来取代p->next,删除语句可以写成:
q=p->next;
p->next = q->next;
单链表第i个数据删除节点的算法思路:
(1)声明一个结点p指向链表第一个结点,初始化j从1开始;
(2)当j小于i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;
(3)若到链表末尾p为空,则说明第i个元素不存在;
(4)否则查找成功,将预删除的结点p->next赋值给q;
(5)单链表的删除标准语句p->next=q->next;
(6)
将q结点中的数据赋值给e,作为返回;
(7)释放q结点;
(8)返回成功。
实现代码算法如下:
实现代码算法如下:
Status ListDelete( LinkList* L, int i, ElemType* e)
{
int j;
LinkList p,q;
p = *L;
j = 1;
while( p->next || j<i )
{
p = p->next;
++j;
}
if ( !(p->next) || j>i)
return ERROR;
q=p->next;
p->next=q->next;
*e = q->data;
free(q);
return OK;
}