线性链表的基本操作如下:
- 相关结构体:
一个线性链表的结构必须包含数据域和指针域
#define OVERFLOW 1
#define ERROR 0
#define OK 1
typedef int Status;
typedef strcut LNode /*节点类型*/
{
ElemType data; //数据域
struct LNode *next; //指针域
}*Link,*LinkList,LNode;
- 函数声明:
Status InitList(LinkList &L); /*构造一个空的线性链表*/
Status ListEmpty(LinkList L); /*线性链表是否为空*/
Status CreatList(LinkList &L,int n); /*逆序输入n个元素的值,建立带头结点L*/
Status ClearList(LinkList &L); /*将线性链表重置为空,并释放原表的节点空间*/
Status DestroyList(LinkList &L); /*销毁线性链表L*/
Status ListLength(LinkList L); /*线性链表长度*/
void FreeNode(Link &p); /*释放p指向的节点*/
Status LocatePos(LinkList L,int i,Link &p); /*返回p指示L中第i个节点的位置*/
LinkList PriorPos(LinkList L,Link p); /*返回p的直接前驱*/
LinkList NextPos(LinkList L,Link p); /*返回p的直接后继*/
LinkList GetHead(LinkList L); /*返回L中头结点的位置*/
LinkList GetLast(LinkList L); /*返回L中尾节点的位置*/
ElemType GetCurElem(LinkList L,int i,ElemType &e); /*已知p指向L中的第i个节点,则返回该节点的值*/
Status InsFirst(Link h,Link s); /*已知h指向头结点,将s所指向的节点插到头结点之前*/
Status ListInsert(LinkList &L,int i,ElemType e); /*在表L第i个位置之前插入元素e*/
Status ListDelete(LinkList &L,int i,ElemType &e); /*删除表第i个位置元素,并用e返回其值*/
Status ListTraverse(LinkList L); /*遍历整个线性链表L*/
- 构造一个空的线性链表L:
一个带头结点空的线性单链表它的头结点的指针必须指向空
Status InitList(LinkList &L); /*构造一个空的线性链表*/
{
L = (LinkList)malloc(sizeof(LNode)); //分配内存空间
if(!L)
return ERROR;
L->next = NULL;
return OK;
}
- 判断线性链表是否为空:
只需判断其头指针是否指向空
Status ListEmpty(LinkList L); /*线性链表是否为空*/
{
if(!L->next) //首节点是否为空
return ERROR;
else
return OK;
}
- 逆序输入n个元素的值,建立带头结点L:
先创建一个带头结点,并让其指针指向空,然后再创建新节点,并向新节点的数据域内写入相应的值,插入到表头即头结点之后,然后再继续进行该操作,直至插入完n个值
Status CreatList(LinkList &L,int n); /*逆序输入n个元素的值,建立带头结点L*/
{
L = (LinkList)malloc(sizeof(LNode)); //头结点的创建
L->next = NULL;
for(int i = 0;i < n;i++)
{
LinkList p = (LinkList)malloc(sizeof(LNode)); //生成新节点
scanf("%d",&p->data); //输入元素值
p->next = L->next; //插入到表头
L->next = p;
}
return OK;
}
- 将线性链表重置为空,并释放原表的节点空间:
先取表L的头指针,然后从头指针指向节点开始删除,删除具体操作为不断的后移头指针指向节点,前一个覆盖后一个,直至指向空,并依次释放无用节点 ,保留头结点
Status ClearList(LinkList &L); /*将线性链表重置为空,并释放原表的节点空间*/
{
LinkList p;
p = L->next; //指针p等于L头指针
while(p) //p中指针指向非空条件执行循环
{
L->next = p->next; //删除除头结点之外所有节点
free(p); //释放无用节点空间
p = L->next;
}
return OK;
}
销毁线性链表L:
从头结点开始,不断后移一位,并释放此节点空间,包括头结点
Status DestroyList(LinkList &L); /*销毁线性链表L*/
{
LinkList p;
while(L) //L中指针指向非空条件执行循环
{
p = L->next; //p指向为头指针L指向节点
free(p); //释放p指向的节点
L = p; //后移一位
}
return OK;
}
- 求线性链表长度:
取表的头指针,然后不断后移并计数,最后计数大小即为长度
Status ListLength(LinkList L); /*线性链表长度*/
{
int i = 0;
LinkList p;
p = L->next; //p指向头指针指向空间
while(p) //p中指针指向非空条件执行循环
{
n++; //长度计数
p = p->next; //后移一位
}
return OK;
}
- 释放p指向的节点:
从p的指向的第一个节点起开始释放并依次后移
void FreeNode(Link &p); /*释放p指向的节点*/
{
while(p)
{
Link q = p->next;
free(p);
p = q;
}
}
- 返回p指示L中第i个节点的位置:
从表头开始计数,并不断后移节点,直至为空或找到与i相等的节点,此时返回i的上一节点的指针
Status LocatePos(LinkList L,int i,Link &p); /*返回p指示L中第i个节点的位置*/
{
int n = 0;
p = L->next; //指针p指向表头
while(p)
{
n++;
if(n == i) //判断是否为第i个节点
break;
p = p->next;
}
return p;
}
- 返回p的直接前驱:
因为直接前驱不能在头指针之前,所以先取p为第二个节点,然后带入查找函数,用p表示位置i的节点,先判断是否第i个节点是否在表中存在,即不能是头结点,然后直接取前驱即可
LinkList PriorPos(LinkList L,Link p); /*返回p的直接前驱*/
{
ElemType ⪯ //直接前驱
p = p->next->next;
p = Status LocatePos(L,i,p); //查找p的位置
if(!p)
return ERROR;
else
{
L->next = p->next;
p->next = L; //取直接前继
pre = p->data;
}
return OK;
}
- 返回p的直接后继:
先取p为头指针,找到p的位置i后,然后带入查找函数,用p表示位置i的节点,然后去取直接后继即可
LinkList NextPos(LinkList L,Link p); /*返回p的直接后继*/
{
Link next; //直接前驱
p = L->next; //将第一个元素的地址赋给p
p = Status LocatePos(L,i,p); //查找p的位置
if(!p->next)
return ERROR;
else
{
next = p->next->data; //取直接后继
}
return OK;
}
- 返回L中头结点的位置:
直接取表头地址即为头节点位置
LinkList GetHead(LinkList L); /*返回L中头结点的位置*/
{
Link p;
p = L->next; //p为头结点地址
return p;
}
- 返回L中尾节点的位置:
先取表头地址,然后不断后移,直到为空时该地址的上一位即为尾节点位置
LinkList GetLast(LinkList L); /*返回L中尾节点的位置*/
{
Link p;
p = L->next; //p为头结点地址
while(p)
{
p = p->next;
}
return p;
}
- 已知p指向L中的第i个节点,则返回该节点的值:
从头节点开始计数,直到计数大小等于第i个节点,且对应的该节点必须是线性链表中的节点,最后返回该节点的值
ElemType GetCurElem(LinkList L,int i,ElemType &e); /*已知p指向L中的第i个节点,则返回该节点的值*/
{
int j = 1;
Link p = L->next; //p指向L的首节点
for(;p&&j < i;++j)
{
p = p->next; //后移一位
}
if(!p||j > i)
return ERROR;
e = p->data; //将第i个位置元素值赋给e
return OK;
}
- 已知h指向头结点,将s所指向的节点插到头结点之前:
用头插法进行插入
Status InsFirst(Link h,Link s); /*已知h指向头结点,将s所指向的节点插到头结点之前*/
{
s->next = h->next; //插到表头
s->next = h;
return OK;
}
- 在表L第i个位置之前插入元素e:
从表头开始计数,因为是在i的前一个节点进行插入,所以当计数大小等于i时,且此时节点仍是表中节点时,再创建一个新节点,将元素e写入新节点中,最后将其用头插法插第i个位置处
Status ListInsert(LinkList &L,int i,ElemType e); /*在表L第i个位置之前插入元素e*/
{
int j = 1;
LinkList p = L;
while(p&&j < i)
{
p = p->next; //寻找第i个节点
j++;
}
if(!p||j > i) //i小于1或大于i
return ERROR;
LinkList s = (LinkList)malloc(sizeof(LNode));
s->data = e; //给新节点赋值
s->next = p->next;
s->next = p; //将新节点插在第i个位置处
}
- 删除表第i个位置元素,并用e返回其值:
从表头开始计数,当计数大小等于i时,且此时节点仍是表中节点时,取第i个位置节点指针的前驱p,再创一个空节点,令它等于前驱节点的下一个节点,最后返回该节点的数据即可
Status ListDelete(LinkList &L,int i,ElemType &e); /*删除表第i个位置元素,并用e返回其值*/
{
int j = 1;
LinkList p = L;
while(p->next&&j < i) // 寻找第i个节点,并让p指向其前驱
{
p = p->next;
j++;
}
if(!(p->next)||j > i) //i小于1或大于i-1
return ERROR;
Link q = p->next;
q->next = p->next; //删除并释放节点
free(p->next);
e = q->data;
return OK;
}
- 遍历整个线性链表L:
取表的头节点,不断后移,并输出其中数据,直至节点为空
Status ListTraverse(LinkList L); /*遍历整个线性链表L*/
{
LinkList p;
p = L->next; //p指向L的头结点
while(p != NULL)
{
printf("%d",p->data) //依次输出L中的元素
p = p->next;
}
printf("\n")
return OK;
}
- 主函数:
#include<stdio.h>
#include<stdlib.h>
typedef strcut LNode /*节点类型*/
{
ElemType data; //数据域
struct LNode *next; //指针域
}*Link,*LinkList,LNode;
int main()
{
LinkList L;
InitList (L);
int n,i;
Link p,q;
ElemType e;
while(1)
{
printf("*************************************************\n");
printf("*[1]判断是否为空表 [2]写入n个数 *\n");
printf("*[3]将表L清空 [4]销毁表L *\n");
printf("*[5]线性链表长度 [6]查找第i个元素位置 *\n");
printf("*[7]返回i的直接前驱 [8]返回i的直接后继 *\n");
printf("*[9]表L头结点位置 [10]表L中尾节点位置 *\n");
printf("*[11]返回第i个节点的值[12]将新节点插到头结点前 *\n");
printf("*[13]表第i个位置前插入[14]删除表第i位置元素 *\n");
printf("* [15]遍历整个线性链表 *\n");
printf("*************** [0]退出系统 ******************\n");
printf("*************************************************\n");
printf("请选择: ");
scanf("%d",&choice);
switch(choice)
{
case 0: break;
case 1: ListEmpty(L);
break;
case 2: printf("请输入数据个数n,然后依次输入数据\n");
scanf("%d",&n);
Status CreatList(L,n); //逆序输入n个元素的值,建立带头结点L
break;
case 3: ClearList(L); //将线性链表重置为空,并释放原表的节点空间
break;
case 4: DestroyList(L); //销毁线性链表L
break;
case 5: ListLength(L); //线性链表长度
printf("%d\n",n);
break;
case 6: printf("请输入位置i\n");
scanf("%d",&i);
LocatePos(L,i,p);//返回p指示L中第i个节点的位置
break;
case 7: printf("请输入位置i\n");
scanf("%d",&i);
PriorPos(L,p); //返回p的直接前驱
break;
case 8: printf("请输入位置i\n");
scanf("%d",&i);
NextPos(L,p); //返回p的直接后继
break;
case 9: GetHead(L); //返回L中头结点的位置
break;
case 10: GetLast(L); //返回L中尾节点的位置
break;
case 11: printf("请输入位置i\n");
scanf("%d",&i);
GetCurElem(L,i,e); //已知p指向L中的第i个节点,则返回该节点的值
printf("%d\n",e);
break;
case 12: printf("请输入s节点中数据\n");
scanf("%d",&s->data);
InsFirst(Link h,Link s); //已知h指向头结点,将s所指向的节点插到头结点之前
break;
case 13: printf("请输入位置i\n");
scanf("%d",&i);
ListInsert(L,i,e); //在表L第i个位置之前插入元素
break;
case 14: printf("请输入位置i\n");
scanf("%d",&i);
ListDelete(L,i,e); //删除表第i个位置元素,并用e返回其值
printf("%d\n",e);
break;
case 15: ListTraverse(L); /、遍历整个线性链表L
}
}
FreeNode(p); //释放p指向的节点