1、线性链表的特点:
线性表的链式存储结构是用一组任意的存储单元(可以是连续的物理存储空间也可以是不连续的)存储数据元素。
2、结点:数据元素的存储映像。由数据域(存储数据元素信息的域)和指针域(存储直接后继存储位置的域)组成。
指针为数据元素之间的逻辑关系的映像。这种存储结构为非顺序映像或链式映像。
n个结点组成链表,又称为单链表或线性链表。
3、整个链表的存储必须从头指针开始,头指针指示链表中的第一个结点的存储位置。
由于最后一个数据元素没有直接后继,单链表中最后一个结点的指针为空(NULL)。
4、单链表可以由头指针唯一确定。
//线性表的链式存储结构
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
5、头结点
头结点位于单链表的第一个结点之前,数据域可以不存储任何数据,也可以存储线性表的长度之类的附加信息;指针域存储指向第一个结点的指针。
6、顺序表和单链表的区别
1)线性表的顺序存储结构中,任何两个元素的存储位置之间都可以从顺序表的起始位置计算得到,而在单链表中任何两个元素的存储位置之间没有固定的联系。
2)单链表是一种动态存储结构,整个可用存储空间可为多个链表共同享用,每个链表占用的空间可由系统应需求及时生成,不需要预先分配划定。
7、GetElem函数在单链表中的算法体现
//L为带头结点的单链表的头指针,当第i个元素存在时,其值赋给e并返回1,否则返回-1
int GetElem_L(LinkList L, int i, int &e){
LNode* p = L->next; int j = 1;
while (p&&j < i){
p = p->next; ++j;
}
if (!p || j>i)return -1;
e = p->data;
return 1;
}
8、在单链表中实现插入和删除操作
在单链表元素a和b之间插入数据元素x,1)生成一个数据域为x的结点 2)修改a的指针域,令其指向x 3)x的指针域指向结点b 用语句描述即为s->next=p->next;p->next=s;
//在带头结点的第i个位置前插入元素e
int ListInsert_L(LinkList &L, int i, int e){
LNode* p;
p = L; int j = 0;
while (p&&j < i - 1)
{ p = p->next; ++j; }//寻找第i-1个结点
if (!p || j>i - 1)return -1;
LNode *s = (LinkList)malloc(sizeof(LNode));//由系统生成一个LNode型的结点s
s->data = e;
s->next = p->next;
p->next = s;
return 1;
}
在线性链表a->b->c中,删除数据元素b,仅需要修改结点a中的指针域即可。语句描述为p->next=p->next->next;
//在带有头结点的线性链表L中删除第i个元素,并由e返回。
int ListDelete_L(LinkList &L, int i, int e){
LNode *p = L; int j = 0;
LNode *q;
while (p->next&&j < i - 1){//让p指向第i个结点的前驱
p = p->next; ++j;
}
if (!(p->next) || j>i - 1)return 0;
q = p->next; p->next = q->next; e = q->data; free(q);//新结点q记录要删除的第i个结点元素最后再由系统回收LNode型结点
return e;
}
9、单链表的创建
由于用指针创建的单链表是一种动态存储结构(用数组描述的单链表为静态结构),因此单链表的建立过程就是一个动态生成链表的过程。从空表的状态开始,依次建立结点并插入链表。
1)尾插入法
//逆位序输入n个元素的值,建立带表头结点的单链线性表L
void createList_L(LinkList &L, int n){
//创建一个带头结点的单链表
LNode *p;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
for (int i = n; i >= 1; i--){
p = (LinkList)malloc(sizeof(LNode));//创建新结点
scanf_s("%d",&p->data);//输入元素值
p->next = L->next; L->next = p;//插入到表头
}
}
2)头插入法
10、将两个有序的链表合并为一个链表
//La和Lb非递减排序,归并后的Lc也是非递减序列
void MergeList(LinkList &La, LinkList &Lb, LinkList &Lc){
LinkList pa, pb, pc;
pa = La->next; pb = Lb->next; pc = Lc; pc = La;//用La的头结点作为Lc的头结点
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;
free(Lb);
}
11、线性表的静态单链表存储结构(用一维数组来描述线性链表)
这种方法便于在不设“指针”类型的高级程序设计语言中使用链表结构。
要点:1)数组的一个分量表示一个结点;
2)用游标(指示器cur)代替指针指示结点在数组中的相对位置;
3)数组的第零分量可看成头结点,其指针域指示链表的第一个结点;
4)需要预先分配一个较大的存储空间(初始空间大小固定与顺序表类似);
5)在做线性表的插入和删除操作时不需要移动元素,仅需修改指针。
6)在静态链表中实现数据元素的插入和删除操作,为了分辨数组已使用和未使用的分量,将所有未被使用过以及删除的分量用游标链成一个备用的链表,插入时可从备用链表上取得第一个结点作为待插入的新结点;反之在删除时将从链表中删除下来的结点链接到备用链表上。
//用一维数组描述单链表
#define MAXSIZE 1000
typedef struct{
int data;
int cur;
}component,SLinkList[MAXSIZE];
假设S为SLinkList型变量,则S[0].cur指示第一个结点在数组中的位置
若设i=S[0].cur则S[i].data存储线性表第一个数据元素,S[i].cur指示第二个结点的位置
一般情况下若i表示第k个结点,则s[i].cur指示的是第k+1个元素的位置
i=S[i].cur的操作实为指针后移(P=P->next)
12、循环链表
特点:表中最后一个结点的指针域指向头结点,整个链表链成一个环,从表中任意一个结点出发均可以找到表中其他任意结点。
13、双向链表
双向链表的结点中有两个指针域,一个指向直接前驱,一个指向直接后继。
在双向链表中插入一个元素的关键语句:
s=(DuLinkList)malloc(sizeof(LNode));
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
在双向链表中删除一个元素:
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
双向链表是链表中最常用的一种结构,要求重点掌握。