C语言单双链表的增删改查(附完整代码)

   大家好,本文是我关于链表的学习内容。 

单链表

一:概述

  但链表是一种物理存储结构上非连续,非顺序的存储结构,数据的逻辑顺序是通过链表中指针链接次序实现的。链表与数组的本质区别是,链表由数据域指针域两部分组成,这样的结构使其既能存储数据,又能比较灵活的对数据进行插入删除。

二:图解

三:代码实现 

1.定义

typedef struct LNode {
	int data;
	struct LNode* next;  //指针指向下一个结点,指针的类型为结点类型
} *LinkNode;          //声明*LinkNode为结构体指针类型

先声明LinkNode为结构体类型,在使用该类型是,将其对应的变量定义为指针即可。该结构体中存放着int类型的数据和指针域。

2.初始化 

void InistLinkNode(LinkNode& L) { //初始化
	L = (LNode*)malloc(sizeof(LNode));   //分配头节点
	L->next = NULL;
}

我们首先要建立一个空的单链表,然后给它分配头节点。

3.头插法 

void InsertLinkNode(LinkNode& L) { //头插法
	LNode* s;
	int x, Len;
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Len);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Len; j++) {
		s = (LNode*)malloc(sizeof(LNode));  //每插入一个元素之前,都要给它分配结点空间
		scanf_s("%d", &x);        //用户自行输入要插入的结点元素
		s->data = x;
		s->next = L->next;
		L->next = s;
	}
}

头插法是将新元素插入到头节点之后,就是真正的第一个结点。

4.尾插法 

void TailInsertLinkNode(LinkNode& L) { //尾插法
	LNode* s, * r;
	int x, Len;
	r = L;           //r表示尾指针
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Len);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Len; j++) {
		s = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &x);
		s->data = x;
		r->next = s;
		r = s;    //s为当前的表尾指针,将它的值赋给r,使r永远指向表尾
	}
	printf("\n");
	r->next = NULL;
}

尾插是将元素插入链表的末尾,以便按照顺序打印,一般尾插分为两种情况

(1)链表初始无结点,开辟结点后直接插入即可。

(2)链表初始有结点,先找尾,再将开辟的结点插入尾部。

5.删除第i个元素 

void DelLinkNode(LinkNode& L) { //删除第i个元素
	int x, j = 0, e;
	printf("请输入你要删除的元素位序:\n");
	scanf("%d", &x);
	LNode* p = L;

	while (p != NULL && j < x - 1) {
		p = p->next;
		j++;
	}

	if (p == NULL) {
		printf("不存在我们要删除的元素");
	}

	if (p->next == NULL) {
		printf("不存在我们要删除的元素");
	}
	LNode* q = p->next;
	e = q->data;
	p->next = q->next;
	free(q);   //为了内存合理利用,将删除的结点释放内存
}

删除某元素之前,首先要保证这个元素是非NULL,其次还要保证它前面的结点也是非NULL,因为只有这样,才能实现后续元素和前面子表的链接。

6.在第i个位置插入 

void InLinkNode(LinkNode& L) { //在第i个位置插入
	printf("请输入要插入的元素和位序:(元素和位序之间用逗号隔开)\n");
	int x, j = 0, e;
	scanf("%d,%d", &e, &x);
	LNode* s = L, * r = (LNode*)malloc(sizeof(LNode));

	while (j < x - 1 && s != NULL) {
		j++;
		s = s->next;
	}
	r->data = e;
	r->next = s->next;
	s->next = r;
}

上面代码的顺序不能改变,即让原来相连的两个结点的第一个指向新节点,新结点再指向原来的第二个结点,这个元素就插入其中了。

7.打印输出单链表 

void PrintLinkNode(LinkNode& L) { //打印输出单链表
	LNode* s = L->next;
	printf("单链表元素如下:\n");
	while (s != NULL) {
		printf("%d", s->data);
		s = s->next;
	}
	printf("\n");
}

将经过上述操作后的链表显示在屏幕上。

8.求单链表结点的个数 

void LenLinkNode(LinkNode& L) { //求线性表长度
	LNode* s = L->next;
	int n = 0;

	while (s != NULL) {
		n++;
		s = s->next;
	}
	printf("单链表长度为%d", n);
	printf("\n");
}

指针每向后移动一次,n加1,从而计算出线性表的长度。

9.查找元素的位序 

void SearchLinkNode(LinkNode& L) {   //查找元素的位序
	int x, j = 1;
	LNode* p = L->next;
	printf("请输入你要查找的元素:\n");
	scanf("%d", &x);

	while (p != NULL && p->data != x) {
		p = p->next;
		j++;
	}

	if (p == NULL) {
		printf("你查找的元素不存在");

	}
	else {
		printf("你要查找的元素%d的位序是%d\n", x, j);
	}
}

利用while循环,如果遍历链表时找到数据相同的结点,就返回它的位序值。

10.结点值的修改 

void change_node(LinkNode& L)   //修改结点的值
{
	LNode* cru = L->next;
	int x, y;
	printf("请输入要修改的数据x:");
	scanf("%d", &x);
	printf("请输入要修改后的数据y:");
	scanf("%d", &y);
	while (cru != NULL)
	{
		if (cru->data == x)
		{
			cru->data = y;
			break;
		}
		else {
			cru = cru->next;
		}
	}
	if (cru == NULL) {
		fprintf(stdout, "要修改的数据不存在,请重新修改\n");  //stdout标准输出流
	}
	else
	{
		fprintf(stdout, "修改成功\n");
	}
}

首先查找到这个结点,再将其值替换。

整个单链表操作的完整代码如下:

#define _CRT_SECURE_NO_WARNINGS 1        //有了这个宏定义,下面的代码就能用sanf()代替scanf_s()
#include <stdio.h>
#include <stdlib.h>   //malloc函数所在的头文件

typedef struct LNode {
	int data;
	struct LNode* next;  //指针指向下一个结点,指针的类型为结点类型
} *LinkNode;          //声明*LinkNode为结构体指针类型

void InistLinkNode(LinkNode& L) { //初始化
	L = (LNode*)malloc(sizeof(LNode));   //分配头节点
	L->next = NULL;
}

void InsertLinkNode(LinkNode& L) { //头插法
	LNode* s;
	int x, Len;
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Len);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Len; j++) {
		s = (LNode*)malloc(sizeof(LNode));  //每插入一个元素之前,都要给它分配结点空间
		scanf_s("%d", &x);        //用户自行输入要插入的结点元素
		s->data = x;
		s->next = L->next;
		L->next = s;
	}
}

void TailInsertLinkNode(LinkNode& L) { //尾插法
	LNode* s, * r;
	int x, Len;
	r = L;           //r表示尾指针
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Len);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Len; j++) {
		s = (LNode*)malloc(sizeof(LNode));
		scanf_s("%d", &x);
		s->data = x;
		r->next = s;
		r = s;    //s为当前的表尾指针,将它的值赋给r,使r永远指向表尾
	}
	printf("\n");
	r->next = NULL;
}

void PrintLinkNode(LinkNode& L) { //打印输出单链表
	LNode* s = L->next;
	printf("单链表元素如下:\n");
	while (s != NULL) {
		printf("%d", s->data);
		s = s->next;
	}
	printf("\n");
}

void LenLinkNode(LinkNode& L) { //求线性表长度
	LNode* s = L->next;
	int n = 0;

	while (s != NULL) {
		n++;
		s = s->next;
	}
	printf("单链表长度为%d", n);
	printf("\n");
}

void GetElemLinkNode(LinkNode& L) { //按照位序查找元素
	printf("输出你要找的元素位序:\n");
	int i, j = 0;
	LNode* s = L;
	scanf("%d", &i);

	while (j < i && s != NULL) {
		j++;
		s = s->next;
	}

	if (s == NULL) {
		printf("不存在我们要查找的元素");

	}
	else {
		printf("元素位序为%d的元素是%d", i, s->data);
	}
	printf("\n");

}

void DelLinkNode(LinkNode& L) { //删除第i个元素
	int x, j = 0, e;
	printf("请输入你要删除的元素位序:\n");
	scanf("%d", &x);
	LNode* p = L;

	while (p != NULL && j < x - 1) {
		p = p->next;
		j++;
	}

	if (p == NULL) {
		printf("不存在我们要删除的元素");
	}

	if (p->next == NULL) {
		printf("不存在我们要删除的元素");
	}
	LNode* q = p->next;
	e = q->data;
	p->next = q->next;
	free(q);   //为了内存合理利用,将删除的结点释放内存
}

void InLinkNode(LinkNode& L) { //在第i个位置插入
	printf("请输入要插入的元素和位序:(元素和位序之间用逗号隔开)\n");
	int x, j = 0, e;
	scanf("%d,%d", &e, &x);
	LNode* s = L, * r = (LNode*)malloc(sizeof(LNode));

	while (j < x - 1 && s != NULL) {
		j++;
		s = s->next;
	}
	r->data = e;
	r->next = s->next;
	s->next = r;
}


void SearchLinkNode(LinkNode& L) {   //查找元素的位序
	int x, j = 1;
	LNode* p = L->next;
	printf("请输入你要查找的元素:\n");
	scanf("%d", &x);

	while (p != NULL && p->data != x) {
		p = p->next;
		j++;
	}

	if (p == NULL) {
		printf("你查找的元素不存在");

	}
	else {
		printf("你要查找的元素%d的位序是%d\n", x, j);
	}
}

void change_node(LinkNode& L)   //修改结点的值
{
	LNode* cru = L->next;
	int x, y;
	printf("请输入要修改的数据x:");
	scanf("%d", &x);
	printf("请输入要修改后的数据y:");
	scanf("%d", &y);
	while (cru != NULL)
	{
		if (cru->data == x)
		{
			cru->data = y;
			break;
		}
		else {
			cru = cru->next;
		}
	}
	if (cru == NULL) {
		fprintf(stdout, "要修改的数据不存在,请重新修改\n");  //stdout标准输出流
	}
	else
	{
		fprintf(stdout, "修改成功\n");
	}
}
int main() {
	LinkNode L;
	InistLinkNode(L);
	TailInsertLinkNode(L);
	PrintLinkNode(L);
	LenLinkNode(L);
	GetElemLinkNode(L);
	InLinkNode(L);
	PrintLinkNode(L);
	DelLinkNode(L);
	PrintLinkNode(L);
	SearchLinkNode(L);
	change_node(L);
	PrintLinkNode(L);
	return 0;
}

















运行结果(示例):

 

 建议此代码在vs2022上运行,因为我在dev c++运行经常报错。

双链表

一.概念

在单链表上能轻松到达下一个结点,但返回上一个结点很难,这种单向的关系就使数据在处理时不够灵活,而双链表就解决了这个问题。在单链表的基础上,双链表是每一个结点的指针既指向下一个结点,也同时指向上一个结点,所以,每个结点都被两个结点所指。既可以从头遍历到尾,也可以从尾遍历到头,但是它占的内存空间更大一些。

二.图解

三.代码实现 

1.创建

//创建一个双向链表的节点
Node *CreatNode(Node *head) {
	head = (Node *)malloc(sizeof(Node)); //申请一个链表节点的空间
	if (head == NULL) {
		printf("malloc error!\r\n");
		return NULL;
	}


	head->pre = NULL; //指向前一个节点的指针
	head->next = NULL; //指向后一个节点的指针
	head->data = rand() % MAX; //随机数 0~100
	return head;
}

//创建一串个数为length的双向链表
Node *CreatList(Node *head) {
	int length;
	printf("请输入链表长度:");
	scanf("%d", &length);
	if (length == 1) {
		return ( head = CreatNode(head));
	} else {
		head = CreatNode(head);//先创建一个链表节点作为链表头
		Node *list = head; //定义一个链表指针指向该链表头
		for (int i = 1; i < length; i++)
			/*创建并初始化一个新结点*/
		{
			Node *body = NULL;
			body = CreatNode(body);//再初始化一个链表节点
			/*直接前趋结点的next指针指向新结点*/
			list->next = body; //链表指针的前一个(指针)与新节点连
			/*新结点指向直接前趋结点*/
			body->pre = list; //新节点的后一个(指针)与上一个节点相连
			/*把body指针给list返回*/
			list = list->next;
		}

	}
	/*加上以下两句就是双向循环链表*/
	// list->next=head;
	// head->prior=list;
	return head;//返回该链表的链表头
}

每创建一个新结点,都要与其前驱节点建立两次联系:

将新节点的prior指针指向直接前驱节点;

将直接前驱节点的next指针指向新节点。

2.插入 

//在第add位置的前面插入data节点
Node *InsertListHead(Node *head) {
	//新建数据域为data的结点
	int add, data;
	printf("请输入要插入的位置和数据(用逗号隔开):");
	scanf("%d,%d", &add, &data);
	Node *temp = (Node *)malloc(sizeof(Node));
	if (temp == NULL) {
		printf("malloc error!\r\n");
		return NULL;
	} else {
		temp->data = data;
		temp->pre = NULL;
		temp->next = NULL;
	}
	//插入到链表头,要特殊考虑
	if (add == 1) {
		temp->next = head;
		head->pre = temp;
		head = temp;
	} else {
		Node *body = head;
		//找到要插入位置的前一个结点
		for (int i = 1; i < add - 1; i++) {
			body = body->next;
		}
		//判断条件为真,说明插入位置为链表尾
		if (body->next == NULL) {
			body->next = temp;
			temp->pre = body;
		} else {
			body->next->pre = temp;
			temp->next = body->next;
			body->next = temp;
			temp->pre = body;
		}
	}
	return head;
}

//在第add位置的后面插入data节点
Node *InsertListEnd(Node *head) {
	int i = 1;
	int add, data;
	printf("请输入要插入的位置和数据(用逗号隔开):");
	scanf("%d,%d", &add, &data);
	//新建数据域为data的结点
	Node *temp = (Node *)malloc(sizeof(Node));
	temp->data = data;
	temp->pre = NULL;
	temp->next = NULL;

	Node *body = head;
	while ((body->next) && (i < add + 1)) {
		body = body->next;
		i++;
	}

	//判断条件为真,说明插入位置为链表尾
	if (body->next == NULL) {
		body->next = temp;
		temp->pre = body;
		temp->next = NULL;
	} else {
		temp->next = body->pre->next;
		temp->pre = body->pre;
		body->next->pre = temp;
		body->pre->next = temp;

	}

	return head;
}

若添加至表头,假设表头结点为head,则temp->next=head;head->prior=temp.

添加至表尾,则找到双链表中最后一个结点,让新结点与最后一个结点建立双层逻辑关系。

3.删除 

//删除数据是data的节点
Node *DeleteList(Node *head) {
	Node *temp = head;
	int data;
	printf("请输入要删除的元素:");
	scanf("%d", &data);
	//遍历链表
	while (temp) {
		//判断当前结点中数据域和data是否相等,若相等,摘除该结点
		if (temp->data == data) {
			//判断是否是头结点
			if (temp->pre == NULL) {
				head = temp->next;
				temp->next = NULL;
				free(temp);
				return head;
			}
			//判断是否是尾节点
			else if (temp->next == NULL) {
				temp->pre->next = NULL;
				free(temp);
				return head;
			} else {
				temp->pre->next = temp->next;
				temp->next->pre = temp->pre;
				free(temp);
				return head;
			}


		}
		temp = temp->next;
	}
	printf("Can not find %d!\r\n", data);
	return head;
}

遍历链表,找到要删除的结点,然后将该结点从表中摘除即可。

4.双链表的更改,查找,打印就不再细说了,大家可以在后面的完整代码里找到,代码中都附有解释。

四:双链表增删改查完整代码 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100    //随机数的范围

typedef struct Node {
	struct Node *pre;
	int data;
	struct Node *next;
} Node;

Node *CreatNode(Node *head);//创建一个双向链表的节点
Node *CreatList(Node *head);//创建一串双向链表
void PrintList(Node *head); //输出链表的功能函数
Node *InsertListHead(Node *head);//在第add位置的前面插入data节点
Node *InsertListEnd(Node *head);//在第add位置的后面插入data节点
Node *DeleteList(Node *head); //删除数据是data的节点
Node *ModifyList(Node *p); //更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
int FindList(Node *head);//head为原双链表,elem表示被查找元素

//创建一个双向链表的节点
Node *CreatNode(Node *head) {
	head = (Node *)malloc(sizeof(Node)); //申请一个链表节点的空间
	if (head == NULL) {
		printf("malloc error!\r\n");
		return NULL;
	}


	head->pre = NULL; //指向前一个节点的指针
	head->next = NULL; //指向后一个节点的指针
	head->data = rand() % MAX; //随机数 0~100
	return head;
}

//创建一串个数为length的双向链表
Node *CreatList(Node *head) {
	int length;
	printf("请输入链表长度:");
	scanf("%d", &length);
	if (length == 1) {
		return ( head = CreatNode(head));
	} else {
		head = CreatNode(head);//先创建一个链表节点作为链表头
		Node *list = head; //定义一个链表指针指向该链表头
		for (int i = 1; i < length; i++)
			/*创建并初始化一个新结点*/
		{
			Node *body = NULL;
			body = CreatNode(body);//再初始化一个链表节点
			/*直接前趋结点的next指针指向新结点*/
			list->next = body; //链表指针的前一个(指针)与新节点连
			/*新结点指向直接前趋结点*/
			body->pre = list; //新节点的后一个(指针)与上一个节点相连
			/*把body指针给list返回*/
			list = list->next;
		}

	}
	/*加上以下两句就是双向循环链表*/
	// list->next=head;
	// head->prior=list;
	return head;//返回该链表的链表头
}

//输出链表
void PrintList(Node *head) {
	Node *temp = head;
	while (temp) {
		//如果该节点无后继节点,说明此节点是链表的最后一个节点
		if (temp->next == NULL) {
			printf("%d\n", temp->data);
		} else {
			printf("%d ", temp->data);
		}
		temp = temp->next;
	}
}

//在第add位置的前面插入data节点
Node *InsertListHead(Node *head) {
	//新建数据域为data的结点
	int add, data;
	printf("请输入要插入的位置和数据(用逗号隔开):");
	scanf("%d,%d", &add, &data);
	Node *temp = (Node *)malloc(sizeof(Node));
	if (temp == NULL) {
		printf("malloc error!\r\n");
		return NULL;
	} else {
		temp->data = data;
		temp->pre = NULL;
		temp->next = NULL;
	}
	//插入到链表头,要特殊考虑
	if (add == 1) {
		temp->next = head;
		head->pre = temp;
		head = temp;
	} else {
		Node *body = head;
		//找到要插入位置的前一个结点
		for (int i = 1; i < add - 1; i++) {
			body = body->next;
		}
		//判断条件为真,说明插入位置为链表尾
		if (body->next == NULL) {
			body->next = temp;
			temp->pre = body;
		} else {
			body->next->pre = temp;
			temp->next = body->next;
			body->next = temp;
			temp->pre = body;
		}
	}
	return head;
}

//在第add位置的后面插入data节点
Node *InsertListEnd(Node *head) {
	int i = 1;
	int add, data;
	printf("请输入要插入的位置和数据(用逗号隔开):");
	scanf("%d,%d", &add, &data);
	//新建数据域为data的结点
	Node *temp = (Node *)malloc(sizeof(Node));
	temp->data = data;
	temp->pre = NULL;
	temp->next = NULL;

	Node *body = head;
	while ((body->next) && (i < add + 1)) {
		body = body->next;
		i++;
	}

	//判断条件为真,说明插入位置为链表尾
	if (body->next == NULL) {
		body->next = temp;
		temp->pre = body;
		temp->next = NULL;
	} else {
		temp->next = body->pre->next;
		temp->pre = body->pre;
		body->next->pre = temp;
		body->pre->next = temp;

	}

	return head;
}

//删除数据是data的节点
Node *DeleteList(Node *head) {
	Node *temp = head;
	int data;
	printf("请输入要删除的元素:");
	scanf("%d", &data);
	//遍历链表
	while (temp) {
		//判断当前结点中数据域和data是否相等,若相等,摘除该结点
		if (temp->data == data) {
			//判断是否是头结点
			if (temp->pre == NULL) {
				head = temp->next;
				temp->next = NULL;
				free(temp);
				return head;
			}
			//判断是否是尾节点
			else if (temp->next == NULL) {
				temp->pre->next = NULL;
				free(temp);
				return head;
			} else {
				temp->pre->next = temp->next;
				temp->next->pre = temp->pre;
				free(temp);
				return head;
			}


		}
		temp = temp->next;
	}
	printf("Can not find %d!\r\n", data);
	return head;
}

//更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
Node *ModifyList(Node *p) {
	Node *temp = p;
	int add, newElem;
	printf("结点位序和新数据值(用逗号隔开):");
	scanf("%d,%d", &add, &newElem);
	//遍历到被删除结点
	for (int i = 1; i < add; i++) {
		temp = temp->next;
	}
	temp->data = newElem;
	return p;
}

//head为原双链表,elem表示被查找元素
int FindList(Node *head) {
	//新建一个指针t,初始化为头指针 head
	Node *temp = head;
	int i = 1;
	int elem;
	printf("输入要查找的元素:");
	scanf("%d", &elem);
	while (temp) {
		if (temp->data == elem) {
			return i;
		}
		i++;
		temp = temp->next;
	}
	printf("该元素的位序是%d\n", i);
	//程序执行至此处,表示查找失败
	return -1;
}

int main() {
	Node *head = NULL;
	//创建双链表
	head = CreatList(head);
	printf("新创建双链表为\n");
	PrintList(head);
	//插入
	head = InsertListHead(head);
	printf("在表中该位置前插入元素后为:\n");
	PrintList(head);
	head = InsertListEnd(head);
	printf("在表中该位置后插入元素后为:\n");
	PrintList(head);
	//删除
	head = DeleteList(head);
	printf("表中删除元素后为: \n" );
	PrintList(head);
	printf("元素的位置是\n%d\n", FindList(head));
	//修改
	head = ModifyList(head);
//	printf("表中第i个节点中的数据改为存储a\n");
	PrintList(head);
	return 0;
}

运行结果(示例):

 

有不当的地方,还请大佬不吝赐教,拜拜 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值