【数据结构】单链表

 为什么要学习链表?

前面我们所学习的顺序表是存在缺陷的

顺序表的缺陷:

1.空间不够用就需要扩容,扩容可能会是异地扩容,并且扩容是会要代价的,可能会造成空间的浪费

2.头部或者中间部分的删除,需要大量的移动数据,效率低下

优化方案:

1.按需申请空间

2.尽可能少的移动数据,或者是不移动数据

1.链表

1.1链表的概念及结构

概念:链表是一种物理存储结构上非连续非顺序存储的结构,数据元素的逻辑顺序是通过链表中的指针将节点链接起来

链式结构在逻辑上是连续的,但是在物理上不一定连续,一般情况下,链表结构中的节点都是从堆上申请出来的,从堆上申请的空间可能连续,也可能不连续

 从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续

现实中的节点都是从堆上申请出来的

从对上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

1.2链表的分类

实际中链表的结构有很多,以下情况组合起来就有八种链表结构

1.单向或者双向

2.带头或者不带头

3.循环或者非循环

虽然有这么多的链表的结构,但是我们实际中最常用的还是以下两种结构

上面两种结构各自的好处:

1.无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接矩阵。另外这种结构在笔试和面试中出现很多

2.带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然复杂,但是在有些题目中会发现这个结构的优势

带哨兵位的链表(带头结点的链表)尾插更加方便一些,oj题基本上都是不带哨兵位的链表,所以oj题中使用这种结构,如果想要返回链表,则需要返回头结点的下一个节点,带哨兵位的链表虽然结构复杂,但是在尾插的时候却很方便,比如单链表中的很多操作需要判断是否为NULL很麻烦,使用这种结构不用判断是否为空

1.3链表的实现

SeqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SLTNode
{
	SLTDataType data;
	struct SLTNode* next;
}SLTNode;

//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x);
//打印
void SLTPrint(SLTNode** pphead);
//头插
void SLTPushFront(SLTNode** pphead,SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);
//销毁
void SLTDestroy(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在pos位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos,SLTDataType x);
//在pos位置之后插入
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos位置后面的值
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos);

SeqList.c

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"

//尾插
SLTNode* BuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	SLTNode* newnode = BuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		while (cur->next)
		{
			cur = cur->next;
		}
		cur->next = newnode;
	}
}
//打印
void SLTPrint(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);

	newnode->next = *pphead;
	(*pphead) = newnode;
}
//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* prev = NULL;
	SLTNode* cur = *pphead;
	while (cur->next)
	{
		prev = cur;
		cur = cur->next;
	}
	prev->next = NULL;
	free(cur);
	cur = NULL;
}
//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* del = *pphead;
	(*pphead) = (*pphead)->next;
	free(del);
	del = NULL;
}
//销毁
void SLTDestroy(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLTNode* del = *pphead;
	while (*pphead)
	{
		del = *pphead;
		*pphead = (*pphead)->next;
		free(del);
	}
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
//在pos位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	SLTNode* newnode = BuyNode(x);
	SLTNode* prev = NULL;
	SLTNode* cur = *pphead;
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		newnode->next = pos;
		cur->next = newnode;
	}
}
//在pos位置之后插入
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除pos位置的值
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(pphead);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* cur = *pphead;
		SLTNode* del = pos;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = pos->next;
		free(del);
	}

}
//删除pos位置后面的值
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	assert(pphead);
	SLTNode* cur = pos->next;
	pos->next = cur->next;

}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void Test1()
{
	SLTNode* phead = NULL;
	SLTPushBack(&phead, 1);
	SLTPushBack(&phead, 2);
	SLTPushBack(&phead, 3);
	SLTPushBack(&phead, 4);
	SLTPrint(&phead);
	SLTNode* pos = SLTFind(phead, 1);
	/*SLTErase(&phead, pos);
	SLTPrint(&phead);*/
	SLTEraseAfter(&phead, pos);
	SLTPrint(&phead);


	SLTDestroy(&phead);
}
int main()
{
	Test1();
	//SLTNode* phead = NULL;
	//SLTPushBack(&phead, 1);
	//SLTPushBack(&phead, 2);
	//SLTPushBack(&phead, 3);
	SLTPrint(&phead);
	//SLTPushFront(&phead, 4);
	//SLTPushFront(&phead, 5);
	//SLTPushFront(&phead, 6);
	//SLTPrint(&phead);
	//printf("----βɾ------\n");
	//SLTPopBack(&phead);
	//SLTPrint(&phead);
	//SLTPopBack(&phead);
	//SLTPrint(&phead);
	//printf("----ͷɾ------\n");
	//SLTPopFront(&phead);
	//SLTPrint(&phead);

	//SLTPopFront(&phead);
	//SLTPrint(&phead);

	//printf("-----------\n");
	//SLTNode* pos=SLTFind(phead, 4);
	///*printf("%d\n", node->data);*/
	//SLTInsert(&phead, pos, 10);
	//SLTPrint(&phead);

	//SLTInsertAfter(&phead, pos, 20);
	//SLTPrint(&phead);
	//SLTErase(phead, pos);
	//SLTPrint(&phead);

	//SLTDestroy(&phead);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值