数据结构 ->链表(C语言实现)


链表的概念

1.官方的概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

2.我的总结:
链表就是一个可变的数据存储表,可以在他的基础上增删查改。不用像顺序表那样频繁增容,也不会浪费空间,操作更加明确。

如图就是一个简易的链表:
在这里插入图片描述

链表的实现

总体思路

首先就是要知道我们实现的链表是单向不带头非循环的链表,我们要建立工程文件,将定义和实现分离。也要知道链可以实现增删查改。首先得定义链表的每个节点:

//我们认为链表的数据类型是int型
typedef int SLTDatatype;

typedef struct SListNode
{
	struct SListNode* next;//指向下个节点的地址。
	SLTDatatype data;//存储的数据
}STNode;

头文件的主要接口有:

void SListPrint(SLTNode* phead); //链表的打印

void SListPushBack(SLTNode** phead,SLTDatatype x);//尾插

void SListPopBack(SLTNode** phead);//尾删

void SListPushFront(SLTNode** phead, SLTDatatype x);//头插

void SListPopFront(SLTNode** phead);//头删

SLTNode* CreateNewnode(SLTDatatype x);//建立一个新节点

SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x);// 单链表查找,同时具有修改的功能

void SListInsertAfter(SLTNode* pos, SLTDatatype x);// 单链表在pos位置之后插入x

void SListEraseAfter(SLTNode* pos);// 单链表删除pos位置之后的值

void SListDestory(SLTNode** phead);// 单链表的销毁

void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x);//在pos之前插入

为什么要传二级指针呢?因为我们设计的链表是不带头的,如果我们传过来的是一级指针,那我们相当于是传的链表的拷贝,是不起作用的,如果有兴趣,可以看看函数栈帧的调用是怎么样的。这里附上链接:

函数栈帧详解

尾插

思路:因为链表不像是顺序表那样可以进行下标访问,所以我们需要找到尾,从而进行尾插,所以时间复杂度为O(N)。

注意 :
(1) 在尾插之前,我们需要建立一个新节点,我们可以知道,不管是在尾插头插还是在任意位置插入,都需要建立新节点,为了防止代码的冗余,我们可以封装一个函数叫BuySListNode。
(2) 尾插的时候如果链表为空,则直接将新建立的节点给给链表即可。

新节点的建立

代码

SLTNode* CreateNewnode(SLTDatatype x)//建立一个新节点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("molloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

代码

void SListPushBack(SLTNode** phead, SLTDatatype x)//尾插
{
	//首先建立一个新节点
	assert(phead);
	SLTNode* newnode = CreateNewnode(x);

	//1.phead是否为空,若为空
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		//2.若不为空
		//(1).找到尾节点
		SLTNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		//(2).新节点
		tail->next = newnode;
	}
}

尾删

思路:尾删首先得判断链表是否为空,为空则断言暴力解决。第二就是判断链表是否只有一个节点,如果是只有一个节点则需要将链表置为空。如果链表的节点个数大于一,我们需要找到尾节点的前一个,在free最后一个节点以后,要将倒数第二个节点的next指向NULL。

代码

void SListPopBack(SLTNode** phead)//尾删
{
	//判断链表是否为空
	assert(*phead != NULL);

	//特殊情况特别对待
	SLTNode* tail = *phead;
	SLTNode* next = (*phead)->next;
	if (next == NULL)
	{
		free(tail);
		*phead = NULL;
	}
	else
	{
		//删除最后一个节点必须要找到倒数第二个节点
		//free最后一个节点之后再把倒数第二个节点next置为空
		while (next->next != NULL)
		{
			tail = tail->next;
			next = next->next;
		}
		free(next);
		tail->next = NULL;
	}
}

头插

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

头删

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

查找

思路:查找就是遍历链表,找到就返回当前节点的地址。

SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x)// 单链表查找
{
	assert(phead);
	SLTNode* tmp = phead;
	while (tmp != NULL)
	{
		if (tmp->data == x)
		{
			return tmp;
		}
		tmp = tmp->next;
	}
	return NULL;
}

其余接口

