C语言 链表 案例

一、案例内容:

1.建立单链表。

2.在单链表上实现插入、删除和查找等操作。

二、案例要求:

编写实现单链表基本操作的以下函数,并在此基础上设计一个主程序完成如下功能:

初始化单链表L,类型自选;

⑵用头插法建立单链表L

⑶用尾插法建立单链表L

⑷输出单链表L的长度;

⑸输出单链表L的第i个元素(按位置查找)

⑹输出给定元素的位置(按内容查找)

⑺在第i个元素前插入给定元素(在第i个位置插入);

删除单链表L的第i个元素;

输出单链表;

⑽清空单链表(只保留头结点);

⑾菜单函数;

⑿单链表逆置;

建立一个非递减排序的单链表。

三,案例设计

结构体

/*链表*/

typedef struct Linklist

{

    MOLD Data;

    struct Linklist* next;

}*Head,Node;

01-初始化链表

Init_Linklist()

/*1.在堆区开辟Node*类型的空间一块

* 2.将此head头指针指向此空间,即首节点

* 3.初始化数据,date存放节点个数,起始为0,next指向空

*/

02-尾插法 --从末尾依次插入

void Input_Linklist(Head head)

/*1.确定该链表的尾结点

* 2.输入数据

* 3.在堆区开辟新空间node

* 4.将数据赋给node->date,并将node->指向空,保证数据不会越界

* 5.将node地址赋给链表最后一个元素的next,head->Date,即结点个数加一

* 6.原来指向最后一个结点的指针向后移动一位

*/

03-头插法 将新结点插在首结点后面

void Head_insert(Head head)

/*1.输入数据

* 2.开辟新结点

* 3.新结点赋值

* 4.新结点指向首结点next

* 5.首结点next指向新结点

*/

04-链表输出

void Output_Linklist(Head head)

/*1.定义结点指针类型now接受链表中的每个结点

* 2.遍历链表,输出数据,当now->next为空,结束

*/

05-求长度

int Link_list_Length(Head head)

/*1.设置记录变量L,每遍历一次链表加一

* 2.返回L

* 3.也可以直接返回head->date,其中便存储结点长度

*/

06-定点查找

Node* Num_Find(Head head)

/*1.输入位置n

* 2.判断n值是否合理

* 3.遍历链表,直到找到第n个结点

* 4.返回该结点指针

*/

07-定值查找

Node* Ele_Find(Head head)

/*1.输入所要查找的元素

* 2.在链表中遍历寻找,直到找到该元素或链表遍历完

* 3.依据2的结果返回该链表地址或空指针

*/

08-删除元素

void LinkedList_Erase(Head head)

删除元素 定点删除  定元素删除

/*1.选择调用,插入查找也可以这样做*/

// 定点删除 

void Erase_Post(Head head)

/*1.定义两指针,分别表示当前结点,与其前驱结点的地址(当然,也可以只用当前元素前驱地址遍历链表,省空间)

*2.遍历链表,找到位置n

*3.将目标结点的后继地址赋给其前驱

* 4.释放目标节点,链表长度减一

*/

定元素删除

void Erase_Ele(Head head)

/*1.输入所要删除的元素,遍历链表找到目标元素,与查找元素方法相同

*2.更改目标元素前驱的后继为目标元素的后继

*3.释放目标节点,链表长度减一

*/

09-定点前插入 插入位置小于等于长度大于0

void Ahead_Insert(Head head)

/*1.判断插入位置是否合理

* 2.堆区开辟新空间给node指针,将之赋予该指针

* 3.将目标结点的后继赋给新指针的后继

* 4.将新结点地址赋给目标结点前驱的后继

* 5.结点数量加一

*/

10-定点后插入 0<n<=Length

void After_Insert(Head head)

/*1.找到目标结点

* 2.开辟新结点并赋值

* 3.将新结点后继指向目标结点后继

* 4.将新结点前驱改为目标结点

* 5.链表长度加一

*/

11-清空链表

