一、单链表的定义和表示:
线性链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示数据元素a(i)与其直接后继数据元素a(i+1)之间的逻辑关系,对数据元素a(i)来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素ai的存储映像,称为结点(node)。它包括两个域:其中存储数据元素信息的域称为数据域;存储后继存储位置的域称为指针域。指针域中存储的信息称为指针或链。n个结点(ai(1<=i<=n)的存储映像)链结成一个链表,即为线性表 (a1,a2,a3,....an)链式存储结构。又由于此链表的每个节点中只包含一个指针域,故又称为线性链表或单链表。
根据链表结点所含指针个数、指针指向和指针连接方式,可将链表分为单链表、循环链表、双向链表、二叉链表、十字链表、邻接表、邻接多重表等。其中单链表、循环链表和双向链表用于实现线性表的链式存储结构,其他形式多用于实现树和图等非线性结构。
先讨论单链表。整个链表的存取必须从头指针开始进行,头指针指示链表中第一个结点(即第一个数据元素的存储映像,也称首元结点)的存储位置。同时,由于最后一个数据元素没有直接后继,则单链表中最后一个结点的指针为空(NULL)。
用单链表表示线性表时,数据元素之间的逻辑关系时由结点中的指针指示的。换句话说,指针为数据元素之间逻辑关系的映像,则逻辑上相邻的两个数据元素其存储的物理位置不要求紧邻,由此,这种存储结构为非顺序或链式映像。通常将链表画成用箭头想链接的结点的序列,结点之间的箭头表示链域中的指针。
由上述可见,单链表可由头指针唯一确定,在C中可用“结构指针”来描述。
//----------单链表的存储结构-----------
typedef struct LNode
{
ElemType data;//结点数据域
struct LNode *next;//结点指针域
}LNode,*LinkList;//Linklist为指向结构体LNode的指针类型
(1)为了提高程序的可读性,在此对同一结构体指针类型起了两个名称,LinkList与LNode*,两者本质上是等价的。通常习惯用LinkList定义单链表,强调定义的是某个单链表的头指针;用LNode *定义指向单链表中任意结点的指针变量。例如,若定义LinkList L,则L为单链表的头指针;若定义LNode *p,则p为指向单链表中某个结点的指针,用*p代表该结点。当然也可以使用LinkList p,这种定义形式完全等价为LNode *p。
(2)单链表是由表头指针唯一确定,因此单链表可以使用头指针的名字来命名。若头指针名是L,则简称能该链表为表L。
(3)注意区分指针变量和结点变量两个不同的概念,若定义LinkList p或者LNode *p,则p为指针为指向某结点的指针变量,表示该结点的地址;而*p为对应的结点变量,表示该结点的名称。
----------------------------------------
下面对首元结点、头结点、头指针三个概念加以说明:
(1)首元结点是指链表中存储第一个数据元素a1的结点。
(2)头结点是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可以存储与数据元素类型相同的其他附加信息。例如当数据元素为整数型时,头结点的数据域可以存放该线性表的长度。
(3)头指针是指向链表中第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。
----------------------------------------
链表增加头结点的作用:
(1)便于首元结点 的处理
增加了头结点之后,首元结点的地址保存在头结点的指针域中,则对链表的第一个数据元素的操作与其他数据元素相同,无需进行特殊处理。
(2)便于空表和非空表的统一处理
当链表不设头结点时,假设L为单链表的头指针,它应该指向首元结点,则当单链表为长度n为0的空表时,L指针为空(判定空表条件可表示为:L==NULL)。
增加头结点后,无论链表是否为空,头指针都是指向头结点的非空指针。若为空表,则头结点的指针域为空(判空条件为L->next==NULL)。
----------------------------------------
单链表各个元素的额存储位置都是随意的,然而每个元素的存储位置都包含在其直接前驱结点的信息之中。单链表是非随机存取的存储结构,要取得第i个数据元素必须从头指针出发顺链进行寻找,也称为顺序存取的存取结构。
-------------------------------------------------------------------------------------------------------------
二、单链表基本操作的实现:
1.初始化
单链表的初始化操作就是构造一个空表。
算法步骤
i 生成新结点作为头结点,用头指针L指向头结点。
ii头结点的指针域为置空。
算法描述
Status InitList(LinkList &L)
{//构造一个空的单链表L
L=new LNode; //生成新结点作为头结点,用头指针L指向头结点
L->next=NULL;//头结点的指针域置空
return OK;
}
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
#define Status int
#define OK 1
#define ERROR 0
typedef struct List
{
ElemType data;
struct List *next;
}linklist;
Status CreateList(linklist *L)
{
//创建一个单链表
linklist *p;
L->next=NULL;
p=(linklist *)malloc(sizeof(linklist));
if(!p)
return ERROR;
scanf("%d",&p->data);
while(p->data!=0)
{
p->next=L->next;
L->next=p;
p=(linklist *)malloc(sizeof(linklist));
scanf("%d",&p->data);
}
return OK;
}
Status reverse(linklist *L)
{
//单链表逆置
linklist *p,*q;
p=L->next;
L->next=NULL;
while(p)
{
q=p;
p=p->next;
q->next=L->next;
L->next=q;
}
return OK;
}
Status listinsert(linklist *L,int i,ElemType e)
{
//单链表的简单插入
int j;
linklist *p,*q;
q=L;
p=(linklist *)malloc(sizeof(linklist));
if(!p)
return ERROR;
p->data=e;
for(j=0;j<i-1;++j)
{
q=q->next;
}
p->next=q->next;
q->next=p;
return OK;
}
Status listdel(linklist *L,int i,ElemType *e)
{
//单链表的删除操作
int j;
linklist *p;
linklist *q=(linklist *)malloc(sizeof(linklist));
p=L;
for(j=0;j<i-1;++j)
{
p=p->next;
}
q=p->next;
*e=q->data;
p->next=q->next;
q->next=NULL;
free(q);
return OK;
}
linklist *MergeList(linklist *La,linklist *Lb )
{
//合并单链表
linklist *Lc;
linklist *pa,*pb,*pc;
pa=La->next;pb=Lb->next;
Lc=pc=La;
while(pa&&pb)
{
if(pa->data<=pb->data)
{
pc->next=pa;pc=pa;pa=pa->next;
}
else
{
pc->next=pb;pc=pb;pb=pb->next;
}
}
pc->next=pa?pa:pb;
return Lc;//返回合并后链表的头指针
}
Status display(linklist *L)
{
linklist *p;
p=L->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return OK;
}
int main(void)
{
int i,di;
ElemType e,de;
linklist *L=(linklist *)malloc(sizeof(linklist));//要想将修改的值带回,需用动态内存分配
//原因是静态分配的内容在被调函数体结束后就会由系统自动将内存回收,而动态内存分配的变量在函数结束后
//不会立即释放,需要程序员手动添加代码,手动释放堆区中的内存
linklist *La=(linklist *)malloc(sizeof(linklist));
linklist *Lb=(linklist *)malloc(sizeof(linklist));
linklist *Lc;
if(!L)
return ERROR;
printf("Please create a linklist:\n");
CreateList(L);
printf("the list after create is ;\n");
display(L);
reverse(L);
printf("The list after reverse is :\n");
display(L);
printf("please input the insert lo & elem:\n");
scanf("%d%d",&i,&e);
listinsert(L,i,e);
printf("The list after insert is ;\n");
display(L);
printf("please input the del number :\n");
scanf("%d",&di);
listdel(L,di,&de);
printf("The del num is %d\n",de);
printf("The list after del is :\n");
display(L);
printf("create the list la:\n");
CreateList(La);
reverse(La);
printf("create the list lb:\n");
CreateList(Lb);
reverse(Lb);
printf("the list after merge is :\n");
Lc=MergeList(La,Lb);
display(Lc);
free(L);free(La);free(Lb);//手动释放堆中分配的内存
return 0;
}