void SListInsertAfter(SLTNode* pos, SLTDatatype x)// 单链表在pos位置之后插入x
{
	assert(pos);
	SLTNode* newnode = CreateNewnode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListEraseAfter(SLTNode* pos)// 单链表删除pos位置之后的值
{
	assert(pos && pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}


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


void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x)//在pos之前插入
{
	assert(phead);
	assert(pos);
	SLTNode* newnode = CreateNewnode(x);
	if (pos == *phead)
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		SLTNode* prev = *phead;
		SLTNode* tail = (*phead)->next;
		while (tail != NULL && tail != pos)
		{
			prev = prev->next;
			tail = tail->next;
		}
		if (tail == NULL)
		{
			printf("Do not have pos\n");
			return;
		}
		else
		{
			prev->next = newnode;
			newnode->next = tail;
		}
	}
}


所有代码

SList.h
#pragma once 

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

typedef int SLTDatatype;

typedef struct SListNode
{
	SLTDatatype data;
	struct SListNode* next;
}SLTNode;



void SListPrint(SLTNode* phead); //链表的打印

void SListPushBack(SLTNode** phead,SLTDatatype x);//尾插

void SListPopBack(SLTNode** phead);//尾删

void SListPushFront(SLTNode** phead, SLTDatatype x);//头插

void SListPopFront(SLTNode** phead);//头删

SLTNode* CreateNewnode(SLTDatatype x);//建立一个新节点

SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x);// 单链表查找,同时具有修改的功能

void SListInsertAfter(SLTNode* pos, SLTDatatype x);// 单链表在pos位置之后插入x

void SListEraseAfter(SLTNode* pos);// 单链表删除pos位置之后的值

void SListDestory(SLTNode** phead);// 单链表的销毁

void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x);//在pos之前插入

SList.c
#define _CRT_SECURE_NO_WARNINGS 1

#include"SList.h"
 
SLTNode* CreateNewnode(SLTDatatype x)//建立一个新节点
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("molloc 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, SLTDatatype x)//尾插
{
	//首先建立一个新节点
	assert(phead);
	SLTNode* newnode = CreateNewnode(x);

	//1.phead是否为空,若为空
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		//2.若不为空
		//(1).找到尾节点
		SLTNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		//(2).新节点
		tail->next = newnode;
	}
}


void SListPopBack(SLTNode** phead)//尾删
{
	//判断链表是否为空
	assert(*phead != NULL);

	//特殊情况特别对待
	SLTNode* tail = *phead;
	SLTNode* next = (*phead)->next;
	if (next == NULL)
	{
		free(tail);
		*phead = NULL;
	}
	else
	{
		//删除最后一个节点必须要找到倒数第二个节点
		//free最后一个节点之后再把倒数第二个节点next置为空
		while (next->next != NULL)
		{
			tail = tail->next;
			next = next->next;
		}
		free(next);
		tail->next = NULL;
	}
}


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


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


SLTNode* SListFindNode(SLTNode* phead, SLTDatatype x)// 单链表查找
{
	assert(phead);
	SLTNode* tmp = phead;
	while (tmp != NULL)
	{
		if (tmp->data == x)
		{
			return tmp;
		}
		tmp = tmp->next;
	}
	return NULL;
}


void SListInsertAfter(SLTNode* pos, SLTDatatype x)// 单链表在pos位置之后插入x
{
	assert(pos);
	SLTNode* newnode = CreateNewnode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListEraseAfter(SLTNode* pos)// 单链表删除pos位置之后的值
{
	assert(pos && pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}


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


void SListInsert(SLTNode** phead, SLTNode* pos, SLTDatatype x)//在pos之前插入
{
	assert(phead);
	assert(pos);
	SLTNode* newnode = CreateNewnode(x);
	if (pos == *phead)
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		SLTNode* prev = *phead;
		SLTNode* tail = (*phead)->next;
		while (tail != NULL && tail != pos)
		{
			prev = prev->next;
			tail = tail->next;
		}
		if (tail == NULL)
		{
			printf("Do not have pos\n");
			return;
		}
		else
		{
			prev->next = newnode;
			newnode->next = tail;
		}
	}
}


谢谢大家!!!

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值