线性链表采用一组任意的存储单元存放线性表,这组存储单元可以是连续的,也可以不是连续的。
链表结点=数据域+指针域。
单链表的每个结点只有一个指针域,所以叫做单链表。
头结点:头结点是为了操作方便统一设立的,放在第一元素结点之前。有了头结点对第一元素结点的插入删除操作就跟其他结点的操作统一起来。
头指针:若无头结点,头指针指向链表第一元素结点;若有头结点,头指针指向头结点。头指针是链表的必要元素,头结点不是必要的。
线性表的单链表存储结构
typedef struct Node /*线性表的单链表存储结构*/
{
ElemType data;
struct Node *next;
} Node;
typedef struct Node *LinkList; /*定义LinkList*/
获取单链表结点数据,主要核心是“工作指针后移”
/*初始条件:带头节点的单链表L已存在,1≤i≤ListLength(L)*/
/*操作结果:用e返回L中第i个数据元素的值*/
Status GetElem(LinkList L, int i, ElemType *e)
{
LinkList p; /*声明一指针*/
int j;
p = L->next; /*让指针指向链表L的第一个节点*/
j = 1; /*j为计数器*/
while (p && j < i) /*p不为空且计数器j还没有等于i时*/
{
p = p->next; /*让p指向下一个结点*/
++j;
}
if (!p || j > i) /*当第i个结点不存在的时候*/
return ERROR;
*e = p->data; /*取得第i个结点的值*/
return OK;
}
单链表的插入操作:
当前结点指针为p,待插入结点指针为s,把s所指结点插入p和p->next所指结点之间:s->next = p->next; p->next = s; 这两句顺序不可交换。
/*初始条件:带头节点的单链表L已存在*/
/*操作结果:在L中第i个元素之前插入新的数据元素e*/
Status ListInsert(LinkList *L, int i, ElemType e)
{
LinkList p, s;
int j;
p = *L;
j = 1;
while (p && j < i) /*寻找第i-1个结点*/
{
p = p->next;
++j;
}
if (!p || j > i) /*第i个结点不存在*/
return ERROR;
s = (LinkList)malloc(sizeof(Node)); /*生成新结点*/
s->data = e;
s->next = p->next; /*将p的后继结点赋值给s的后继*/
p->next = s; /*将s赋值为p的后继*/
return OK;
}
单链表的删除操作:
删除p所指结点的直接后继结点:q = p->next; p->next = q->next;
/*初始条件:带头节点的单链表L已存在*/
/*操作结果:删除L的第i个结点,并用e返回其值*/
Status ListDelete(LinkList *L, int i, ElemType *e)
{
LinkList p, q;
int j;
p = *L;
j = 1;
while (p->next && j < i) /*寻找第i-1个结点*/
{
p = p->next;
++j;
}
if (!(p->next) || j > i)
return ERROR;
q = p->next;
*e = q->data; /*将q结点的数据赋给e*/
p->next = q->next; /*将q的后继赋值为p的后继*/
free(q); /*让系统回收此结点,释放内存*/
return OK;
}
单链表整表创建(头插法):把新结点插在头结点后面。
/*随机产生n个元素的值,建立带头结点的单链表L(头插法)*/
void CreateListHead(LinkList *L, int n)
{
LinkList p;
int i;
srand(time(0)); /*初始化随机数种子*/
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL; /*先建立一个带头结点的单链表*/
for(i = 0; i < n; i++)
{
p = (LinkList)malloc(sizeof(Node)); /*生成新结点*/
p->data = rand() % 100 + 1; /*随机生成100以内的数字*/
p->next = (*L)->next;
(*L)->next = p; /*插入到表头*/
}
}
单链表整表创建(尾插法):把新结点插在链表的最后
/*随机产生n个元素的值,建立带头结点的单链表L(尾插法)*/
void CreateListTail(LinkList *L, int n)
{
LinkList p, r;
int i;
srand(time(0)); /*初始化随机数种子*/
*L = (LinkList)malloc(sizeof(Node));
r = *L; /*r为指向尾部的结点*/
for(i = 0; i < n; i++)
{
p = (Node *)malloc(sizeof(Node)); /*生成新结点*/
p->data = rand() % 100 + 1; /*随机生成100以内的数字*/
r->next = p; /*将表尾终端结点的指针指向新结点*/
r = p; /*将当前的新结点定义为表尾终端结点*/
}
r->next = NULL; /*表示当前链表结束*/
}
单链表整表删除
/*初始条件:带头结点的单链表L已存在*/
/*操作结果:将L重置为空表*/
Status CLearList(LinkList *L)
{
LinkList p, q;
p = (*L)->next; /*p指向第一个结点*/
while (p) /*没到表尾*/
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL; /*头结点指针域为空*/
return OK;
}
单链表和顺序线性表空间性能对比:
顺序线性表需要欲分配存储空间,分大了浪费,分小了容易发生上溢。
单链表不需要预分配存储空间,可以根据实际需求即时分配存储空间。
单链表和顺序线性表时间性能对比:
查找:顺序线性表是O(1),单链表是O(n)。
插入删除:顺序线性表是O(n),单链表在找出某个位置指针后,插入删除时间为O(1)。