【数据结构】单链表

个人主页点这里~

原代码在gitee里~


单链表

一、链表的概念及结构

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

与顺序表不同的是,链表里的每个存储单元叫做节点,都是独立申请下来的空间,节点由两部分组成:当前节点要保存的数据和下一个节点的指针变量

我们创建一个变量为plist指向第一个数据

链表中每个节点都是独立申请的,我们需要通过指针变量来保存下一个节点位置才能从当前节点找到下一个节点

我们用结构体来实现这个单链表

当节点数据类型为整形时:

c 复制代码
struct SListNode
{
 int data; //节点数据
 struct SListNode* next; //指针变量⽤保存下⼀个节点的地址
};

链式结构在逻辑上是连续的,在物理结构上不一定连续

节点一般是在堆上申请的

从堆上申请来的空间,可能连续可能不连续

二、单链表的实现

project.h

c 复制代码
#pragma once
#include <stdio.h>
#include <assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
 SLTDataType data; //节点数据
 struct SListNode* next; //指针保存下⼀个节点的地址
}SLTNode;

void SLTPrint(SLTNode* phead);
//头部插⼊删除/尾部插⼊删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);

project.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS

#include "project.h"
//打印
void SLTPrint(SLTNode* phead)
{
	assert(phead);//防止phead为空
	while (phead)
	{
		printf("%d ", phead->data);
		phead = phead->next;
	}
}
//创建节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* newcode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newcode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newcode->data = x;
	newcode->next = NULL;//指向下一节点的指针置为NULL
	return newcode;
}
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* new = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = new;
	}
	else
	{
		SLTNode* pur = *pphead;
		while (pur->next)
		{
			pur = pur->next;//遍历数组,当pur在尾部时停止
		}
		pur->next = new;//插入
	}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* new = SLTBuyNode(x);
	new->next = *pphead;//将新创建的节点指针指向原链表的头
	*pphead = new;//新节点为新头
}
//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pur = *pphead;
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* prev = *pphead;
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		ptail = NULL;
		free(ptail);
		//双指针,当ptail遍历数组为尾时,prev在它前边的这个节点
	}
}
//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* node = (*pphead)->next;//定义node存放下一个节点
	(*pphead)->next = NULL;
	free(*pphead);
	*pphead = node;//头指针重新赋值,此时node为头
}
//查找某个量是否在链表中,若是,返回地址,若不是,返回NULL
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pur = phead;
	while (pur)
	{
		if (pur->data == x)
		{
			return pur;
		}
		pur = pur->next;//遍历链表找出来即可
	}
	return NULL;
}
//在pos之后插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* pur = *pphead;//给一个pur记录头节点位置
	SLTNode* new = SLTBuyNode(x);//创建一个新的节点
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);//若只有一个节点,则直接头插
	}
	else
	{
		while (pur->next != pos)
		{
			pur = pur->next;
		}
		new->next = pos;//不止一个节点就遍历链表找到pos,修改后边节点和新节点指针
		pur->next = new;
	}
}
//删除pos
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(*pphead);
	if (*pphead == pos)
	{
		SLTNode* prev = *pphead;
		*pphead = (*pphead)->next;
		free(prev);//若只有一个节点,直接删除
	}
	else
	{
		SLTNode* pur = *pphead;
		while (pur->next != pos)
		{
			pur = pur->next;
		}
		SLTNode* pucr = pos->next;
		pur->next = pucr;
		pos->next = NULL;
		free(pos);//不止一个节点,找到pos节点,可以直接使用SLTFind函数,改变前后指针然后删除
	}
}
//在指定位置后插入节点
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* new = SLTBuyNode(x);
	SLTNode* pur = pos->next;
	pos->next = new;
	new->next = pur;//这个比较简单,之间改变这个位置后与新节点的指针即可
}
//删除pos节点之后那个节点
void SLTEraseAfter(SLTNode* pos)
{
	SLTNode* pur = pos->next;//记录pos后的节点
	SLTNode* pucr = pur->next;//记录pos之后的之后的节点
	pos->next = pucr;
	pur->next = NULL;//将pos和pucr连接
	free(pur);
}
//销毁链表
void SListDesTroy(SLTNode** pphead)
{
	SLTNode* pur = *pphead;
	while ((*pphead)->next != NULL)
	{
		pur = *pphead;
		*pphead = (*pphead)->next;
		pur->next = NULL;
		free(pur);
	}//遍历链表将堆上内存回收,将指针置为空
	free(*pphead);
}

test.c

c 复制代码
#include "project.h"

void test1()//
{
		//链表是由一个一个的节点组成
		//创建几个节点
		SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
		node1->data = 1;

		SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
		node2->data = 2;

		SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
		node3->data = 3;

		SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
		node4->data = 4;

		//将四个节点连接起来
		node1->next = node2;
		node2->next = node3;
		node3->next = node4;
		node4->next = NULL;

		//调用链表的打印
		SLTNode* plist = node1;
		SLTPrint(plist);
}
void test2()
{
	//尾插
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	//SLTPrint(plist);
	//SLTPushBack(NULL, 5);
	//
	//测试头插
	//SLTPushFront(&plist, 6);
	//SLTPrint(plist);
	//SLTPushFront(&plist, 7);
	//SLTPrint(plist);
	//SLTPushFront(&plist, 8);
	//SLTPrint(plist);

	//测试尾删
	/*SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);*/

	//指定位置后插
	/*SLTInsertAfter(plist->next,9);
	SLTPrint(plist);*/

	//指定位置前插
	//寻找指定位置的数据
	/*SLTNode* find = SLTFind(plist, 1);
	SLTInsert(&plist, find, 11);
	SLTPrint(plist);*/

	//指定位置后插
	/*SLTNode* find = SLTFind(plist, 1);
	SLTInsertAfter(find, 11);
	SLTPrint(plist);*/

	//指定位置删除
	/*SLTNode* find = SLTFind(plist, 1);
	SLTErase(&plist, find);
	SLTPrint(plist);*/

	//删除pos之后的节点
	/*SLTNode* find = SLTFind(plist, 1);
	SLTEraseAfter(find);
	SLTPrint(plist);*/

	//销毁链表
	/*SListDesTroy(&plist);*/
} 
int main()
{
	//test1();
	test2();
	return 0;
}

今天的分享就到这里了~

相关推荐
vvw&27 分钟前
使用同一个链接,如何实现PC打开是web应用,手机打开是一个H5应用
开发语言·前端·javascript·智能手机·面试题·每日一道前端面试题
平头哥在等你33 分钟前
C语言简答题答案
java·c语言·jvm
疯狂吧小飞牛1 小时前
无锁编程–C语言
c语言·数据结构·c++·算法
梦深时有鹿1 小时前
C#基础上机练习题
数据结构·算法·c#
cccccc语言我来了1 小时前
详解 【AVL树】
数据结构·c++
黑客Ela1 小时前
网络安全加解密原理
开发语言·php
MATLAB代码顾问1 小时前
MATLAB实现多种群遗传算法(multiple population GA,MPGA)
开发语言·matlab
wqyc++1 小时前
C++ 中的 Lambda 表达式
开发语言·c++
2303_Alpha1 小时前
数据结构——哈夫曼编码
c语言·数据结构·笔记·算法·图论
成为不掉头发的工程师1 小时前
conda下载与pip下载的区别
开发语言·python