单向链表操作的实现(验证性实验)
1. 需求分析
单向链表是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点。基本操作包括:生成链表,添加节点,返回节点,删除节点,查找结点,遍历链表。
(1) 输入的形式和输入值的范围:程序是按照数字选择对应功能的,各项功能中,除了退出程序,其他功能都要输入一个int型数字,输出的数字也为整数。
(2) 输出的形式:插入成功后显示完成;删除成功后返回完成;查找到对应位置后显示对应位置的数字,再显示完成;退出时显示链表。
(3) 程序所能达到的功能:对链表实行插入,删除,查找,遍历这些操作。
(4) 测试数据:
· 执行5次插入操作,依次输入数据1,2,3,4,5;
· 查找第一个数据;
· 删除查找的第一个数据;
· 再次查找第一个数据;
· 退出并显示链表。
2. 概要设计
- 为了实现程序功能,需要定义单向链表的抽象数据类型。
ADT List {
数据对象:D={ai |ai ∈ElemSet,i = 1,2,…,n,n>=0}
数据关系:RI={<ai-1,ai>|ai-1,ai∈D,i = 2,…,n}
typedef struct lnote{
elemtype data;
struct lnote *next;
}*node;
typedef struct LIST{
node head;
node tail;
int len;
}list;
· 基本操作:
createlist (list &L)
操作结果:构造一个空的单向链表
add_node(list &L,elemtype e)
初始条件:单循环链表L存在
操作结果:在L的第1个位置之前插入元素e
return_node(list L,elemtype,e)
初始条件:单循环链表L存在
操作结果:返回L的第e个节点
delete_node(list &L,int e)
初始条件:单循环链表L存在
操作结果:删除L中值为e的一个节点
find_node(list L,elemtype n,int &e)
初始条件:单循环链表L存在
操作结果:返回L中值为n的一个节点,并令e等于该节点的位置。
Listtraverse(list L,int (*visit)())
初始条件:单循环链表L存在
操作结果:遍历L
} ADT List
3.具体代码
#include<stdio.h>
#include<stdlib.h>
#define elemtype int
#define ERROR 0
#define OK 1
typedef struct lnote{ //节点结构
elemtype data; //节点数值
struct lnote *next;
}*node;
typedef struct LIST{ //链表表结构
node head; //头指针
node tail; //尾指针
int len; //链表长度
}list;
int visit(node p) //打印节点
{
if(p)
{
printf("%d ",p->data);
return OK;
}
return ERROR;
}
void createlist(list *p); //生成链表
void add_node(list *p,elemtype num); //添加节点
node return_node(list p,int num); //返回节点
void delete_node(list *p,int num); //删除节点
node find_node(list p,elemtype n,int &number); //查找结点
void listtraver(list l,int (*visit)(node)); //遍历链表
int main()
{
list test;
createlist(&test);
do
{
printf("请选择:1.插入 2.删除 3.查找 4.退出:\n");//让用户选择
int i,j,k;
scanf("%d",&i);
switch(i)
{
case 1:
do
{
printf("请输入插入的值:");
scanf("%d",&j);
add_node(&test,j);
printf("完成\n");
}while(0);
break;
case 2:
do
{
printf("请输入要删除的节点的值:");
scanf("%d",&j);
find_node(test,j,k);
delete_node(&test,k);
printf("完成\n");
}while(0);
break;
case 3:
do
{
printf("请输入要查找结点的位置:");
scanf("%d",&j);
node test_node;
test_node=return_node(test,j);
printf("第%d个结点数值为%d\n",j,test_node->data);
printf("查找完成\n");
}while(0);
break;
case 4:
printf("链表:");
listtraver(test,visit);
return 0;
default:
printf("你的输入有误!请重新输入!\n");
break;
}
}while(1);
return 0;
}
void createlist(list *p) //生成链表
{
p->head = (node)malloc(sizeof(node));//为头指针指向的头节点分配储存空间
if(!p->head)//若分配空间失败
{
exit(ERROR);//直接退出
}
p->head->next = NULL;//头节点 指向null(初始条件)
p->tail = p->head; //初始时头尾指针指向同一个位置
p->len=0;//长度为0
}
void add_node(list *p,elemtype num) //添加节点
{
//输入num 为节点数据
node q;//创建一个新的指针节点
q = (node)malloc(sizeof(node));//为该指针分配一个内存空间
if(!q)//若分配空间失败
{
exit(ERROR);//直接退出
}
q->data = num;//赋值
q->next = NULL;//next域置空
p->tail->next = q;//使链表的尾部节点next指向添加的节点
p->tail = q;//再更新链表的尾指针,指向新的节点
p->len++;//链表长度+1
}
node return_node(list p,int num) //返回节点
{
//输入的num为返回节点的位置
if(p.len < num)
{
//如果链表长度小于所输入的长度,直接返回空
return NULL;
}
node q;//否则创建一个q用于返回
q = p.head;//先让q为头节点
int n=1;//用于for循环
for(;n++ <= num;q = q->next);//然后让q往后走num次
return q;
}
void delete_node(list *p,int num) //删除节点
{
//num为删除的节点顺序
if(p->len < num)
{
//如果链表长度小于所输入的长度,直接退出
exit(ERROR);
}
node q,r;//创建节点指针
q = p->head;
int n = 1;//用于for循环
for(;n++ < num;q = q->next);//使q往后走num-1次,指向定点位置的前驱节点
r=q->next;//r指向定点位置
q->next=r->next;//直接跨过定点节点
if(num == p->len)
{
//如果num就是链表的长度,即删除的是尾节点
p->tail = q;//还需要额外更新tail的指向
}
free(r);//释放r的空间(定点)
p->len--;//更新长度,减一
}
node find_node(list p,elemtype n,int &number) //查找结点
{
//按数据查找结点
node q;//创建结点指针
q = p.head->next;//q指向p的头节点的后一个
int i = 0;//用于for循环
for(;++i <= p.len;q = q->next)//遍历链表,查找
{
if(q->data == n)
{
//如果发现位置
number = i;//则直接改变number值为位置
break;
}
}
if(!q)//如果找不到
{
number = -1;//返回
return NULL;
}
return q;
}
void listtraver(list l,int (*visit)(node)) //遍历链表
{
//visit为输入的函数
node p = l.head->next;//创建指针p指向头节点后面第一个节点
for(;p != NULL;p = p->next)//for循环遍历
{
if(!visit(p))//进行访问,如果访问失败直接跳出
{
exit(ERROR);
}
}
}