单链表的基本操作
实验性质
设计性实验
实验目的
通过该实验,深入理解链表的逻辑结构、物理结构等概念,掌握链表基本操作的编程实现,熟练掌握C语言中指针的操作。和实验一对比,掌握线性结构两种不同存储方式的区别。
实验内容
编程实现链表下教材第二章定义的单链表的基本操作,最好用菜单形式对应各个操作,使其变成一个完整的小软件。
参考界面
验收/测试用例
同实验一。
参考代码
//From:TengMMVP
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
//链表节点结构体
typedef struct LNode {
ElemType data; //节点数据
int tail; //记录节点数
struct LNode *next; //指向下一个节点的指针
} LNode, *LinkList;
//初始化或重置链表的函数实现
bool InitList(LinkList *L) {
//为头结点分配存储空间
*L = (LinkList)malloc(sizeof(LNode));
//如果头结点空间分配失败,则输出错误信息并退出程序
if (*L == NULL) {
perror("InitList malloc");
exit(1);
}
(*L)->tail = 0; //结点数目为0
(*L)->next = NULL; //初始化头节点
return true;
}
//判断链表是否非空
bool isEmpty(LinkList L) {
return L == NULL || L->tail == 0; //如果链表不存在或tail为0,链表为空
}
//在链表头部插入元素值
void insertAtHead(LinkList L, ElemType value) {
//为新结点分配存储空间
LinkList newNode = (LinkList)malloc(sizeof(LNode));
//如果存储空间分配失败则输出提示信息,退出程序
if (newNode == NULL) {
perror("insertAtHead malloc");
exit(1);
}
newNode->data = value; //设置新节点的数据
newNode->next = L->next; //新节点指向原头节点的下一个节点
L->next = newNode; //头节点指向新节点
L->tail++; //更新节点计数
}
//在链表尾部插入元素值
void insertAtTail(LinkList L, ElemType value) {
//为新结点分配存储空间
LinkList newNode = (LinkList)malloc(sizeof(LNode));
//如果存储空间分配失败则输出提示信息,退出程序
if (newNode == NULL) {
perror("insertAtTail malloc");
exit(1);
}
newNode->data = value;
newNode->next = NULL;
//将原尾结点指向新尾结点
if (L->tail == 0) { //如果是空链表
L->next = newNode;
} else {
LinkList temp = L;
while (temp->next != NULL) { //找到链表的最后一个节点
temp = temp->next;
}
temp->next = newNode; //将新节点插入到链表末尾
}
L->tail++; //更新节点计数
}
//在链表指定位置插入元素值
bool insertAtPosition(LinkList L, int position, ElemType value) {
//检查位置合法性
if (position < 1 || position > (L->tail+1)) {
printf("插入位置无效,请重新操作!\n");
return false;
}
//为新结点分配存储空间
LinkList newNode = (LinkList)malloc(sizeof(LNode));
//如果存储空间分配失败则输出提示信息,退出程序
if (newNode == NULL) {
perror("insertAtPosition malloc");
exit(1);
}
newNode->data = value;
//更新链表结点关系
LinkList current = L;
for (int i = 0; i < position-1; ++i) {
current = current->next; //移动到指定位置的前一个节点
}
newNode->next = current->next;
current->next = newNode;
L->tail++; //更新节点计数
return true;
}
//删除链表首元结点
bool deleteHead(LinkList L) {
//判断能否进行删除操作
if (isEmpty(L)) {
printf("链表为空,无法删除首元节点!\n");
return false;
}
LinkList temp = L->next; //指向头节点的下一个节点
if (temp != NULL) { // 确保链表不只是一个头节点
L->next = temp->next;
free(temp);
L->tail--; //更新节点计数
}
return true;
}
//删除链表尾结点
bool deleteTail(LinkList L) {
//判断能否进行删除操作
if (isEmpty(L)) {
printf("链表为空,无法删除尾节点!\n");
return false;
}
LinkList current = L;
while (current->next->next != NULL) {
current = current->next; // 移动到尾节点的前一个节点
}
free(current->next); //释放尾节点
current->next = NULL; //更新尾节点的前一个节点的next指针
L->tail--; //更新节点计数
return true;
}
//删除链表指定位置的结点
bool deleteAtPosition(LinkList L, int position) {
//判断能否进行删除操作
if (isEmpty(L) || position < 1 || position > L->tail) {
printf("删除位置无效,请重新操作!\n");
return false;
}
LinkList current = L;
for (int i = 0; i < position-1; ++i) {
current = current->next; //移动到要删除节点的前一个节点
}
LinkList temp = current->next;
current->next = temp->next;
free(temp);
L->tail--; //更新节点计数
return true;
}
//按值查找元素的位置
int findPositionByValue(LinkList L, ElemType value) {
LinkList current = L->next; //从第一个节点开始遍历链表
int position = 1;
while (current != NULL) {
if (current->data == value) {
return position; // 找到节点的数据值等于目标值,返回节点位置
}
current = current->next;
position++;
}
return -1; // 未找到目标值,返回-1
}
//按位置查找元素的值
int findValueByPosition(LinkList L, int position) {
if (position < 1 || position > L->tail) { //确保位置在有效范围内
printf("输入的位置下标无效,请重新操作!\n");
return -1;
}
LinkList current = L->next; //从第一个节点开始遍历链表
for (int count = 1; count < position; ++count) { //移动到指定位置的节点
current = current->next;
}
return current->data; //返回找到的节点的数据值
}
//修改指定位置的元素值
bool modifyByPosition(LinkList L, int position, ElemType value) {
if (position < 1 || position > L->tail) {
printf("输入的位置下标无效,请重新操作!\n");
return false;
}
LinkList current = L->next;
for (int i = 0; i < position-1; ++i) {
current = current->next;
}
current->data = value;
return true;
}
//修改指定元素的元素值
bool modifyValue(LinkList L, ElemType oldValue, ElemType newValue) {
//判断能否进行修改操作
if (isEmpty(L)) {
printf("链表为空,修改元素值无效,请重新操作!\n");
return false;
}
LinkList current = L->next; //从第一个节点开始遍历链表
while (current != NULL) {
if (current->data == oldValue) {
current->data = newValue; //找到节点的元素值等于旧值,更新节点的元素值为新值
return true;
}
current = current->next;
}
printf("未在链表中查找到该元素值,修改操作无效!\n");
return false;
}
//求链表的长度
int getLength(LinkList L) {
return L->tail;
}
//输出所输入的链表元素
void traverse(LinkList L) {
//如果链表不存在
if(L == NULL) {
puts("traverse argument error");
return ;
}
//如果链表为空
if (isEmpty(L)) {
printf("链表为空。\n");
return;
}
//遍历输出链表中的每一个元素
LinkList current = L->next;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
//查找元素的直接前驱
ElemType findPredecessor(LinkList L, ElemType value) {
if (L == NULL || L->next == NULL) {
printf("链表为空或只有头节点,不存在前驱!\n");
return -1; // 返回一个特殊值表示不存在
}
LinkList current = L->next;
// 头节点没有前驱
if (current->data == value) {
printf("头节点没有前驱。\n");
return -1; // 返回一个特殊值表示不存在
}
while (current->next != NULL && current->next->data != value) {
current = current->next;
}
if (current->next == NULL) {
printf("元素%d不存在于链表中,因此没有前驱。\n", value);
return -1; // 返回一个特殊值表示不存在
}
return current->data;
}
//查找元素的直接后继
ElemType findSuccessor(LinkList L, ElemType value) {
if (L == NULL || L->next == NULL) {
printf("链表为空或只有头节点,不存在后继。\n");
return -1; // 返回一个特殊值表示不存在
}
LinkList current = L->next;
while (current != NULL && current->data != value) {
current = current->next;
}
if (current == NULL || current->next == NULL) {
printf("元素%d不存在于链表中或没有后继。\n", value);
return -1; // 返回一个特殊值表示不存在或者没有后继
}
return current->next->data;
}
//清空链表
void clearList(LinkList L) {
LinkList current = L->next;
while (current != NULL) {
LinkList temp = current;
current = current->next;
free(temp);
}
L->next = NULL;
L->tail = 0;
}
int main() {
LinkList list;
bool InitResult=false,InsertResult,DeleteResult,ModifyResult,EmptyResult;
int choice, value, position, oldValue, newValue;
ElemType predecessor,successor;
while (true) {
printf("\n*********************************链表操作菜单**********************************\n");
printf("1. 初始化或重置链表 2. 在链表头部插入元素 3. 在链表尾部插入元素\n");
printf("4. 在链表指定位置插入元素 5. 删除链表首元结点 6. 删除链表尾结点\n");
printf("7. 删除链表指定位置的结点 8. 按值查找元素的位置 9. 按位置查找元素的值\n");
printf("10.修改指定位置的元素值 11.修改指定元素的元素值 12.求链表的长度\n");
printf("13.求指定元素的直接前驱 14.求指定元素的直接后继 15.输出所输入的链表元素\n");
printf("16.判断链表是否非空 17.清空链表 18.退出程序\n");
printf("*******************************************************************************\n");
printf("请输入操作编号:");
scanf("%d", &choice);
if(choice != 1 && !InitResult) {
printf("链表未初始化,请先执行初始化操作。\n");
continue;
}
switch (choice) {
case 1: //初始化或重置链表
InitResult=InitList(&list); //调用初始化函数进行初始化
if(InitResult==true) { //根据返回值判断初始化成功与否
printf("初始化或重置链表成功!\n");
} else {
printf("初始化或重置链表失败!\n");
}
break;
case 2: //在链表头部插入元素
printf("请输入要插入的元素值:");
scanf("%d", &value);
//调用头部插入函数进行链表头部插入
insertAtHead(list, value);
printf("元素%d成功插入链表。\n",value);
break;
case 3: //在链表尾部插入元素
printf("请输入要插入的元素值:");
scanf("%d", &value);
//调用尾部插入函数进行链表尾部插入
insertAtTail(list, value);
printf("元素%d成功插入链表。\n",value);
break;
case 4: //在链表指定位置插入元素
printf("请输入要插入的位置下标(从1开始):");
scanf("%d", &position);
printf("请输入要插入的元素值:");
scanf("%d", &value);
//调用指定位置插入函数进行链表指定位置插入
InsertResult=insertAtPosition(list, position, value);
//根据返回值判断插入操作是否成功
if(InsertResult) {
printf("元素%d成功插入链表。\n",value);
}
//插入结果重置
InsertResult=false;
break;
case 5: //删除链表首元结点
//调用首元结点删除函数进行首元结点删除操作
DeleteResult=deleteHead(list);
//根据返回值判断删除操作是否成功
if(DeleteResult) {
printf("链表首元结点成功删除。\n",value);
}
//删除结果重置
DeleteResult=false;
break;
case 6: //删除链表尾结点
//调用尾结点删除函数进行尾结点删除操作
DeleteResult=deleteTail(list);
//根据返回值判断删除操作是否成功
if(DeleteResult) {
printf("链表尾结点成功删除。\n",value);
}
//删除结果重置
DeleteResult=false;
break;
case 7: //删除链表指定位置的结点
printf("请输入要删除的位置下标(从1开始):");
scanf("%d", &position);
//调用指定位置删除函数进行链表指定位置删除
DeleteResult=deleteAtPosition(list, position);
//根据返回值判断删除操作是否成功
if(DeleteResult) {
printf("链表%d号元素成功删除。\n",position);
}
//插入结果重置
DeleteResult=false;
break;
case 8: //按值查找元素的位置
printf("请输入要查找的元素值:");
scanf("%d", &value);
//调用位置查找函数,返回位置信息
position = findPositionByValue(list, value);
if (position != -1) {
printf("元素值%d的位置下标是:%d。\n", value, position);
} else {
printf("元素值%d在链表中不存在!\n", value);
}
break;
case 9: //按位置查找元素的值
printf("请输入要查找的位置下标:");
scanf("%d", &position);
//调用元素查找函数,返回元素值
value = findValueByPosition(list, position);
if (value != -1) {
printf("位置下标%d上的元素值是:%d\n", position, value);
}
break;
case 10: //修改指定位置的元素值
printf("请输入要修改的位置下标:");
scanf("%d", &position);
printf("请输入修改后的元素值:");
scanf("%d", &value);
//调用元素修改函数,执行指定位置元素修改操作
ModifyResult=modifyByPosition(list, position, value);
if(ModifyResult) {
printf("链表%d号元素已成功修改为%d。\n",position,value);
}
ModifyResult=false;
break;
case 11: //修改指定元素的元素值
printf("请输入要修改的元素值:");
scanf("%d", &oldValue);
printf("请输入修改后的元素值:");
scanf("%d", &newValue);
//调用元素修改函数,执行指定元素修改操作
ModifyResult=modifyValue(list, oldValue, newValue);
if(ModifyResult) {
printf("链表元素%d已成功修改为%d。\n",oldValue,newValue);
}
ModifyResult=false;
break;
case 12: //求链表的长度
printf("链表的长度是:%d。\n", getLength(list));
break;
case 13: //求链表中指定元素的直接前驱
printf("请输入要查找前驱的元素值:");
scanf("%d", &value);
//调用查找前驱函数
predecessor = findPredecessor(list, value);
if (predecessor != -1) {
printf("元素%d的直接前驱是:%d。\n", value, predecessor);
}
break;
case 14: //求链表中指定元素的直接后继
printf("请输入要查找后继的元素值:");
scanf("%d", &value);
//调用查找后继函数
successor = findSuccessor(list, value);
if (successor != -1) {
printf("元素%d的直接后继是:%d。\n", value, successor);
}
break;
case 15: //输出所输入的链表元素
traverse(list);
break;
case 16: //判断链表是否非空
EmptyResult=isEmpty(list);
if(EmptyResult) {
printf("链表为空。\n");
} else {
printf("链表非空。\n");
}
break;
case 17: //清空链表
clearList(list);
break;
case 18: //退出程序
exit(0);
default:
printf("操作码无效,请重新输入!\n");
break;
}
}
return 0;
}
//From:TengMMVP