前言
由于顺序表的存储特点是用物理上的相邻实现了逻辑上的相邻,它要求用连续的存储单元顺序存储线性表中各元素,因此对顺序表的插入、删除时需要通过移动数据元素来实现,影响了运行效率并且会产生空间浪费,为此引入线性表另一结构—链式存储
如图是一个单向链表,表头变量head用来存放链表首结点的地址,链表中每个结点由数据部分和下一个结点的地址部分组成,即每个结点都包含指向下一个结点的指针。最后一个结点称为表尾,其下一个结点的地址部分的值为NULL(表示空地址),链表中的各个结点在内存中的存放可能是连续的,也可能是不连续的,具体存放位置由系统分配。
一、链式表的结构定义
typedef struct LNode * PtrToLNode;
struct LNode
{
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
二、求表长
在链式表中求表长需要将链表从头到尾遍历一遍,设一个移动指针p和计数器cnt,初始化后,p从表的第一个结点开始逐步往后移,同时cnt+1,当后面不再有结点时,cnt的值就是结点个数,即表长。具体代码如下:
int Length (List L)
{
Position p;
int cnt=0;//初始化计数器
p=L;//p指向第一个结点
while (p)
{
p=p->Next;
cnt++;//当前p指向的是第cnt个结点
}
return cnt;
}
二、查找
1、链式表的按序号查找
对于链式存储的查找,假设要查找第K个元素的值,需要从链表的第一个结点起,判断当前结点是否是第K个,若是,返回该结点的值,否则继续后一个,直到表结束为止,如果没有第K个结点则返回错误信息。
代码如下:
#define ERROR -1
ElementType FindKth(List L,int K)
{//根据指定的位序K返回L中相应的元素
Position p;
int cnt=1;
p=L;
while (p&&cnt<K)
{
p=p->Next;
cnt++;
}
if (cnt == K&&p)
return p->Data;
else
return ERROR;
}
2、链式表的按值查找
按值查找的基本方法也是从头到尾遍历,直到找到为止,从第一个结点起,判断当前结点的值是否等于X,若是,返回该结点的位置,否则继续后一个,直到表结束为止,找不到则返回错误信息。
代码如下:
#define ERROR NULL//用空地址表示错误
Position Find (List L, ElementType X)
{
Position p=L;//p指向L的第一个结点
while (p&&p->Data!=X)
p=p->Next;
if (p)
return p;
else
return ERROR;
}
三、插入
线性表的插入是在指定位序i(1<=i<=n+1)前插一个新元素X。当插入位序为1时,代表插入到链表的头,i为n+1时,代表插入到链表最后。基本思路是如果i不为1,则找到位序为i-1的结点pre,若存在,则申请一个新结点并在数据域填上相应值X,然后将新结点插入到结点pre之后,返回链表,如果不存在则返回错误信息。代码如下:
1、不带头结点的链式表的插入
#define ERROR NULL
List Insert(List L, ElementType X, int i)
{
Position tmp,pre;
tmp=(Position)malloc(sizeof(struct LNode));//申请,填装结点
tmp->Data=X;
if(i==1)//新结点插在表头
{
tmp->Next=L;
return tmp;//返回新表头指针
}
else
{//查找位序为i-1的结点
int cnt=1;
pre=L;
while(pre&&cnt<i-1)
{
pre=pre->Next;
cnt++;
}
if(pre==NULL||cnt!=i-1)//所找结点不在L中
{
printf("插入位置参数错误\n");
free(tmp);
return ERROR;
}
else//找到了待插结点的前一个结点pre
{
//插入新结点并返回表头
tmp->Next=pre->Next;
pre->Next=tmp;
return L;
}
}
}
2、带头结点的链式表的插入
bool Insert(List L, ElementType X, int i)
{
Position tmp,pre;
int cnt=0;
pre=L;//pre指向表头
while(pre&&cnt<i-1)
{
pre=pre->Next;
cnt++;
}
if(pre==NULL||cnt!=i-1)//所找结点不在L中
{
printf("插入位置参数错误");
return false;
}
else
{//找到了待插结点的前一个结点pre,插入新结点,若i=1,则pre指向表头
tmp=(Position)malloc(sizeof(struct LNode));//申请,填装结点
tmp->Data=X;
tmp->Next=pre->Next;
pre->Next=tmp;
printf("插入成功");
return true;
}
}
四、带头结点的链式表的删除
在单向链表中删除指定位序i的元素,首先需要找到被删除结点的前一个元素,然后再删除结点并释放空间。代码如下:
bool Delete( List L, int i)
{
//这里默认L有头结点
Position tmp,pre;
int cnt=0;
pre=L;//pre指向表头
while(pre&&cnt<i-1)
{
pre=pre->Next;
cnt++;
}
if(pre==NULL||cnt!=i-1||pre->Next==NULL)//所找结点或位序为i的结点不在L中
{
printf("删除位置参数错误");
return false;
}
else//找到了待删结点的前一个结点
{//将结点删除
tmp=pre->Next;
pre->Next=tmp->Next;
free(tmp);
printf("删除成功");
return true;
}
}