void  Linked_list_Clear(Head head)

/*1.从前向后,找到最后的结点

 *2.将其前驱的后继改为空指针

 *3.释放最后的结点,链表长度减一

 *4.继续循环上述操作,直至链表清空

*/

12-排序 --此排序只是更改了本来链表中元素的值,并非对链表的链接进行改变

void Sort_list(Head head)

/*1.定义一个数据类型临时变量存储两个相邻结点中较大元素

* 2.将较小元素值赋给前面的结点

* 3.降临时变量值赋给后面结点,实现数据交换

*/

13-逆置

Head Invert_List(Head head)

/*1.将原来链表元素从头到尾使用前插法构建新的链表

* 2.将原链表的头指针指向新链表并返回新链表的头指针

*/

四,案例结果
菜单/初始化/头插法

尾插法

输出

长度

定点前插入

定点后插入

定值查找

定点查找

删除元素
定点

定值

排序

逆置

清空

五、案例总结
问题1:开始并没有考虑随时向链表中增添元素,尾插法只满足传入空链表,进行插入操作

改进:利用head->Date/遍历操作,找到该链表的尾结点,在尾结点后插入元素

问题2:清空链表如果只是将链表首结点的后继改为空,会造成空间浪费

改进:从前向后找寻尾结点,逐个改变其前驱的next指针,释放尾结点

但时间代价很大

经验1:开始在指定位置插入/删除元素时,习惯上只考虑了定义两个指针解决,实际上也可以只用目标结点前驱指针,修改边界条件即可完成上述操作,

或者化繁为简,将前驱上的操作经过赋值操作改编为后继结点上的操作,更为简单

六,案例源代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define MOLD int
/*链表*/
typedef struct Linklist
{
	MOLD Data;
	struct Linklist* next;
}*Head,Node;


//01-初始化链表
/*1.在堆区开辟Node*类型的空间一块
* 2.将此head头指针指向此空间,即首节点
* 3.初始化数据,date存放节点个数,起始为0,next指向空
*/
Head Init_Linklist()
{
	Node* node = (Node*)malloc(sizeof(Node));
	
	if (node)
	{	
		node->Data = 0;
		node->next = NULL;
		printf("初始化成功!!!\n");
	}
	Head head = node;
	return head;
}

//02-尾插法 --从末尾依次插入
/*1.确定该链表的尾结点
* 2.输入数据
* 3.在堆区开辟新空间node
* 4.将数据赋给node->date,并将node->指向空,保证数据不会越界
* 5.将node地址赋给链表最后一个元素的next,head->Date,即结点个数加一
* 6.原来指向最后一个结点的指针向后移动一位
*/
void Input_Linklist(Head head)
{	
	
	MOLD X;
	Node* Box,*last;
	last = head;
	for (int i = 0; i < head->Data; ++i)
	{
		last = last->next;
	}
	Box = last;
	while (1)
	{	

		printf("请输入所要增添的元素\n");
		scanf("%d", &X);
		Node* node= (Node*)malloc(sizeof(Node));
		
		if (node&&Box)
		{	
			node->Data = X;
			node->next = NULL;
			head->Data+=1;	
			Box->next = node;
		}

		int Key = 1;
		printf("是否继续输入(0/1)\n");
		scanf("%d", &Key);
		if (Key == 0) break;
		Box = Box->next;

	}
}
//04-链表输出
/*1.定义结点指针类型now接受链表中的每个结点
* 2.遍历链表,输出数据,当now->next为空,结束
*/
void Output_Linklist(Head head)
{
	Node* now = head->next;
	printf("该链表元素如下:\n");
	while (1)
	{
		if(now) printf("%d\t", now->Data);
		
		if (now&&now->next) now = now->next;
		else break;
	}
	printf("\n");
	
}
//03-头插法 将新结点插在首结点后面
/*1.输入数据
* 2.开辟新结点
* 3.新结点赋值
* 4.新结点指向首结点next
* 5.首结点next指向新结点
*/
void Head_insert(Head head)
{
	//Node* ptr = head->next;
	int Key = 6;
	printf("请输入所要插入的元素/输入666结束输入\n");
	while (1)
	{	
		Node* node = (Node*)malloc(sizeof(Node));
		scanf("%d", &Key);
		if (Key == 666) return;
		node->Data = Key;
		++head->Data;
		node->next = head->next;
		head->next= node;

	}
	
}
//05-求长度
/*1.设置记录变量L,每遍历一次链表加一
* 2.返回L
* 3.也可以直接返回head->date,其中便存储结点长度
*/
int Link_list_Length(Head head)
{
	Node* ptr = head->next;
	int L = 0;
	while (ptr)
	{
		++L;
		ptr = ptr->next;
	}
	printf("Length=%d\n", L);
	printf("该链表的长度为:%d\n", head->Data);
	return L;
}

//06-定点查找
/*1.输入位置n
* 2.判断n值是否合理
* 3.遍历链表,直到找到第n个结点
* 4.返回该结点指针
*/
Node* Num_Find(Head head)
{	
	printf("该链表的长度为:%d\n", Link_list_Length(head));
	printf("请输入所要查找的的元素位置:\n");
	int i=1,n;
	scanf("%d", &n);
	if (n == 0)
	{
		printf("输入不合理!!!\n");
		return NULL;
	}
	Node* ptr = head->next;
	while (i < n && ptr)
	{
		
		ptr = ptr->next;
		++i;
	}
	if (i == n)
	{
		printf("找到!!!\n");
		printf("该元素为%d\n", ptr->Data);
		return ptr;
	}
	printf("No找到!!!\n");
	return NULL;
}
//07-定值查找
/*1.输入所要查找的元素
* 2.在链表中遍历寻找,直到找到该元素或链表遍历完
* 3.依据2的结果返回该链表地址或空指针
*/
Node* Ele_Find(Head head)
{
	int x,n=0;
	Node* ptr = head->next;
	printf("请输入所要查找的元素:\n");
	scanf("%d", &x);
	while (ptr)
	{
		++n;
		if (x == ptr->Data)
		{	
			printf("找到!!!\n");
			printf("在第%d个位置\n", n);
			return ptr;
		}
		ptr = ptr->next;
	}
	printf("No找到!!!\n");
	return NULL;
}
//08-删除元素 
// 定点删除  
/*1.定义两指针,分别表示当前结点,与其前驱结点的地址(当然,也可以只用当前元素前驱地址遍历链表,省空间)
*2.遍历链表,找到位置n
*3.将目标结点的后继地址赋给其前驱
* 4.释放目标节点,链表长度减一
*/
void Erase_Post(Head head)
{
	//printf("该链表的长度为:%d\n", Link_list_Length(head));
	printf("该链表的长度为:%d\n", head->Data);
	printf("请输入所要删除的的元素位置:\n");
	int i = 1, n;
	scanf("%d", &n);
	if (n == 0)
	{
		printf("输入不合理!!!\n");
		return;
	}
	Node* ptr = head->next, * lastptr = head;
	while (i < n && ptr)
	{
		lastptr = ptr;
		ptr = ptr->next;
		++i;
	}
	if (i == n)
	{
		lastptr->next = ptr->next;
		free(ptr);
		--head->Data;
		printf("删除成功!!!\n");
	}
	

}

