3.2单链表增删查改代码实现

单链表的增删查改代码实现(可运行)

文章目录

  • 单链表的增删查改代码实现(可运行)
    • @[toc]
    • 1.单链表的增删查改功能的函数定义
    • 2.单链表的增删查改功能的代码内容
    • 3.单链表的增删查改功能的理解深度剖析
    • 备注:推荐大家买一本《大话数据结构溢彩加强版》用来学习数据结构

1.单链表的增删查改功能的函数定义

单链表的增删查改功能的函数定义包括:
1.动态申请结点
2.打印单链表
3.单链表头插
4.单链表头删
5.单链表尾插
6.单链表尾删
7.单链表查找
8.单链表pos之前插入
9.单链表pos之后插入
10.单链表pos位置删除
11.单链表销毁

文件定义:
1.头文件:SList.h
2.源文件1:SList.c
3.源文件2:源.c


2.单链表的增删查改功能的代码内容

    //SList.h
    #pragma once
    #include
    #include
    #include
    #include
    
    typedef int SListDataType;
    
    typedef struct SListNode
    {
    	int data;//val  
    	struct SListNode* next;//存储下一个结点的地址
    }SListNode;
    
    struct SListNode* BuyListNode(SListDataType x);//动态申请一个结点
    void SListPrint(struct SListNode* phead);//单链表打印
    void SListPushBack(struct SListNode** pphead, SListDataType x);//单链表尾插
    void SListPushFront(struct SListNode** pphead, SListDataType x);//单链表头插
    void SListPopBack(struct SListNode** pphead);//单链表尾删
    void SListPopFront(struct SListNode** pphead);//单链表头删
    struct SListNode* SListFind(struct SListNode* phead, SListDataType x);//单链表查找
    void SListInsert(struct SListNode** pphead,struct SlistNode* pos, SListDataType x);//单链表pos之前插入
    void SListErase(struct SListNode** pphead,struct SListNode* pos);//单链表删除pos位置
    void SListInsertAfter(struct SListNode* pos, SListDataType x);//单链表pos之后插入
    void SListDestory(struct SListNode** pphead);//单链表销毁
    //SList.c
    #define _CRT_SECURE_NO_WARNINGS 1
    
    #include"SList.h"
    
    void SListPrint(struct SListNode* phead)//打印链表   这里不需要改变值  所以一级指针*phead就可以 不需要二级指针
    {
    	struct SListNode* cur = phead;
    	while (cur != NULL)
    	{
    		printf("%d->", cur->data);
    		cur = cur->next;
    	}
    	printf("NULL\n");
    };
    
    struct SListNode* BuyListNode(SListDataType x)//新结点创建
    {
    	struct SListNode* newnode = (struct SListNode*)malloc(sizeof(struct SListNode));
    	if (newnode == NULL)
    	{
    		printf("malloc失败!\n");
    		exit(-1);
    	}
    	else
    	{
    		newnode->data = x;
    		newnode->next = NULL;
    	}
    	return newnode;
    };
    
    void SListPushBack(struct SListNode** pphead, SListDataType x)//尾插
    {
    	//要改变*phead 就要用**pphead  传数据进去要用&*phead
    	struct SListNode* newnode = BuyListNode(x);
    	if (*pphead == NULL)
    	{
    		pphead = newnode;
    	}
    	else
    	{
    		//找尾
    		struct SListNode* tail = *pphead;//phead的地址赋给tail指针
    		while (tail->next != NULL)
    		{
    			tail = tail->next;
    		}
    		tail->next = newnode;//新插入的newnode   newnode->next=NULL;
    	}
    };
    
    void SListPushFront(struct SListNode** pphead, SListDataType x)//头插
    {
    	struct SListNode* newnode = BuyListNode(x);
    	newnode->next = *pphead;//*pphead就是*phead 头指针地址
    	*pphead = newnode;//头指针指向新结点
    }
    
    void SListPopBack(struct SListNode** pphead)//尾删
    {
    	//尾删最后一个next必须为NULL   直接tail->next=NULL 不行 需要多一个结构体让tail指向其next指向NULL
    	assert(pphead);
    	//可能情况
    	//1.空
    	//2.一个结点
    	//3.多个结点
    	if (*pphead == NULL)//暴力方式:assert(*pphead!=NULL);
    	{
    		return;
    	}
    	else if ((*pphead)->next == NULL)
    	{
    		free(*pphead);
    		*pphead = NULL;
    	}
    	else {
    		struct SListNode* tail = *pphead;
    		while (tail->next->next != NULL)
    		{
    			tail = tail->next;
    		}
    		free(tail->next);
    		tail->next = NULL;//tail指向prev
    		
    	}
    };
    
    void SListPopFront(struct SListNode** pphead)//头删
    {
    	assert(pphead);
    	//1.空
    	//2.非空
    	if (*pphead == NULL)
    	{
    		return;
    	}
    	else
    	{
    		struct SListNode* next = (*pphead)->next;
    		free(*pphead);
    		*pphead = next;
    	}
    };
    
    struct SListNode* SListFind(struct SListNode* phead, SListDataType x)//单链表查找
    {
    	struct SListNode* cur = phead;
    	while (cur != NULL)
    	{
    		if (cur->data == x)
    		{
    			return cur;
    		}
    		else
    		{
    			cur = cur->next;
    		}
    		
    	}
    	return NULL;//没找到就返回NULL
    	
    };
    
    void SListInsert(struct SListNode** pphead, struct SlistNode* pos, SListDataType x)//在pos位置之前插入
    {
    	assert(pphead);
    	assert(pos);
    	//1.pos是第一个结点
    	//2.pos不是第一个结点
    	if (pos == *pphead)
    	{
    		SListPushFront(pphead, x);//头插一个结点
    	}
    	else
    	{
    		struct SListNode* prev = *pphead;
    		while (prev->next != pos)
    		{
    			prev = prev->next;
    		}
    		struct SListNode* newnode = BuyListNode(x);
    		prev->next = newnode;
    		newnode->next = pos;
    	}
    };
    
    void SListInsertAfter(struct SListNode* pos, SListDataType x)//在pos位置之后插入
    {
    	assert(pos);
    	struct SListNode* next = pos->next;
    	struct SListNode* newnode = BuyListNode(x);
    	pos->next = newnode;
    	newnode->next = next;
    };
    
    void SListErase(struct SListNode** pphead, struct SListNode* pos)//单链表删除pos位置
    {
    	assert(pphead);
    	assert(pos);
    	if (*pphead == pos)
    	{
    		SListPopFront(pphead);
    	}
    	else
    	{
    		struct SListNode* prev = *pphead;
    		while (prev->next != pos)
    		{
    			prev = prev->next;
    		}
    		prev->next = pos->next;
    		free(pos);
    		pos = NULL;
    	}
    };
    
    void SListDestory(struct SListNode** pphead)//单链表销毁
    {
    	assert(pphead);
    	struct SListNode* cur = *pphead;
    	while (cur != NULL)
    	{
    		struct SListNode* next = cur->next;
    		free(cur);
    		cur = next;
    	}
    	*pphead = NULL;
    };

    //源.c
    #define _CRT_SECURE_NO_WARNINGS 1
    //链表
    #include
    #include
    #include
    #include"Slist.h"
    
    int main()
    {	
    	SListNode* slist=NULL;//空链表
    	printf("1:测试头插功能!\n");
    	printf("头插入4,3,2,1\n");
    	SListPushFront(&slist, 4);
    	SListPushFront(&slist, 3);
    	SListPushFront(&slist, 2);
    	SListPushFront(&slist, 1);
    	SListPrint(slist);
    	printf("\n");
    	
    	printf("2:测试头删功能!\n");
    	printf("头删1\n");
    	SListPopFront(&slist, 1);
    	SListPrint(slist);
    	printf("\n");
    
    	printf("3:测试尾插功能!\n");
    	printf("尾插7\n");
    	SListPushBack(&slist, 7);
    	SListPrint(slist);
    	printf("\n");
    
    	printf("4:测试尾删功能!\n");
    	printf("尾删7\n");
    	SListPopBack(&slist);
    	SListPrint(slist);
    	printf("\n");
    
    	printf("5:测试查找功能!\n");
    	printf("查找3\n");
    	if (SListFind(slist, 3) == NULL)
    		printf("没有找到数据3\n");
    	else
    		printf("找到了数据3\n");
    	printf("\n");
    
    	printf("6:测试随机位置前插入功能!\n");
    	printf("在第3个位置前插入5\n");
    	SListInsert(&slist, slist->next->next, 5);
    	SListPrint(slist);
    	printf("\n");
    
    	printf("7:测试随机位置后插入功能!\n");
    	printf("在第3个位置后插入8\n");
    	SListInsertAfter(slist->next->next, 8);
    	SListPrint(slist);
    	printf("\n");
    
    	printf("8:测试pos位置删除数据!\n");
    	printf("删除第3个位置的数据\n");
    	printf("原链表:");
    	SListPrint(slist);
    	printf("删除后:");
    	SListErase(&slist, slist->next->next);
    	SListPrint(slist);
    	printf("\n");
    
    	printf("9:测试销毁链表!\n");
    	printf("销毁前链表:\n");
    	SListPrint(slist);
    	printf("销毁后链表:\n");
    	SListDestory(&slist);
    	SListPrint(slist);
    	printf("\n");
    
    	return 0;
    }


效果展示:

3.2单链表增删查改代码实现_第1张图片

3.单链表的增删查改功能的理解深度剖析

理解指针插入删除细节:我们举例下面两个代码来描述
单链表的插入:

代码一(插入正确操作):
S->next=P->next
P->next=S


代码二(插入错误操作):
P->next=S
S->next=p->next

那么为什么第二个代码错误,第一个代码正确呢?
下面我们图解两个代码给你阐述原因:
代码一:
3.2单链表增删查改代码实现_第2张图片
阐述:首先我们假定P结点的地址为0X1000,P的下一个结点P->next地址是0X1004,S结点的地址是0x1100。原来P的后继指针地址存的是P->next的地址也就是0X1004,S的后继指针地址为NULL,执行操作①后S的后继指针地址变为了&P->next。执行代码②后,P的后继指针地址指向S结点,也就是&S,那么P就不再指向p->next了,而p->next地址又没变,所以实现了在p和p->next之间插入了S结点。最终变成了我们图上的最终结果图!

代码二:
3.2单链表增删查改代码实现_第3张图片
阐述:前面和上面一样的,但是操作①将P的后继指针地址变成了S的地址,然后执行操作②的时候,它让S的后继指针地址变成了P的后继节点地址,也就是S->next=S了,S的后继指针地址变成了自己,就成为了自己循环了,因此错误。这是没有深入理解指针的朋友们经常犯的错误之一!

