线性表的链式存储又称单链表,它是指任意一组存储单元来存储线性表中的数据元素。
data(数据域) | next(指针域) |
---|
typedef struct LNode{
ElemType data; //数据域
struct LNode *next;//指针域
}LNode, *LinkList;
通常用头指针来标识一个单链表,如单链表L,头指针为NULL时为空表。单链表在第一个结点前附加一个结点,称为头结点。头结点的数据域可以不设任何值,也可以存储表长等相关信息。
头结点和头指针的区分:
不管带不带头结点,头指针始终指向链表的第一个结点。
引入头结点可以带来两个优点:
1)由于开始结点的位置被存放在头结点的指针域,所以在链表的第一个位置上的操作和在表中的其他位置的操作是一致的,无须特殊处理。
2)无论链表是否为空,头指针都指向头结点的非空指针,因此空表和非空表也得到了统一。
LinkList List_HeadInsert(LinkList &L){
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode));//创建头结点
L->next=NULL;//初始空链表
scanf("%d",&x);
while(x!=9999){//输入9999标识结束
s=(LNode*)malloc(sizeof(LNode));//创建新结点
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
每个结点插入时间为O(1),则时间复杂度为O(n)
2)采用尾插法建立单链表
该方法将新结点插入到当前链表的表尾,为此必须增加一个尾指针r。
LinkList List_TailInsert(LinkList &L){
int x;
L=(LinkList)malloc(sizeof(LNode));
LNode *s, *r=L;//r为表尾指针
scanf("%d",&x);
while(x!=9999){
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r=s;//r指向新的表尾结点
scanf("%d",&x);
}
r->next=NULL:
return L;
}
时间复杂度为O(n),和头插法相同
3)按序号查找结点值
LNode *GetElem(LinkList L,int i){
int j=1;
LNode *p=L->next;//头结点指针赋值给p
if(i==0) return L;
if(i<1) return NULL;
while(p&&j<i){//从第一个结点开始找,找第i个
p=p->next;
j++;
}
return p;
}
时间复杂度为O(n)
4)按值查找表结点
LNode *LocateElem(LinkList L,ElemType e){
LNode *p=L->next;
while(p!=NULL&&p->data!=e){
p=p->next;
}
return p;
}
时间复杂度O(n)
5)插入结点操作
p=GetElem(L,i-1);
s-next=p->next;//s为新结点
p->next=s;
三个语句的顺序不可以乱
p=GetElem(L,i-1);//找到被删除结点的前驱结点
q=p->next;
p->next=q->next;
free(q);
时间复杂度O(n)
单链表结点只有一个指向其后的指针,使得单链表只能从头结点依次顺序的向后遍历。为克服单链表缺点,引入双链表,双链表中有两个指针prior和next,分别指向前驱和后继结点。
*prior | data | *next |
---|
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
1)双链表的插入操作
s->next=p->next;
p->next->prior=s;
s->prior=p;
p->next=s;
p->next=q->next;
q->next->prior=p;
free(q);
循环双链表L头结点的prior指向表尾结点,表尾结点的next指向L。当循环双链表为空时,其头结点的prior和next都等于L。
静态链表借助数组来描述线性表的链式存储,结点也有data和next
#define MaxSize 50
typedef struct{
ElemType data;
int next;//存储下一个元素的数组下标
}SLinkList[MaxSize];
静态链表以next等于-1作为结束
1)存储方式
顺序表可以顺序存储也可以随机存储,链表只能从表头到表尾存取元素。
2)逻辑结构和物理结构
采用顺序存储时,逻辑上相邻的元素,对应物理存储位置也相邻。而采用链式存储时,逻辑上上相邻的元素,物理存储位置不一定相邻。
3)查找、插入和删除操作
对于按值查找,顺序表无序时,两者的时间复杂度均为O(n)。顺序表有序时,可采用折半查找,时间复杂度为O(logn)。
对于按序号查找,顺序表支持随机访问,时间复杂度仅为O(1),而链表的时间复杂度为O(n)。