单链表图文详解(C语言实现)

本文详细介绍了链表和数组在内存分配、访问速度、增删操作方面的特点,并提供了C语言实现的示例,包括创建链表、插入、删除等基本操作。
摘要由CSDN通过智能技术生成

认识什么是链表和数组,他们有什么区别?

数组:内存是连续的。

优缺点:

优点:
1.下标访问(随机访问)时间复杂度是O(1)
2.末尾位置增加删除元素时间复杂度是O(1)
3.访问元素前后相邻位置的元素非常方便
缺点:
1.非末尾位置增加删除元素需要进行大量的数据移动O(n)
2.搜索的时间复杂度:
无序数组-线性搜索O(n)
有序数组-二分搜索O(logn)
3.数组扩容消耗比较大,少了需要再扩容,多了浪费资源!

在这里插入图片描述

单链表:每一个节点都是在堆内存上独立new出来的,节点内存不连续。

优缺点:

优点:
1.内存利用率高,不需要大块连续内存
2.插入和删除节点不需要移动其它节点,时间复杂度O(1)
3.不需要专门进行扩容操作
缺点:
1.内存占用量大,每一个节点多出存放地址的空间
2.节点内存不连续,无法进行内存随机访问
3.链表搜索效率不高,只能从头节点开始逐节点遍历

在这里插入图片描述

全部源代码

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

typedef int SLTDataType;
//创建链表
typedef struct SListNode
{
	SLTDataType data;                //数据域
	struct SListNode* next;
}SLTNode;

//遍历链表,打印数据
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//清空链表
void SListClear(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	SLTNode* nextNode = NULL;
	while (cur != NULL)
	{
		nextNode = cur->next;
		free(cur);
		cur = nextNode;
	}
	*pphead = NULL;
}

//创建链表节点
SLTNode* CreateSListNode(SLTDataType x)
{
	SLTNode* NewNode = (SLTNode*)malloc(sizeof(SLTNode));       //用malloc动态开辟一个节点空间大小
	NewNode->data = x;                                          //新节点的data存放数据
	NewNode->next = NULL;                                       //新节点的next指向空指针
	return NewNode;
}

//尾部插入节点
void SListPushBack(SLTNode** pphead, SLTDataType x)         //传链表plist的地址和数据
{
	SLTNode* NewNode = CreateSListNode(x);                  //创建新节点
	//当链表为空
	if (*pphead == NULL)
	{
		*pphead = NewNode;                                 //把头指针指向新的节点
	}
	else
	{
		SLTNode* tail = *pphead;                           //先把头指针指向尾节点,通过循环把尾节点放到最后面
		while (tail->next != NULL)                         //如果尾节点的next不等于空指针就把尾节点往后移
		{
			tail = tail->next;                             //当尾节点移到最后一个位置时,跳出循环
		}
		tail->next = NewNode;                              //最后把新节点赋给尾节点
	}
}

//尾部删除节点
void SListPopBack(SLTNode** pphead)
{
	//链表为空
	if (*pphead == NULL)                                  //如果尾节点为空,什么也不返回
	{
		return;
	}
	//只有一个节点
	else if ((*pphead)->next == NULL)                     //只有一个节点的时候,删除了就会出现野指针
	{
		free(*pphead);                                    
		*pphead = NULL;                                   //需要把头指针置为空,防止出现野指针
	}
	//有多个节点
	else
	{
		SLTNode* prev = NULL;                      //指向尾节点的前一个节点
		SLTNode* tail = *pphead;                   //指向尾节点
		while (tail->next != NULL)                 //如果尾节点的next不等于空指针就把尾节点往后移
		{
			prev = tail;                           //把尾指针复制到前一个指针(头指针)
			tail = tail->next;                     //尾指针指向下一个节点
		}
		free(tail);                                //当尾指针指向NULL时
		prev->next = NULL;            //此时尾节点的前一个节点就移动到最后,要把它置为空指针,防止出现野指针
	}
}

//头部插入节点
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* NewNode = CreateSListNode(x);         //创建新的节点
	NewNode->next = *pphead;                       //把头指针指向新节点的next
	*pphead = NewNode;                             //把新节点放到头结点位置
}

//头部删除节点
void SListPopFront(SLTNode** pphead)
{
	if (*pphead == NULL)                           //空链表的时候,什么也不返回
	{
		return;
	}
	SLTNode* nextnode = (*pphead)->next;           //新节点指向头结点的next
	free(*pphead);                                 //
	*pphead = nextnode;                            //
}

//查找值为x的节点并返回节点的指针
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;          //找到返回该结点指针
		}
		cur = cur->next;
	}
	//找不到返回NULL指针
	return NULL;
}

//在pos指针前一个插入一个节点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	//pos在第一个节点,相当与头插
	if (*pphead == pos)
		SListPushFront(pphead, x);
	else
	{
		SLTNode* NewNode = CreateSListNode(x);
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = NewNode;
		NewNode->next = pos;
	}
}

//删除pos指针指向的结点
void SListErese(SLTNode** pphead, SLTNode* pos)
{
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}


//主函数,程序从这里进入
int main()
{
	SLTNode* plist = NULL;
	//尾插
	printf("\n尾部插入5个数据:15 16 17 18 19\n");
	printf("链表:");
	SListPushBack(&plist, 15);
	SListPushBack(&plist, 16);
	SListPushBack(&plist, 17);
	SListPushBack(&plist, 18);
	SListPushBack(&plist, 19);
	SListPrint(plist);                                //打印链表

	//尾删
	printf("\n尾部删除2个数据:18 19\n");
	SListPopBack(&plist);
	SListPopBack(&plist);
	printf("链表:");
	SListPrint(plist);
	//头插
	printf("\n头部插入3个数据: 77 29 64\n");
	SListPushFront(&plist, 64);
	SListPushFront(&plist, 29);
	SListPushFront(&plist, 77);
	printf("链表:");
	SListPrint(plist);
	//头删
	printf("\n头部删除1个数据:77\n");
	SListPopFront(&plist);
	printf("链表:");
	SListPrint(plist);
	return 0;
}

在这里插入图片描述

创建链表

typedef int SLTDataType;
//创建链表
typedef struct SListNode
{
	SLTDataType data;                //数据域
	struct SListNode* next;          //指针域
}SLTNode;

在这里插入图片描述

创建链表的节点

//创建链表节点
SLTNode* CreateSListNode(SLTDataType x)
{
	SLTNode* NewNode = (SLTNode*)malloc(sizeof(SLTNode));       //用malloc动态开辟一个节点空间大小
	NewNode->data = x;                                          //新节点的data存放数据
	NewNode->next = NULL;                                       //新节点的next指向空指针
	return NewNode;
}

清空链表的数据

//清空链表
void SListClear(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	SLTNode* nextNode = NULL;
	while (cur != NULL)
	{
		nextNode = cur->next;
		free(cur);                      //清空一个,就把该节点的空间释放掉,直到全部节点都被删除完
		cur = nextNode;
	}
	*pphead = NULL;                     //防止出现野指针
}

在这里插入图片描述

链表尾部插入节点

//尾部插入节点
void SListPushBack(SLTNode** pphead, SLTDataType x)         //传链表plist的地址和数据
{
	SLTNode* NewNode = CreateSListNode(x);                  //创建新节点
	//当链表为空
	if (*pphead == NULL)
	{
		*pphead = NewNode;                                 //把头指针指向新的节点
	}
	else
	{
		SLTNode* tail = *pphead;                           //先把头指针指向尾节点,通过循环把尾节点放到最后面
		while (tail->next != NULL)                         //如果尾节点的next不等于空指针就把尾节点往后移
		{
			tail = tail->next;                             //当尾节点移到最后一个位置时,跳出循环
		}
		tail->next = NewNode;                              //最后把新节点赋给尾节点
	}
}

在这里插入图片描述

链表尾部删除节点

//尾部删除节点
void SListPopBack(SLTNode** pphead)
{
	//链表为空
	if (*pphead == NULL)                                  //如果尾节点为空,什么也不返回
	{
		return;
	}
	//只有一个节点
	else if ((*pphead)->next == NULL)                     //只有一个节点的时候,删除了就会出现野指针
	{
		free(*pphead);                                    
		*pphead = NULL;                                   //需要把头指针置为空,防止出现野指针
	}
	//有多个节点
	else
	{
		SLTNode* prev = NULL;                      //指向尾节点的前一个节点
		SLTNode* tail = *pphead;                   //指向尾节点
		while (tail->next != NULL)                 //如果尾节点的next不等于空指针就把尾节点往后移
		{
			prev = tail;                           //把尾指针复制到前一个指针(头指针)
			tail = tail->next;                     //尾指针指向下一个节点
		}
		free(tail);                                //当尾指针指向NULL时
		prev->next = NULL;            //此时尾节点的前一个节点就移动到最后,要把它置为空指针,防止出现野指针
	}
}

在这里插入图片描述

链表头部插入节点

//头部插入节点
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* NewNode = CreateSListNode(x);         //创建新的节点
	NewNode->next = *pphead;                       //把头指针指向新节点的next
	*pphead = NewNode;                             //把新节点放到头结点位置
}

在这里插入图片描述

链表头部删除节点

//头部删除节点
void SListPopFront(SLTNode** pphead)
{
	if (*pphead == NULL)                           //空链表的时候,什么也不返回
	{
		return;
	}
	SLTNode* nextnode = (*pphead)->next;           //新节点指向头结点的next
	free(*pphead);                                 
	*pphead = nextnode;                            
}

在这里插入图片描述

查找链表任意位置的节点

//查找值为x的节点并返回节点的指针
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;          //找到返回该结点指针
		}
		cur = cur->next;
	}
	//找不到返回NULL指针
	return NULL;
}

在链表的任意位置插入节点

//在pos指针前一个插入一个节点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	//pos在第一个节点,相当与头插
	if (*pphead == pos)
		SListPushFront(pphead, x);
	else
	{
		SLTNode* NewNode = CreateSListNode(x);
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = NewNode;
		NewNode->next = pos;
	}
}

在这里插入图片描述

在链表的任意位置删除节点

//删除pos指针指向的结点
void SListErese(SLTNode** pphead, SLTNode* pos)
{
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

在这里插入图片描述

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值