链表是一种物理存储上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
建立结点结构体
C
//定义一个结点
#include<stdio.h>
typedef int ElemType;
typedef struct node
{
ElemType e;//数据域
struct node *next;//指针域
}LinkList;
链表是由一个个结点所构成,首先定义一个结点的结构体。
该结构体包括存放数据的数据域和指向结点所需的指针域,分别由ElemType e 和 node *next所定义,该指针域直接指向后继元素。
每一个链表中的结点即是该LinkList结构体。
每当需要一个链表时,即可调用这个模子(结构体),完成一个链表的创建。
链表的初始化
C
//初始化结点中的指针 令指针域为空
void Initnode(LinkList *n)
{
n->next=NULL;
}
链表的初始化分为结点和表的初始化。
定义Initnode函数完成对指针域的初始化。
C
//创建一个空表
void InitLinkList(LinkLinst * &h) //头指针->头结点
{
h=new LinkList; //动态要一个结点
Initnode(h); //指针域初始化
}
定义InitLinkList函数完成对一个空链表的建立。
函数参数中'&'可理解为对建立的头结点需进行地址的访问。
函数内部第一条语句通过一个new函数向系统内存动态的取得一个头结点,一个头结点即是一个空链表的标志。
最后完成对指针域的初始化。
链表的创建
C
//创建一个链表
void CreateLink(LinkList * &h)
{
LinkList *p,*t;//p指向最后一个结点,t动态要一个结点
p=h;
while(1)
{ ElemType e;
scanf("%d",&e); //输入数据
if(e==-1) break;
t=new LinkList; //尾部获取新结点
Initnode(t); //新结点初始化
t->data=e; //所输入数据存储到的新的结点的数据域中
p->next=t;
p=p->next; //指针的定向移动
}
链表的建立可采取头插法和尾插法,此处采用尾插法。
定义CreateLink函数完成对链表的建立。
函数内部定义两个指针,分别用来指向后继结点和获取新结点。
通过一个while函数,在两个指针的定向移动中完成对链表的输入。
定义e变量来存放输入的元素。
此处采用的是数据-1为链表的终止标志。(if语句)
基本操作
1.取值
C
//取值
void GetElem(LinkList *h,int i,ElemType &e)
{
LinkList *p=h->next;
int j=1;
while(p&&j<i) //指针移动
{ p=p->next;
j++;
}
if(!p||i<j) return; //判断i是否合法
e=p->data; //将该结点的值存放在e中
}
定义GetElem函数完成取值操作。
在函数内部从首结点开始完成指针的移动,在移动时要保证p指针不能为空且j不能与i相等,一旦相等,说明到达了该地址的结点,循环结束。
if循环来判断i是否合法。
最后将p指针所指的结点数据保存在e中,完成取值操作。
2.查找
C
//查找
void Find(LinkList *h,ElemTyep e)
{
LinkList *p=h->next;
int j=1;
while(p&&p->data!=e)
{ p=p->next;
j++;
}
if(p) return j;
return 0;
}
定义Find函数完成查找操作。
查找操作与取值操作十分相似,在函数内部的while循环中,首先保证p指针不为空,其次保证在指针在数据域中的筛选,当输入值e与某结点数据相等时,结束循环。
若此时p不为空则返回坐标j,完成查找操作。
3.插入
C
//插入(插在第i个结点之前)
void Insert(LinkList *&h,int i,ElemType e)
{
LinkList *p=h,*t;
int j=0; //此处从头结点开始插入,j=0
while(p&&j<i-1)
{ p=p->next;
j++;
}
if{!p||i<1} return;
t=new LinkList;
InList(t);
t->data=e;
t->next=p->next;
p->next=t;
}
定义Insert函数完成插入操作。
由于插入操作可以从头结点后开始(即插入第一个结点),故此时定义的指针p需指向头结点且j=0。
t为新结点(插入结点)。
t->next=p->next; 语句完成t结点与i结点的链接。
p->next=t; 语句完成t与i-1结点的链接。
4.删除
C
//删除
void Del(LinkList * &h,int i)
{
LinkList *p=h,*q;
int j=0;
while(p->next&&j<i-1){p=p->next;j++;}
if(!(p->next)||i<1) return;
q=p->next;
p->next=q->next;
delete q;
定义Del函数完成删除操作。
和查找类似,由于可在头结点后进行操作,故将指针p的初始位置指向头结点且j=0。
经历循环过后,p指针指向i的前驱。
q=p->next; 语句完成q指针指向i结点的操作,此时q->next的地址为i结点后继的地址。
p->next=q->next; 语句完成i的前驱与i的后继的链接,i结点被隔离。
delete q; 完成删除操作。
5.遍历列表
C
//遍历
void Display(LinkList *h)
{
LinkList *p=h->next;
while(p)
{
printf("%d->",p->data);
p=p->next;
}
printf("\n");
代码测试
C
int main()
{
LinkList *h;
ElemType e;
InitLink(h);
CreateLink(h);
Display(h);
GetElem(h,3,e);printf("%d\n",e);
printf("%d\n",Find(h,4));
Insert(h,4,10);
Display(h);
Del(h,3);
Display(h);
return 0;
}
