数据结构----单链表的基本操作

 前言

在学习链表之前,已经学习过了顺序表,没有接触过的同学请点击下方链接:

http://t.csdn.cn/JKO8E

单链表的定义:

  • 由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相。
  •  概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表(SListl),每个节点(SListNode)都存储着一个数据(data),还有一个指向下个节点的指针(next)。

 单链表的优缺点:

  • 单链表的优点:1、元素的存储单元是任意的,可连续也可不连续。2、可以按照实际所需创建结点增减链表的长度,更大程度地使用内存 。
  • 单链表的缺点:1、进行尾部或者任意位置上插入或删除时时间复杂度和空间复杂度较大。2、存放元素时需要另外开辟一个指针域的空间。

顺序表的优缺点:

  • 优点:可以通过下标直接访问所需要的数据。
  • 缺点:不能按实际所需分配内存,只能使用malloc或者realloc函数进行扩容,容易实现频繁扩容,容易导致内存浪费与数据泄露等问题。

单链表的基本操作:


 单链表的打印、尾插、头插、尾删、头删、销毁这些较为简单的操作,还有更为重要的单链表的查找、插入、在pos后插入、删除入和删除pos后的一个建立单链表。

单链表的实现 

结构定义、接口函数的声明

常规定义下的单链表,一般包含数据域指针域

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SLTNode;

void SListPrint(SLTNode* phead);  //打印
void SListPushBack(SLTNode** phead, SLTDateType x);  //尾插
void SListPushFront(SLTNode** phead, SLTDateType x);  //头插
void SListPopBack(SLTNode** phead);  //尾删
void SListPopFront(SLTNode** phead);  //头删
SLTNode* SListFind(SLTNode* phead, SLTDateType x);  //查找
void SListInsert(SLTNode** phead,SLTNode* pos, SLTDateType x);   //插入
void SListInsertAfter(SLTNode* po, SLTDateType x);  //在pos的后面插入
void SListErease(SLTNode** phead, SLTNode* pos);  //删除
void SListEreaseAfter(SLTNode* pos);  //删除pos后面一个
void SListDestory(SLTNode** phead);  //销毁

封装结点

将结点封装成函数,等我们后面需要插入时调用此接口就可以了,提高代码的复用性

SLTNode* CreateListNode(SLTDateType x)   //封装结点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

打印输出

这个不用过多解释,直接整。

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

 尾插

通过遍历链表找到尾节点,并将新节点链接到尾节点之后,实现了新元素的添加。

void SListPushBack(SLTNode** phead, SLTDateType x)  //尾插
{
	assert(phead);
	SLTNode* newnode= CreateListNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		//找到尾节点
		SLTNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}  
		tail->next = newnode;
	}
}

尾删

需要分情况进行判断

在链表为空或只有一个节点时,直接释放相应内存空间即可;否则通过遍历找到尾节点,并释放其空间,然后将前一个节点的 next 指针指向 NULL。

void SListPopBack(SLTNode** phead)  //尾删
{
	//分情况
	assert(*phead != NULL);   //没有结点
	if ((*phead)->next == NULL)  //一个结点
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		//多个结点
		SLTNode* tail = *phead;
		while (tail->next->next)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

头插

void SListPushFront(SLTNode** phead, SLTDateType x)  //头插
{
	assert(phead);
	SLTNode* newnode = CreateListNode(x);
	newnode->next = *phead;
	*phead = newnode;
}

 头删

void SListPopFront(SLTNode** phead)  //头删
{
	assert(*phead);
	SLTNode* next = (*phead)->next;
	free(*phead);
	*phead = next;
}

查找值

SLTNode* SListFind(SLTNode* phead, SLTDateType x)  //查找
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{ 
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

在调用查找接口函数时也可以修改值,如果找到返回地址,可将此地址的值进行修改。

SLTNode* pos=SListFind(plist,3);   //查找
	int i = 1;
	while (pos)
	{
		printf("找到第%d个,地址为%p->%d\n", i++, pos, pos->data);
		pos = SListFind(pos->next, 3);
	}

	pos = SListFind(plist, 3);  //修改
	while(pos)
	{
		pos->data = 30;
		pos = SListFind(pos->next, 3);
	}
	SListPrint(plist);

在pos前插入

void SListInsert(SLTNode** phead, SLTNode* pos, SLTDateType x)  //在pos前插入
{
	assert(phead);
	assert(pos);
	SLTNode* newnode = CreateListNode(x);
	if (*phead == pos)   //第一个位置插入
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		//需要找到pos前一个位置
		SLTNode* posPrev = *phead;
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}
}

在pos后插入

//在pos的后面插入
void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);
	SLTNode* newnode = CreateListNode(x);
	newnode->next = pos->next;
	pos->next = newnode; 
}

删除

void SListErease(SLTNode** phead, SLTNode* pos)  //删除
{
	assert(phead);
	assert(pos);
	if (*phead==pos)
	{
		*phead = pos->next;
		free(pos);
		//SListPopFront(*phead);  // //也可直接传函数
	}
	else
	{
		SLTNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

删除pos后面一个

void SListEreaseAfter(SLTNode* pos)  //删除pos后面一个
{
	assert(pos);
	assert(pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

销毁链表

void SListDestory(SLTNode** phead) //销毁链表
{
	assert(phead);
	SLTNode* cur = *phead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

---------------------------------------------------------------------------------------------------------------------------------

最后希望能帮助到更多同学,共同进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
DS单链表是一种线性数据结构,它由若干个节点组成,每个节点包括数据域和指针域,其中数据域用于存储数据,指针域用于指向下一个节点。DS单链表的存储结构如下: ``` typedef struct ListNode { int val; struct ListNode *next; } ListNode; ``` 其中,val表示节点存储的数据,next表示指向下一个节点的指针。 DS单链表基本操作包括:创建、插入、删除、查找、遍历等。 1. 创建 创建DS单链表的方法有多种,例如头插法、尾插法等。其中,头插法的实现代码如下: ``` ListNode* createList(int arr[], int n) { ListNode *head = NULL; for (int i = 0; i < n; i++) { ListNode *node = (ListNode*)malloc(sizeof(ListNode)); node->val = arr[i]; node->next = head; head = node; } return head; } ``` 2. 插入 DS单链表插入操作包括在指定位置插入节点和在末尾插入节点。其中,指定位置插入节点的实现代码如下: ``` void insert(ListNode *head, int val, int pos) { ListNode *node = (ListNode*)malloc(sizeof(ListNode)); node->val = val; ListNode *p = head; for (int i = 1; i < pos && p != NULL; i++) { p = p->next; } if (p == NULL) { return; } node->next = p->next; p->next = node; } ``` 3. 删除 DS单链表的删除操作包括删除指定位置的节点和删除指定值的节点。其中,删除指定位置的节点的实现代码如下: ``` void delete(ListNode *head, int pos) { ListNode *p = head; ListNode *q = NULL; for (int i = 1; i < pos && p != NULL; i++) { q = p; p = p->next; } if (p == NULL) { return; } if (q == NULL) { head = head->next; } else { q->next = p->next; } free(p); } ``` 4. 查找 DS单链表的查找操作包括查找指定位置的节点和查找指定值的节点。其中,查找指定值的节点的实现代码如下: ``` ListNode* find(ListNode *head, int val) { ListNode *p = head; while (p != NULL) { if (p->val == val) { return p; } p = p->next; } return NULL; } ``` 5. 遍历 DS单链表的遍历操作包括正向遍历和反向遍历。其中,正向遍历的实现代码如下: ``` void traverse(ListNode *head) { ListNode *p = head; while (p != NULL) { printf("%d ", p->val); p = p->next; } } ``` 以上就是DS单链表的存储结构与操作的介绍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

¥sunrise

来自大牛的认可,是我梦寐以求的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值