提醒:
1.我们之所以难以理解是因为我们把p->next看成了一定是p的后一个结点,其实p->next只是一个名称,指针我们看的是地址,p->next只是因为内存是连在一起的才这样写的,但链表存储数据并不是连续存储的。
2.结点分为数据域和指针域,数据域存储数据比如P->data=1,p->next是指针域,指向下一个结点,因此后继指针地址是指向下一个结点而不是什么指针的数据域。
3.研究指针问题真的建议大家画图,不然有些问题真的不好理解。

//总结
单链表:
1.适合头插头删
2.尾部或者中间某个位置插入删除不适合
3.如果要使用链表单独存储数据,那我们后面学的双链表更适合
    
顺序表
优点:连续物理空间方便下标随机访问 
缺点:①插入数据,空间不足时要扩容,扩容有性能消耗
	  ②头部或者中间位置插入数据,需要挪动数据,效率较低
	  ③可能存在一定空间占用,浪费空间

备注:推荐大家买一本《大话数据结构溢彩加强版》用来学习数据结构

推荐此书的原因:
1.此书很好的用彩色画图的方式为大家展示了基本的增删查改问题
2.此书代码简洁,逻辑清晰
3.此书的虽然还是达不到企业水平,但是基本入门了,适合用来深入学习数据结构
4.学校里学习的数据结构非常的死板,而且非常不实用,比如从来没考虑插入、删除那些位置导致的各种问题和判断他们

你可能感兴趣的:(图解数据结构,算法,数据结构,链表)