在2.6节阐述了纯链表的理论思想后,我们可以模仿链式数组表的代码编写方式,编写完整的可执行的链表程序。
为了简化编程模型,假设我们要处理的数据元素为整型,数据来源为键盘输入,添加main()函数,以及输入输出函数,按照软件函数化(模块化)方法设计。
首先,我们构造出元素结点与头结点:
#include <stdio.h>
#include <stdlib.h> //malloc,free需要
#include <time.h>
#include <stdbool.h>
struct node //构建元素结点
{
int data;
struct node *next;
};
struct list //构建左边头结点
{
int curr_length; //表长
char *create_date; //表创建时间
struct node *next; //指向第一个元素位置
};
构造初始化函数initlist():
struct list * initlist (void); //返回值是指向头结点的指针
struct list * initlist(void)
{
struct list *L = (struct list *)malloc(sizeof(struct list)); //动态分配
if (L==NULL)
printf("\n初始化失败!!\n");
L->curr_length =0; //初始化空表信息
L->create_date =__DATE__;
L->next = NULL;
return L; //返回分配好的指针
}
和链式数组表不同,无论是信息结构体的创建,还是元素部分的创建,不在代码中显式写出,而是通过动态分配完成,尽管显式写出在功能上没有差别,但违背了链表动态创建的最大特色。
销毁函数destroylist():
void destroylist(struct list *L);
void destroylist(struct list *L)
{
free(L);
}
该函数尚未完成,仅仅释放掉了头结点,而没有释放掉元素部分。
纯链表大概形态如图所示。
因为代码中需要大量使用struct结构体类型,我们可以引入typedef进行简化声明.
typedef struct node NODE;
typedef struct node* PTONODE;
typedef struct list LIST;
typedef struct list* PTOLIST;
改写后的完整程序如下:
<pre name="code" class="cpp">#include <stdio.h>
#include <stdlib.h> //malloc,free需要
#include <time.h>
#include <stdbool.h>
struct node //构建元素结点
{
int data;
struct node *next;
};
struct list //构建左边头结点
{
int curr_length; //表长
char *create_date; //表创建时间
struct node *next; //指向第一个元素位置
};
typedef struct node NODE;
typedef struct node* PTONODE;
typedef struct list LIST;
typedef struct list* PTOLIST;
//函数声明
PTOLIST initlist (void); //返回值是指向头结点的指针
void destroylist(PTOLIST L);
//函数声明结束
//主程序开始:
int main()
{
PTOLIST L = initlist();
destroylist(L);
return 0;
}
//主程序结束
//函数实现部分:
//初始化
PTOLIST initlist(void)
{
PTOLIST L = (PTOLIST)malloc(sizeof(LIST)); //动态分配
if (L==NULL)
printf("\n初始化失败!!\n");
L->curr_length =0; //初始化空表信息
L->create_date =__DATE__;
L->next = NULL;
return L; //返回分配好的指针
}
//销毁表
void destroylist(PTOLIST L)
{
free(L);
}
从键盘读入数据时,只能在表尾处插入,才能保证数据的顺序不变。
insertend(),注意细节:
//函数声明
bool insertend(PTOLIST L, int e);
//函数声明结束
//表尾插入
bool insertend(PTOLIST L, int e)
{
PTONODE new_unit = (PTONODE)malloc(sizeof(NODE));
if (new_unit == NULL)
{
printf("\n分配新元素空间失败!\n");
return 0;
}
PTONODE p = L->next;
if (L->next == NULL) //细节:空表第一个元素插入时有区别
L->next = new_unit;
else
{
while (p->next != NULL) //循环找出最后一个元素位置
p = p->next;
p->next = new_unit;
}
new_unit->data = e; //插入表尾
new_unit->next = NULL;
++ L->curr_length; //更新表长
return 1;
}
删除表中所有元素时,既可以从表尾处开始,也可以从表头第一个元素开始,通过分析得知,从第一个元素开始删除效率最高deletefirst(),在此基础上还能构建出重置空表clearlilst():
//函数声明
void deletefirst(PTOLIST L);
void clearlist(PTOLIST L);
//函数声明结束
//函数实现部分:
//表头删除
void deletefirst(PTOLIST L)
{
PTONODE p = L->next;
L->next = p->next;
free(p);
-- L->curr_length;
}
//重置空表
void clearlist(PTOLIST L)
{
while (L->next != NULL)
deletefirst(L);
}
此时可以完善销毁表destroylist函数了,理念是先删除所有元素,再删除表头:
void destroylist(PTOLIST L)
{
clearlist(L);
free(L);
}
添加遍历visitlist(),和查看表信息infolist()功能:
//函数声明
bool visitlist(PTOLIST L);
void infolist(PTOLIST L);
//函数声明结束
//主程序开始:
int main()
{
PTOLIST L = initlist();
printf("\n请输入整型数据,互相之间以回车或空格隔开:\n");
printf("输入完成后按回车,再按ctrl+d结束:\n");
int num; //存放scanf返回值
char nouse[30]; //30个字节的无用区
int e; //输入值临时存放在e中
while ((num= scanf ("%d",&e)) != EOF )
{
if (num == 0) {scanf("%s", nouse); continue; } //非数值则放进无用区,跳过
insertend(L,e);
}
infolist(L);
visitlist(L);
printf("\n开始重置空表\n");
clearlist(L);
infolist(L);
visitlist(L);
destroylist(L);
return 0;
}
//主程序结束
//函数实现部分:
//遍历表
bool visitlist(PTOLIST L)
{
PTONODE p = L->next;
if (p == NULL)
{
printf("\n数据表为空,请添加数据\n");
return 0;
}
printf("\n链表中的数据如下:\n");
while ( p->next !=NULL) //小细节:为空说明是最尾元素了。
{ printf("%d ", p->data);
p = p-> next;
}
printf("%d \n", p->data); //要单独显示
return 1;
}
//查看表信息
void infolist (PTOLIST L)
{
printf("\n表中现有元素:%d个\n",L->curr_length);
printf("表创建时间为:%s\n", L->create_date);
}
运行结果: