链表实现增删查改

一 链表的特点

1 按需申请空间

由于每一块物理空间都是单独申请的,因此需要考虑如何关联每一个内存块

-》用指针关联(每个节点存放两个东西:内容和指向下一个内存块的指针)

相比于顺序表空间浪费较小:一次开辟两倍,过多浪费;频繁开辟那么频繁访问内存,效率低下。

2 效率更高

头部或者中间插入删除,不需要挪动数据(顺序表需要),相对于顺序表效率更高,因为顺序表再头部或者中间插入的话,需要挪动数据。

总结:链表与顺序表优势互补,各有特点。

二 链表的结构

对于单向不循环链表

每个节点包括所包含的内容和下一个结点的位置。最后一个结点存放所包含的内容和NULL

三基本操作

1 如何创建链表?

每个结点所包含的内容应该是所包含的数据和下一个结点的位置。定义了这样一个结构体之后,通过创建结构体类型的指针对结构体中内容进行访问。注意:对类型进行重定义可以后期存放其他类型的数据。

 代码如下:

​
typedef int slistdatatype;

typedef struct slistnode
{
	slistdatatype data;
	struct slistnode* next;
}slistnode;

​
slistnode* n1 = (slistnode*)malloc(sizeof(slistnode));
	assert(n1);

	slistnode* n2 = (slistnode*)malloc(sizeof(slistnode));
	assert(n2);

	slistnode* n3 = (slistnode*)malloc(sizeof(slistnode));
	assert(n3);

	slistnode* n4 = (slistnode*)malloc(sizeof(slistnode));
	assert(n4);

	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

2 如何打印链表?

创建一个指向第一个结构体的结构体指针,如果这个结构体不为NULL,那么就打印其中的数据。同时这个指针指向下一个结点。

void slist_print(slistnode* phead)
{
	assert(phead);

	while (phead != NULL)//如果是phead->next!=NULL作为判断条件的话,最后一个结点无法打印
	{
		printf("%d->", phead->data);
		phead = phead->next;
	}
	printf("NULL");
}//打印

3 如何在链表的头部插入一个数据

首先我们需要创建一个结点。这个节点的指针需要指向原先的第一个结构体指针。之后需要修改phead的值。因此需要传入一个二级指针pphead。

 

//头插 头插的话需要创建一个新的结点 并且在这个结点中存储下一个结点的位置(原先头结点的位置),最后plist需要重新指向最开始创建出来的结点 那么pphead也会随之修改
void slist_frontpush(slistnode** pphead, slistdatatype x)
{
	slistnode* newnode = slist_buy(x);
	newnode->next = *pphead;
	*pphead = newnode;
}//可以同时解决空链表的问题,不需要单独处理

4 如何在链表的尾部插入一个数据

首先需要创建一个新节点,作为插入的新节点。原先链表的最后一个结点的next需要指向这个结点。如何找链表的最后一个结点?需要用tail进行循环遍历查找尾巴。需要注意的是:如果是空链表的话,因此需要单独进行处理,直接将创建的新结点给*phead。

 

void slist_pushback(slistnode** pphead, slistdatatype x)
{
	slistnode* newnode= slist_buy(x);//需要一个变量接受返回的参数

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}//如何解决空结点的问题?
	else {
		slistnode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}//找尾巴
		tail->next = newnode;//找到尾结点之后把新创建结点的位置给给它
		//需要注意的是,如果第一个结点是个空结点 的话,那么无法尾插数据
	}
}//尾插

5 如何在链表的头部删除一个数据

①如果是空链表不能删除

②如果只有一个结点的话,也可以正常处理。

③如果是多个结点的话进行常规的操作。phead指向下一个结点的位置,之后将原先的结点释放掉 。           

 


void slist_popfront(slistnode** pphead)//头删
{assert(*pphead);
slistnode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}

6 如何在链表的尾部删除一个数据

①如果是空链表无法删除

②如果只有一个结点的话,会造成空指针的问题,单独考虑

③如果多个结点正常操作

需要先找到尾巴和尾巴前面的一个结点。把尾巴释放掉的时候,把前一个结点的指针设置成NULL。

