单链表-C语言

// 单链表

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int ElemType;

/*定义单链表结点类型*/
typedef struct LNode
{
	ElemType data;  // 数据域
	struct LNode* next;  // 指针域。next这个指针指向的结构体类型的结点
}lnode;

/*函数声明*/
// 创建
lnode* createList();  
// 输出
void outputList(lnode* lp); 
// getElemByIndex 按下标查找结点值
lnode* getElemByIndex(lnode* lp, int i);
// getIndexByElem 按值查找下标
int getIndexByElem(lnode* lp, int e);
// insertElem(lp, index, e) // 插入结点
lnode* insertElem(lnode* lp, int index, ElemType e);	
//  根据下标获取结点
lnode* getElem(lnode* lp, int index);
//  根据下标获取结点
lnode* delElem(lnode* lp, int index);
//  求表长
int lenList(lnode* lp);

int main(int argc, char** argv)
{
	// code
	int a;
	int index;  // 插入/删除的位置
	ElemType e;  // 要插入/删除的元素
	lnode* lp = NULL;

	// 初始化操作
	while (1)
	{
		printf("\n");
		printf("\n**************************\n");
		printf("==========================\n");
		printf("1. 创建单链表(尾插法)\n");
		printf("2. 查看单链表\n");
		printf("3. 按下标查找结点值\n");
		printf("4. 按值查找下标\n");
		printf("5. 插入结点操作\n");
		printf("6. 删除结点操作\n");
		printf("7. 求表长操作\n");
		printf("0. 退出\n");
		printf("请选择操作:");
		scanf("%d", &a);
		printf("\n==========================\n");
		printf("**************************\n");

		if (a == 0)
		{
			printf("=====程序结束=====\n");
			break;
		}

		switch (a)
		{
		case 1:
			lp = createList();
			break;
		case 2:
			outputList(lp);
			break;
		case 3:
			printf("请输入要查询元素的下标:");
			scanf("%d", &index);
			lnode* temp_lp = getElemByIndex(lp, index);
			if (temp_lp)
			{
				printf("下标为%d的元素为%d ", index, temp_lp->data);
			}
			else
			{
				printf("操作失败,请重新查询...");
			}
			break;
		case 4:
			printf("请输入要查询的元素:");
			scanf("%d", &e);
			index = getIndexByElem(lp, e);
			if (index || index == 0)
			{
				printf("元素为%d的下标为%d ", e, index);
			}
			else
			{
				printf("操作失败,请重新查询...");
			}
			break;
		case 5:
			printf("请输入要插入的结点下标:");
			scanf("%d", &index);
			printf("请输入要插入的结点元素:");
			scanf("%d", &e);
			lp = insertElem(lp, index, e);
			if (lp)
			{
				outputList(lp);
			}
			else
			{
				printf("操作失败,请重新查询...");
			}
			break;
		case 6:
			printf("请输入要删除的结点下标:");
			scanf("%d", &index);
			lp = delElem(lp, index);
			if (lp)
			{
				outputList(lp);
			}
			else
			{
				printf("操作失败...");
			}
			break;
		case 7:
			index = lenList(lp);
			printf("表长为:%d\n	", index);
			break;
		default:
			break;
		}
	}
	
	system("pause");
	return 0;
}

lnode* createList()
{
	int x, l;  // int类型的元素
	lnode* lp = (lnode*)malloc(sizeof(lnode));  // 创建头结点
	lnode* sp = NULL;
	lnode* rp = lp;  // r为表尾指针 声明一个指针指向头结点,用于遍历链表

	printf("请输入创建单链表的值(以回车间隔):\n");
	for (int i = 0; i < 5; i++)
	{
		scanf("%d", &x);  // 输入结点的值

		/*由系统生成一个LNode型的结点,同时将该结点的起始位置赋给指针变量s*/
		sp = (lnode*)malloc(sizeof(lnode));  // 创建新的结点
		sp->data = x;  // 新结点的数据
		rp->next = sp;  // 每次将s所指的结点插在最末端
		rp = sp;  // r指向新的表尾结点
	}
	rp->next = NULL;  // 未结点指针置空
	return lp;
}

void outputList(lnode* lp)
{
	lnode* temp = lp->next;  // 将头结点的next赋给temp
	printf("单链表为:");
	while (temp)
	{
		printf("%d  ", temp->data);
		temp = temp->next;
	}
}

lnode* getElem(lnode* lp, int index)
{
	lnode* temp = lp->next;
	if (index == -1)  // 数据部分从0开始,则头结点为-1
	{
		return lp;
	}
	if (index < 0)
	{
		return NULL;  // 下标无效
	}
	for (int i = 0; i < index; i++)
	{
		if (temp)
		{
			temp = temp->next;
		}
		else
		{
			return NULL;
		}
	}
	return temp;
}

lnode* getElemByIndex(lnode* lp, int i)
{	
	lnode* temp = lp->next;  // temp 指向头结点的next,用于遍历
	if (i < 0)  // 下标无效
	{
		return NULL;
	}
	for (int j = 0; j < i; j++)
	{
		if (temp)
		{
			temp = temp->next;
		}
		else  // 下标无效, 溢出
		{
			return NULL;
		}
	}
	return temp;
}

lnode* insertElem(lnode* lp, int index, ElemType e)
{
	// 查找插入下标的前驱结点
	lnode* temp = getElem(lp, index - 1);

	if (!temp)
	{
		return NULL;  // 查找插入下标的前驱结点失败
	}

	// 插入操作
	lnode* s = (lnode*)malloc(sizeof(lnode));  // 新结点的存储空间
	s->data = e;  // 新结点的值
	s->next = temp->next;  // 新结点指向插入下标的下一结点
	temp->next = s;  // 插入下标的前驱结点指向新结点

	return lp;
}

int getIndexByElem(lnode* lp, ElemType e)
{
	int j = 0;
	lnode* temp = lp->next;  // temp 指向头指针的next,用于遍历
	while (temp && temp->data != e)
	{
		temp = temp->next;
		j++;
	}
	if (temp)
	{
		return j;
	}
	else
	{
		return NULL;  // 没有查到
	}
}

lnode* delElem(lnode* lp, int index)
{
	lnode* temp = getElem(lp, index - 1);  // 被删除结点下标的前驱结点
	lnode* delElemP = temp->next;  // 被删除的结点

	temp->next = delElemP->next;  // delElep的前驱结点指向后继结点,跳过delElemP结点
	free(delElemP);  // 释放结点的存储空间

	return lp;
}

int lenList(lnode* lp)
{
	int j = 0;
	lnode* temp = lp->next;
	while (temp)
	{
		temp = temp->next;
		j++;
	}
	return j;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) C语言下的单链表,可以增加,删除,查找,销毁节点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值