数据结构_实验二_单链表的基本操作

单链表的基本操作

实验性质

设计性实验

实验目的

通过该实验,深入理解链表的逻辑结构、物理结构等概念,掌握链表基本操作的编程实现,熟练掌握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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值