void slist_popback(slistnode** pphead)
{
	assert(*pphead);//不能为空链表

	if ((*pphead)->next == NULL)//如果只有一个结点,cur会成为空指针
	{
		free(*pphead);
		*pphead = NULL;
	}//为什么不能删除最后一个结点??
	//else
	//{
	//	slistnode* cur = NULL;
	//	slistnode* tail = *pphead;
	//	while (tail->next != NULL)
	//	{
	//		cur = tail;//找到尾删结点的上一个结点指针,并且把它设置成null
	//		tail = tail->next;
	//	}//找尾巴
	//	free(tail);
	//	cur->next = NULL;
	/*}*/
	else
{
		slistnode* tail = *pphead;
		while(tail->next->next!=NULL)
		{
			
			tail = tail->next;//找倒数第二个
		 }
		free(tail->next);
		tail->next = NULL;
}

}

  需要注意的是,由于插入的时候必定要创造新的结点,因此可以将他们封装成函数,进行复用

slistnode* slist_buy(slistdatatype x)//增加一个结点 返回slistnode*类型的参数,要制定增加的内容
{
	slistnode* node = (slistnode*)malloc(sizeof(slistnode));
	if (node == NULL)
	{
		printf("fail to malloc");
	}
	node->data = x;
	node->next = NULL;
	return node;
}//创建结点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用链表实现增(插入)、删(删除)、查(查询)和改(修改)操作的示例代码。该示例还根据数据大小将元素插入有序链表中。 ```c #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node* next; } Node; Node* createNode(int data) { Node* newNode = (Node*)malloc(sizeof(Node)); if (newNode == NULL) { printf("内存分配失败\n"); exit(1); } newNode->data = data; newNode->next = NULL; return newNode; } void insertNode(Node** head, int data) { Node* newNode = createNode(data); // 如果链表为空或新节点的数据小于头节点的数据 if (*head == NULL || data < (*head)->data) { newNode->next = *head; *head = newNode; } else { Node* curr = *head; // 找到新节点应该插入的位置 while (curr->next != NULL && curr->next->data < data) { curr = curr->next; } newNode->next = curr->next; curr->next = newNode; } } void deleteNode(Node** head, int data) { if (*head == NULL) { printf("链表为空,无法删除节点\n"); return; } Node* curr = *head; Node* prev = NULL; // 找到要删除的节点 while (curr != NULL && curr->data != data) { prev = curr; curr = curr->next; } // 如果找到了要删除的节点 if (curr != NULL) { // 如果要删除的节点是头节点 if (prev == NULL) { *head = curr->next; } else { prev->next = curr->next; } free(curr); printf("成功删除节点 %d\n", data); } else { printf("未找到要删除的节点 %d\n", data); } } Node* searchNode(Node* head, int data) { Node* curr = head; // 遍历链表查找节点 while (curr != NULL && curr->data != data) { curr = curr->next; } if (curr != NULL) { printf("找到节点 %d\n", curr->data); } else { printf("未找到节点 %d\n", data); } return curr; } void updateNode(Node* head, int targetData, int newData) { Node* node = searchNode(head, targetData); if (node != NULL) { node->data = newData; printf("成功将节点 %d 更新为 %d\n", targetData, newData); } } void printList(Node* head) { Node* curr = head; printf("链表内容:"); while (curr != NULL) { printf("%d ", curr->data); curr = curr->next; } printf("\n"); } void freeList(Node* head) { Node* curr = head; Node* temp; while (curr != NULL) { temp = curr; curr = curr->next; free(temp); } } int main() { Node* head = NULL; insertNode(&head, 5); insertNode(&head, 2); insertNode(&head, 10); insertNode(&head, 7); insertNode(&head, 3); printList(head); deleteNode(&head, 10); deleteNode(&head, 4); printList(head); searchNode(head, 7); updateNode(head, 2, 8); updateNode(head, 6, 9); printList(head); freeList(head); return 0; } ``` 该示例中定义了一个链表节点结构体 `Node`,并使用 `insertNode` 函数将元素按升序插入有序链表中。`deleteNode` 函数用于删除指定的节点,`searchNode` 函数用于查找指定的节点,`updateNode` 函数用于更新指定节点的数据。最后,使用 `printList` 函数打印链表的内容,并使用 `freeList` 函数释放链表的内存。 您可以根据需要修改示例代码,并根据具体的业务逻辑进行扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值