void Erase_Post_Plus(Head head)
{
	//printf("该链表的长度为:%d\n", Link_list_Length(head));
	printf("该链表的长度为:%d\n", head->Data);
	printf("请输入所要删除的的元素位置:\n");
	int i = 1, n;
	scanf("%d", &n);
	if (n == 0)
	{
		printf("输入不合理!!!\n");
		return;
	}
	Node* ptr = head;
	while (i < n && ptr)
	{
		
		ptr = ptr->next;
		++i;
	}
	if (i == n)
	{
		ptr->next = ptr->next->next;
		//free(ptr);  //额找不到 要删除的指针啦,此计可依,但非全策
		--head->Data;
		printf("删除成功!!!\n");
	}


}
//定元素删除
/*1.输入所要删除的元素,遍历链表找到目标元素,与查找元素方法相同
*2.更改目标元素前驱的后继为目标元素的后继
*3.释放目标节点,链表长度减一
*/
void Erase_Ele(Head head)
{
	int x;
	Node* ptr = head->next,*lastptr=head;
	printf("请输入所要删除的元素:\n");
	scanf("%d", &x);
	while (ptr)
	{	
		
		if (x == ptr->Data)
		{	
			lastptr->next = ptr->next;
			
			ptr = ptr->next;
			--head->Data;
			printf("删除成功!!!!\n");
			
		}
		else
		{
			lastptr = ptr;
			ptr = ptr->next;
		}
	}
	
}

//删除元素 定点删除  定元素删除
/*1.选择调用,插入查找也可以这样做*/
void LinkedList_Erase(Head head)
{
	int chose = 1;
	printf("Please chose the way to erase elemt \n");
	printf("01-定点删除\n02-定元素删除\n");
	scanf("%d", &chose);
	switch (chose)
	{
	/*case 1:Erase_Post_Plus(head);
		break;*/
	case 1:Erase_Post(head);
		break;
	case 2:Erase_Ele(head);
		break;
	}
}

//09-定点前插入 插入位置小于等于长度大于0
/*1.判断插入位置是否合理
* 2.堆区开辟新空间给node指针,将之赋予该指针
* 3.将目标结点的后继赋给新指针的后继
* 4.将新结点地址赋给目标结点前驱的后继
* 5.结点数量加一
* 也可以直接将所要插入元素的值赋给目标结点,将其改为后插
*/
void Ahead_Insert(Head head)
{	
	int i = 1, n;
	int X;
	printf("该链表的长度为:%d\n", head->Data);	
	printf("请输入所要插入的的元素位置:\n");
	scanf("%d", &n);
	
	if (n == 0||n>head->Data)
	{
		printf("输入不合理!!!\n");
		return;
	}
	printf("请输入所要插入的的元素:\n");
	scanf("%d", &X);
	Node* ptr = head->next, * lastptr = head;
	Node* New_node = (Node*)malloc(sizeof(Node));
	while (i < n && ptr)
	{
		lastptr = ptr;
		ptr = ptr->next;
		++i;
	}
	if (i == n)
	{	
		New_node->Data = X;
		New_node->next = ptr;
		lastptr->next =New_node;
		++head->Data;
		printf("添加成功!!!\n");
	}
}
//10-定点后插入 0<n<=Length
/*1.找到目标结点
* 2.开辟新结点并赋值
* 3.将新结点后继指向目标结点后继
* 4.将新结点前驱改为目标结点
* 5.链表长度加一
*/
void After_Insert(Head head)
{
	int i = 1, n;
	int X;
	printf("该链表的长度为:%d\n", head->Data);
	printf("请输入所要插入的的元素位置:\n");
	scanf("%d", &n);
	
	if (n == 0||n>head->Data)
	{
		printf("输入不合理!!!\n");
		return;
	}
	printf("请输入所要插入的的元素:\n");
	scanf("%d", &X);
	Node* ptr = head->next;
	Node* New_node = (Node*)malloc(sizeof(Node));
	while (i < n && ptr)
	{
		ptr = ptr->next;
		++i;
	}
	if (i == n)
	{
		New_node->Data = X;
		New_node->next = ptr->next;
		ptr->next = New_node;
		++head->Data;
		printf("添加成功!!!\n");
	}

}
//11-清空链表
//找最后一个,不好用
//Node* Last_Node(Node* node)
//{
//	if (node->next == NULL) return node;
//	else return Last_Node(node->next);
//}
//
//Node* Last02_Node(Node* node)
//{
//	
//		if (node->next&&node->next->next == NULL) return node;
//	else return Last_Node(node->next);
//}

