前言:
前面介绍了顺序存储结构,了解完后我们发现顺序表在使用时需要事先占用一大块存储空间,这大大降低了存储空间的利用率,而且在实现一些操作时需要移动大量元素,时间复杂度大大提高。为了解决这些问题就有了链式存储结构,它可以实现存储空间的动态管理。
1.链表
三种链表如图所示,通过示意图便可了解链表中每个内存结点不仅有包含信息的数据域,还包含了表示元素之间逻辑关系的信息(指针域) ,这次我们先了解单链表,理解好单链表对于理解其他形式的链表会有很大帮助。
2.单链表
除数据域外只设置一个指针域,用来指向它的后继结点,这样就构成了单链表。这是最简单最常用的方法。
3.单链表的相关操作
3.1 定义存储结构
存储元素的数据域用data表示,指针域用next表示,在后面的操作中我们均采取带头结点的单链表,即单链表是否为空都有一个头结点。
//建立存储结构
typedef struct LNode
{
Elemtype data; //存放数据
struct LNode *next; //指向后继结点
}LinkNode;
3.2 初始化线性表
因为我们建立的是带头结点的线性表,所以就要创建一个头节点并将它的next域置为空。
//初始化
void InitList(LinkNode *& L)
{
L=(LinkNode *)malloc(sizeof(LinkNode));
L->next=NULL; //创建头节点后继指针指向null
}
3.2 插入、删除结点
这里通过示意图理解插入结点的步骤:
插入前
s->next=p->next;
p->next=s;
插入后
了解这个过程后我们就可以依据原理实现在单链表的指定位置插入元素,这里我们设要在第i个位置插入元素,首先我们需要先找到第i-1个结点并让p指针指向它,如果存在这样的结点,就按上面的过程插入结点。
//插入值
int ListInsrt(LinkNode *& L,int i,Elemtype e)
{
int j=0;
LinkNode *p=L,*s;
if(i<=0) return false;
while(j<i-1&&p!=NULL) //查找第i-1个节点
{
j++;
p=p->next;
}
if(p==NULL) return false;
else
{
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
}
删除节点时可分为如图所示的三步,原理代码:
q=p->next; //q用来临时保存被删除结点
p->next=q->next; //删除结点q
free(q); //释放结点q的内存
依据原理就可以实现删除链表中指定位置的元素。
//删除节点
int deleteList(LinkNode *& L,int i)
{
int j=0;
LinkNode *p=L,*q;
if(i<=0) return false;
while(j<i-1&&p!=NULL) //找到要删除结点的前驱结点
{
j++;
p=p->next;
}
if(p==NULL) return false;
else
{
q=p->next;
if(q==NULL)
return false;
p->next=q->next;
free(q);
return true;
}
}
通过这两项操作我们就可以发现链式存储结构相较于,顺序存储结构的优点——不需要大量移动元素就可以实现对数据的插入和删除。
3.3 查找结点
void findElem(LinkNode *L,Elemtype e)
{
int i=1;
LinkNode *p=L->next;
while (p!=NULL&&p->data!=e)
p=p->next;
if(p==NULL)
printf("不存在\n");
else
printf("存在\n");
}
3.4 输出链表
这一操作相对简单,只需定义一个指向首结点的指针,指针不为空就输出指针的数据域,并一项下一个节点如此往复。
//输出表
void DispList(LinkNode * L)
{
LinkNode *p=L->next;
while(p!=NULL)
{
printf("%d",p->data);
p=p->next;
printf("\n");
}
}
3.5 销毁线性表
逐一释放全部结点的空间,定义两个指向相邻结点的指针(pre、p),开始时pre指向头结点,p指向首结点。当p不为空时,循环下面操作:释放结点pre,然后pre、p同时移向下一个结点。循环结束后,pre指向尾结点,再将其释放。
//销毁表
void Destroy(LinkNode *& L)
{
LinkNode *pre=L, *p=L->next; //pre指向p结点的前驱结点
while(p!=NULL)
{
free(pre);
pre=p;
p=pre->next;
}
free(pre);
}
4.完整代码及测试
#include<stdio.h>
#include<malloc.h>
typedef int Elemtype;
//建立存储结构
typedef struct LNode
{
Elemtype data; //存放数据
struct LNode *next; //指向后继结点
}LinkNode;
//初始化
void InitList(LinkNode *& L)
{
L=(LinkNode *)malloc(sizeof(LinkNode));
L->next=NULL; //创建头节点后继指针指向null
}
//插入值
int ListInsrt(LinkNode *& L,int i,Elemtype e)
{
int j=0;
LinkNode *p=L,*s;
if(i<=0) return false;
while(j<i-1&&p!=NULL) //查找第i个节点
{
j++;
p=p->next;
}
if(p==NULL) return false;
else
{
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
}
//删除节点
int deleteList(LinkNode *& L,int i)
{
int j=0;
LinkNode *p=L,*q;
if(i<=0) return false;
while(j<i-1&&p!=NULL) //找到要删除结点的前驱结点
{
j++;
p=p->next;
}
if(p==NULL) return false;
else
{
q=p->next;
if(q==NULL)
return false;
p->next=q->next;
free(q);
return true;
}
}
//输出表
void DispList(LinkNode * L)
{
LinkNode *p=L->next;
while(p!=NULL)
{
printf("%d",p->data);
p=p->next;
printf("\n");
}
}
//销毁表
void Destroy(LinkNode *& L)
{
LinkNode *pre=L, *p=L->next; //pre指向p结点的前驱结点
while(p!=NULL)
{
free(pre);
pre=p;
p=pre->next;
}
free(pre);
}
//查找结点
void findElem(LinkNode *L,Elemtype e)
{
int i=1;
LinkNode *p=L->next;
while (p!=NULL&&p->data!=e)
p=p->next;
if(p==NULL)
printf("不存在\n");
else
printf("存在\n");
}
int main()
{
LinkNode *L;
Elemtype e,h;
int i;
InitList(L);
ListInsrt(L,1,2);
ListInsrt(L,2,8);
ListInsrt(L,3,10);
ListInsrt(L,4,6);
DispList(L);
printf("输入要查找值:");
scanf("%d",&e);
findElem(L,e);
printf("输入删除结点的位置:");
scanf("%d",&i);
deleteList(L,i);
DispList(L);
Destroy(L);
return 0;
}
结果:
努力学习中,不足之处还请多多指教。