【数据结构】单链表

个人主页点这里~

原代码在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;
}

今天的分享就到这里了~

相关推荐
DARLING Zero two♡20 分钟前
关于我、重生到500年前凭借C语言改变世界科技vlog.16——万字详解指针概念及技巧
c语言·开发语言·科技
Gu Gu Study22 分钟前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
芊寻(嵌入式)44 分钟前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
泉崎1 小时前
11.7比赛总结
数据结构·算法
有梦想的咸鱼_1 小时前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法
你好helloworld1 小时前
滑动窗口最大值
数据结构·算法·leetcode
海阔天空_20131 小时前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
天下皆白_唯我独黑1 小时前
php 使用qrcode制作二维码图片
开发语言·php
QAQ小菜鸟1 小时前
一、初识C语言(1)
c语言