/*1.从前向后,找到最后的结点
 *2.将其前驱的后继改为空指针
 *3.释放最后的结点,链表长度减一
 *4.继续循环上述操作,直至链表清空 
*/
void  Linked_list_Clear(Head head)
{	
	

	while (head->Data)
	{
		Node* ptr = head->next, * lastptr = head;
		for (int i = 1; i <= head->Data; ++i)
		{	
			
			if (i == head->Data)
			{
				lastptr->next = NULL;
				free(ptr);
				--head->Data;
			}
			lastptr = ptr;
			ptr = ptr->next;
			
		}
	}
	printf("清除成功!!!\n");
}

//12-排序 --此排序只是更改了本来链表中元素的值,并非对链表的链接进行改变
/*1.定义一个数据类型临时变量存储两个相邻结点中较大元素
* 2.将较小元素值赋给前面的结点
* 3.降临时变量值赋给后面结点,实现数据交换
*/
void Sort_list(Head head)
{	
	
	int temp = 0;
	for (int i = 0; i < head->Data-1; ++i)
	{	
		Node* ptr = head->next;
		for (int j = 0; j < head->Data-1-i; ++j)
		{
			if (ptr->Data > ptr->next->Data)
			{
				temp = ptr->Data;
				ptr->Data = ptr->next->Data;
				ptr->next->Data = temp;
			}
			ptr = ptr->next;
		}
	}
}

//13-逆置
/*1.将原来链表元素从头到尾使用前插法构建新的链表
* 2.将原链表的头指针指向新链表并返回新链表的头指针
*/
Head Invert_List(Head head)
{	
	Head temp=Init_Linklist();
	Node* ptr = head->next;
	for(int i=0;i<head->Data;++i)
	{
		Node* node = (Node*)malloc(sizeof(Node));
		node->Data =ptr->Data ;
		++temp->Data;
		node->next = temp->next;
		temp->next = node;
		ptr = ptr->next;
	}
	Output_Linklist(temp);
	head->next = temp->next;
	return temp;
}

//00-Menu
void Menu()
{
	printf("*****02-尾插法*********\n");
	printf("*****03-头插法*********\n");
	printf("*****04-链表输出*******\n");
	printf("*****05-求长度*********\n");
	printf("*****06-定点查找*******\n");
	printf("*****07-定值查找*******\n");
	printf("*****08-删除元素*******\n");
	printf("*****09-定点前插入*****\n");
	printf("*****10-定点后插入*****\n");
	printf("*****11-清空链表*******\n");
	printf("*****12-排序***********\n");
	printf("*****13-逆置***********\n");
	printf("******00-exit**********\n");
	printf("请选择你的操作:\n");

}

//测试1
void test01()
{
	Head head = Init_Linklist();
	Input_Linklist(head);
	Output_Linklist(head);
	printf("\nlength=%d", head->Data);
}

//
void test05()
{
	Head head=Init_Linklist();
	Head_insert( head);
	Output_Linklist( head);
	Link_list_Length(head);
	Num_Find(head);
	Ele_Find(head);
	Erase_Ele(head);
	Output_Linklist(head);
}

//
void test06()
{	
	Head head = Init_Linklist();
	int Key = 0;
	while (1)
	{
		Menu();
		scanf("%d", &Key);
		switch (Key)
		{
		case 2:Input_Linklist( head);
			break;
		case 3:Head_insert(head);
			break;
		case 4:Output_Linklist( head);
			break;
		case 5:Link_list_Length( head);
			break;
		case 6:Num_Find( head);
			break;
		case 7:Ele_Find(head);
			break;
		case 8:LinkedList_Erase(head);
			break;
		case 9:Ahead_Insert(head);
			break;
		case 10:After_Insert(head);
			break;
		case 11:Linked_list_Clear(head);
			break;
		case 12:Sort_list(head);
			break;
		case 13:Invert_List(head);
			break;
		case 0:exit(0);
		}
		system("pause");
		system("cls");

	}
}

int main()
{
	test06();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值